Skip to content

fix(copilot): keep sidebar mode beside host content instead of overlapping it - #3029

Open
RoyBA wants to merge 10 commits into
Chainlit:mainfrom
RoyBA:fix/copilot-sidebar-overlap
Open

fix(copilot): keep sidebar mode beside host content instead of overlapping it#3029
RoyBA wants to merge 10 commits into
Chainlit:mainfrom
RoyBA:fix/copilot-sidebar-overlap

Conversation

@RoyBA

@RoyBA RoyBA commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

In the Copilot widget's sidebar display mode, the fixed panel could overlap host content or leave a white gap on some host pages. This PR fixes that: the sidebar now stays pinned as a true viewport-height panel beside the host content, and it also handles viewport-filling host apps (maps/dashboards) through an opt-in config.

recording

Root cause

The widget reserves space with body { margin-right }, which only works while the fixed sidebar is anchored to the viewport and the host content flows inside the body's content box. Two host situations break that:

  • A containing block on <body> — if the host sets transform (commonly translateZ(0)), perspective, or will-change on <body>, that element becomes the containing block for the fixed sidebar, which re-anchors to the margin-shrunk body: the sidebar gets pushed (leaving a white gap) and, on tall pages, stretches to the full document height and scrolls with the page.
  • Viewport-filling shells — apps sized to the viewport (100vw / position: absolute; inset: 0, e.g. a full-screen map) don't live in the body's content box, so a body margin can't shrink them and the sidebar overlaps.

How to reproduce

The bug only appears when the host page makes <body> a containing block for fixed elements (via a transform / perspective / will-change — commonly a translateZ(0) GPU hint) or when the host fills the viewport. A plain host page won't show it.

1. Start a Chainlit server (any app), e.g. chainlit run app.py on http://localhost:8000.

