85b303e387
Drag the floating AI-chat window onto the sidebar and release over it to DOCK it — the window pins to the live navbar rect, overlaying the page tree; a drop-zone highlight shows while dragging over it. Closing the chat re-shows the tree. Undock via a header button or by dragging the docked window back onto content (pops out floating at the drop point). The docked/floating mode persists in localStorage and the docked window follows the navbar width (manual resize, space<->shared route change) via a ResizeObserver + sidebar-toggle/transitionend re-sync; when the navbar is collapsed/absent the window falls back to floating instead of vanishing. Dock/undock only flips a mode atom + geometry — ChatThread is never remounted, so an in-flight response stream is not interrupted. Frontend only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
195 lines
5.5 KiB
CSS
195 lines
5.5 KiB
CSS
/* Floating AI chat window shell. Ported from the GitmostAgent.jsx design.
|
|
Dynamic values (left/top/width/height) stay inline on the element; only the
|
|
static chrome lives here. */
|
|
|
|
.window {
|
|
/* position: fixed + left/top/width/height are applied inline so the window
|
|
floats over the whole viewport. z-index sits above page content and the
|
|
app shell but BELOW Mantine overlays (modals=200, menus=300,
|
|
notifications=400) so the rename input, kebab menu and delete-confirm
|
|
modal still render above the window. */
|
|
position: fixed;
|
|
z-index: 105;
|
|
background: light-dark(#fff, var(--mantine-color-dark-7));
|
|
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
|
/* Match the rest of the UI: use the Mantine md radius token instead of an
|
|
oversized hard-coded value so the window corners blend with inner cards. */
|
|
border-radius: var(--mantine-radius-md);
|
|
box-shadow:
|
|
0 10px 24px rgba(0, 0, 0, 0.13),
|
|
0 30px 64px rgba(0, 0, 0, 0.17);
|
|
overflow: hidden;
|
|
resize: both;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 300px;
|
|
min-height: 400px;
|
|
max-width: 900px;
|
|
max-height: 1100px;
|
|
font-size: 11px;
|
|
color: light-dark(var(--mantine-color-black), var(--mantine-color-dark-0));
|
|
}
|
|
|
|
/* Hide the native resizer; we draw our own affordance icon in the corner. */
|
|
.window::-webkit-resizer {
|
|
background: transparent;
|
|
}
|
|
|
|
/* Docked into the sidebar: the window pins itself to the live navbar rect
|
|
(position/size supplied inline). It sits flush inside the navbar area, so we
|
|
drop the floating chrome — no border-radius, drop shadow or user resize — and
|
|
remove the floating min/max clamps so the size is driven ENTIRELY by the
|
|
inline navbar rect (which may be narrower than the floating min-width of
|
|
300px, e.g. the 220px navbar minimum). z-index 105 keeps it above the page
|
|
tree (navbar 101) but below the header and Mantine overlays. */
|
|
.docked {
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
resize: none;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
max-width: none;
|
|
max-height: none;
|
|
}
|
|
|
|
/* Drop-zone highlight shown over the navbar bounds while a floating window is
|
|
dragged onto the sidebar. Sits just above the docked window (106) so the cue
|
|
is visible; purely decorative, so it never intercepts pointer events. */
|
|
.dockHighlight {
|
|
position: fixed;
|
|
z-index: 106;
|
|
border: 2px dashed light-dark(var(--mantine-color-blue-5), var(--mantine-color-blue-4));
|
|
background: light-dark(rgba(34, 139, 230, 0.08), rgba(34, 139, 230, 0.14));
|
|
border-radius: var(--mantine-radius-sm);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* When minimized the window collapses to the header only: auto height, no
|
|
resize. Width/height inline values are overridden. */
|
|
.minimized {
|
|
height: auto !important;
|
|
min-height: 0 !important;
|
|
resize: none;
|
|
}
|
|
|
|
/* Body wrapper (history + chat thread). Always rendered so ChatThread stays
|
|
mounted; hidden (not unmounted) when minimized so an in-flight stream keeps
|
|
running and the window collapses to just the header. */
|
|
.content {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.minimized .content {
|
|
display: none;
|
|
}
|
|
|
|
/* In the collapsed state the header expands the window on click, so hint that
|
|
it is clickable (override the drag `grab` cursor). */
|
|
.minimized .dragBar {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dragBar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 9px 11px;
|
|
border-bottom: 1px solid light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
|
background: light-dark(#fcfcfd, var(--mantine-color-dark-6));
|
|
flex: none;
|
|
cursor: grab;
|
|
user-select: none;
|
|
}
|
|
|
|
.title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.headerBtn {
|
|
width: 24px;
|
|
height: 24px;
|
|
border: none;
|
|
background: transparent;
|
|
border-radius: 6px;
|
|
color: var(--mantine-color-dimmed);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
}
|
|
|
|
.headerBtn:hover {
|
|
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
|
}
|
|
|
|
.badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 11px;
|
|
color: var(--mantine-color-dimmed);
|
|
background: light-dark(#f6f7f8, var(--mantine-color-dark-6));
|
|
border: 1px solid light-dark(#eceef0, var(--mantine-color-dark-4));
|
|
border-radius: 6px;
|
|
padding: 3px 9px;
|
|
}
|
|
|
|
.historySection {
|
|
padding: 6px 8px;
|
|
border-bottom: 1px solid light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
|
flex: none;
|
|
}
|
|
|
|
.historyHeader {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 4px 6px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
color: var(--mantine-color-dimmed);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.newChatBtn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 4px 7px;
|
|
border-radius: 6px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--mantine-color-dimmed);
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.newChatBtn:hover {
|
|
background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
|
}
|
|
|
|
.body {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 13px 14px 12px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.resizeHandle {
|
|
position: absolute;
|
|
right: 3px;
|
|
bottom: 3px;
|
|
color: light-dark(#ced4da, var(--mantine-color-dark-3));
|
|
pointer-events: none;
|
|
display: flex;
|
|
}
|