2. Serve this host page from a static server (not file://) and open it:

<!doctype html>
<html>
  <head><meta charset="utf-8" /></head>
  <!-- transform on <body> is the trigger (a common GPU hint) -->
  <body style="min-height: 100vh; transform: translateZ(0);">
    <div style="height: 250vh; padding: 1rem; background: linear-gradient(#111, #333); color: #fff;">
      Host content — scroll down
    </div>
    <script src="http://localhost:8000/copilot/index.js"></script>
    <script>
      localStorage.setItem("chainlit-copilot-displayMode", "sidebar");
      window.mountChainlitWidget({ chainlitServer: "http://localhost:8000", displayMode: "sidebar", opened: true });
    </script>
  </body>
</html>

3. Observe the sidebar.

  • Before this PR: the sidebar leaves a white gap on the right, stretches to the full document height, and scrolls away with the page.
  • After this PR: the sidebar is pinned to the right edge, is viewport-height, stays fixed while scrolling, and the host content shrinks beside it.

Viewport-filling variant (hostRoot)

Give the host a full-screen shell instead of normal-flow content:

<div id="root" style="position: fixed; inset: 0"></div>
  • Without hostRoot, the sidebar overlaps it — a body margin can't shrink a 100vw / absolute-inset layout.
  • Add hostRoot: "#root" to mountChainlitWidget, and the app reflows into the reduced width beside the sidebar.

Fix

While the sidebar is open, on <body> we suspend the containing-block properties (transform, perspective, will-change) so the fixed sidebar stays anchored to the viewport, and reserve space with margin-right.

For viewport-filling apps, a new opt-in hostRoot config lets integrators name their app root; when set, that element's width is constrained to calc(100vw - sidebarWidth) instead of the body margin. Everything is saved and restored on close / mode-switch / unmount.

window.mountChainlitWidget({
  chainlitServer: "...",
  displayMode: "sidebar",
  hostRoot: "#root" // optional: for 100vw / absolute-inset app shells
});

There is no DOM reparenting and no style copying, so the host's own layout and native scrollbar are preserved.


Summary by cubic

Fixes the Copilot widget sidebar so it stays beside host content instead of overlapping it or leaving a white gap.

Bug Fixes

  • Suspends transform, perspective, and will-change on <body> while the sidebar is open, keeping the fixed panel viewport-anchored.
  • Restores all modified styles on close, mode switch, or unmount, even if a hostRoot node is swapped or removed while open, and undoes the body margin only when the fallback was used.

New Features

  • Adds optional hostRoot config for viewport-filling shells (e.g. maps) that ignore body margins.
  • When set, constrains that element's width to calc(100vw - sidebarWidth) instead of the body margin.

Written for commit 4537875. Summary will update on new commits.

Review in cubic

RoyBA and others added 2 commits August 27, 2026 02:25
…pping it

In sidebar display mode, reserve space for the panel with body margin-right and suspend the containing-block properties the host may set on <body> (transform, perspective, will-change) while the sidebar is open — all restored on close. Those properties make <body> the containing block for the fixed sidebar, re-anchoring it to the margin-shrunk body: the sidebar gets pushed (white gap) and, on tall pages, stretches to the document height and scrolls with the page. Suspending them keeps the sidebar a true viewport-fixed panel and leaves the host's own layout untouched.

Co-Authored-By: GitHub Copilot <noreply@github.com>
…pps in sidebar mode

Viewport-filling hosts (100vw / position: absolute inset shells like maps and dashboards) ignore the body margin, so the sidebar overlapped them. Add an optional hostRoot widget config: when set, sidebar mode constrains that element's width to calc(100vw - sidebarWidth) instead of nudging the body margin, restored on close. No behavior change when it is unset.

Co-Authored-By: GitHub Copilot <noreply@github.com>
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. e2e-tests Has E2E tests frontend Pertains to the frontend. labels Aug 27, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
Share a single getHostRoot helper and re-query the host on cleanup instead of reusing a cached node; use overflow-x: clip (not hidden) so the host does not become a scroll container; toggle the drag transition on whichever element is reserved so host-width drags animate too.

Co-Authored-By: GitHub Copilot <noreply@github.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Aug 27, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
…idth before transition

Cleanup now restores the exact node it styled using that node's captured styles, and undoes the body-margin reservation whenever the body fallback was used, so a hostRoot that is swapped or removed while open no longer leaves the wrong node or the body in a stale state.

Also flush the host width before enabling its transition: Chromium cannot interpolate width from auto to calc(), so it stuck the host at its pre-open width and never shrank. The body margin animates from 0 and is unaffected.

Co-Authored-By: GitHub Copilot <noreply@github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
RoyBA and others added 2 commits August 27, 2026 14:53
…malization

The browser rewrites inline style values: translateZ(0) becomes translateZ(0px) and calc(100vw - 400px) becomes calc(-400px + 100vw). Assert the restored transform against the normalized value and check the hostRoot width via its bounding rect instead of the raw calc string.

Co-Authored-By: GitHub Copilot <noreply@github.com>
…ssion

reserveSpace resolves the host fresh each call to handle SPA node swaps, but cleanup only restored the node captured at open, leaving a swapped-in host stuck at width: calc(100vw - Xpx). Track each styled host node in a ref map and restore them all on close.

Co-Authored-By: GitHub Copilot <noreply@github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
… fallback

The host path never touches body margin-right or transition, so restoring them unconditionally on close clobbered any host-app updates made while the sidebar was open. Restore them only when no host node was constrained.

Co-Authored-By: GitHub Copilot <noreply@github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread libs/copilot/src/hooks/useSidebarResize.ts Outdated
RoyBA and others added 3 commits August 27, 2026 15:25
hosts.size doubled as the body-fallback signal, so a hostRoot that disappeared mid-session (map non-empty, body margin written) skipped the margin restore and left the page shifted. Add a usedBodyFallback ref set on each body-branch write and reset per session; cleanup restores all host nodes always and body margin/transition only when the fallback was used.

Co-Authored-By: GitHub Copilot <noreply@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-tests Has E2E tests frontend Pertains to the frontend. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant