Files
fusion/packages/dashboard/app/components/FloatingWindow.css
gsxdsm bb11e493f7 fix(auth): restore Codex login and promote outboard resize targets
Operator report from a containerized dashboard: OpenAI Codex login never opened
a browser window at all, and floating windows still needed the FN-8015 follow-up.

- pi's `AuthPrompt` is a discriminated union — text, secret, select, manual_code —
  and FusionAuthStorage.login's interaction shim flattened every variant into
  `onPrompt({message, placeholder})`, discarding `type` and a select's `options`.
  pi's Codex `login()` OPENS with `prompt({type:"select"})` (Browser vs Device
  code) before emitting any auth URL, so the dashboard answered the method picker
  with the promise that waits for a pasted code — input the UI never solicits,
  because nothing had been surfaced yet. The flow hung until the route's 30s
  kickoff timeout: "Login initiation timed out", no window. The route's
  onSelect/selectOauthOption has had the right answer since FN-5917, but the
  callback was dead code from the moment login moved to pi's ModelRuntime.
  Verified against a real container: the login endpoint now returns Codex's
  auth.openai.com URL in 0.03s instead of timing out after 30s.
- Promote FN-8766's outboard east/NE/SE resize targets from Task Detail to every
  desktop window. With FN-8015's body gutter deleted, a hosted scrollbar sits
  flush against the painted edge where those hit zones used to cover it (issue
  #2140); moving the targets outside the shell keeps it grabbable without
  insetting anything. That needs the host to stop clipping, so the body and its
  direct child inherit the corner radius — only 8 of ~30 callers set that
  themselves — and phones re-assert clipping since they hide every handle.
- Document the fixed OAuth callback ports (Anthropic 53692, Codex 1455) and
  PI_OAUTH_CALLBACK_HOST for Docker: without them the browser callback cannot
  reach the container's loopback listener, which is why subscription logins
  appeared to fail there.

Verified: 14989 dashboard tests, 58 engine auth-storage tests (4 new, covering
each prompt type), pnpm test:gate, eslint, and both typechecks all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 17:38:26 -07:00

656 lines
28 KiB
CSS

/*
FNXC:FloatingWindow 2026-06-22-20:45:
FloatingWindow is a non-blocking floating window (generalized from RightDockExpandModal). The overlay is a full-viewport, transparent, NON-dimming, NON-blurring, click-through layer: `pointer-events: none` lets every click pass through to the app and to other windows behind it. Only the panel re-enables `pointer-events: auto`. Multiple overlays/panels coexist with no mutual blocking — z-stacking is driven by inline `z-index` from the component's per-window counter.
FNXC:FloatingWindow 2026-06-27-00:00:
Click-through overlays cannot implement backdrop clicks in CSS/DOM structure. Outside-click dismissal is therefore a component-level opt-in document listener for transient windows such as Quick Chat; persistent task and terminal pop-outs keep the default non-dismissable page-click behavior.
*/
.floating-window-overlay {
position: fixed;
inset: 0;
background: transparent;
backdrop-filter: none;
pointer-events: none;
}
/*
FNXC:FloatingWindow 2026-07-18-14:05:
FN-8340 resolves #2114: Quick Chat minimize must retain its desktop window and ≤768px full-screen
sheet in the layout tree. `visibility: hidden` removes paint while preserving child geometry and
message-list scroll state; `pointer-events: none` keeps the already click-through overlay inert.
Never replace this with `display: none`, which collapses layout and makes reopening visibly reflow.
*/
/*
FNXC:ModalTouchGeometry 2026-07-26-19:46:
FN-8619's blocking modal migrations retain the visible scrim and blur that Agent Detail previously
owned locally. Modal windows block the application beneath them; utility FloatingWindows remain
transparent and click-through so concurrent tools continue to coexist.
*/
.floating-window-overlay--modal {
background: color-mix(in srgb, var(--bg) 60%, transparent);
-webkit-backdrop-filter: blur(var(--space-xs));
backdrop-filter: blur(var(--space-xs));
pointer-events: auto;
}
.floating-window-overlay--hidden {
visibility: hidden;
pointer-events: none;
}
/*
FNXC:FloatingWindow 2026-06-22-20:45:
Floating panel positioned by state-driven inline `left/top/width/height` and stacked by inline `z-index`. min/max keep the panel usable and on-screen. `resize: none` because resizing is handled by the corner/edge handles. `pointer-events: auto` re-enables interaction on the panel only.
*/
.floating-window {
--floating-window-shadow: var(--shadow-lg);
position: fixed;
display: flex;
flex-direction: column;
min-width: calc(var(--space-2xl) * 7.5);
min-height: calc(var(--space-2xl) * 5.83);
max-width: calc(100vw - (var(--space-lg) * 2));
max-height: calc(100dvh - (var(--space-lg) * 2));
overflow: hidden;
background: var(--surface);
border: thin solid var(--border);
border-radius: var(--radius-lg);
/*
FNXC:FloatingWindow 2026-06-23-23:25:
Floating modals need a gentle, theme-controlled drop shadow. Use a local token with the app's existing shadow fallback instead of the undefined --shadow-xl so themes can soften, strengthen, or remove modal elevation intentionally.
*/
box-shadow: var(--floating-window-shadow, var(--shadow-lg));
color: var(--text);
resize: none;
pointer-events: auto;
}
/*
FNXC:FloatingWindow 2026-06-22-20:45:
Header is the drag handle. `touch-action: none` (matching the resize handles) hands the whole gesture to the pointer handlers so touch dragging stays smooth and never scrolls the page behind it. A comfortable min-height makes a forgiving touch target. `user-select: none` protects the drag from selecting header text.
*/
.floating-window__header {
display: flex;
flex-shrink: 0;
align-items: center;
gap: var(--space-sm);
min-height: 44px;
padding: var(--space-sm) var(--space-md);
border-bottom: thin solid var(--border);
background: var(--surface-2, var(--surface));
cursor: grab;
user-select: none;
touch-action: none;
}
.floating-window__header:active {
cursor: grabbing;
}
/*
FNXC:FloatingWindow 2026-06-22-12:20:
Headerless task pop-outs still need a visible drag affordance. The embedded task-detail modal header becomes the grab handle, matching the one-header "Open task" modal while preserving FloatingWindow's drag and resize behavior.
*/
.floating-window--headerless .floating-window__body {
overflow: hidden;
}
.floating-window--headerless .task-detail-content--embedded {
border-radius: inherit;
overflow: hidden;
}
.floating-window--headerless .task-detail-content--embedded > .modal-header {
cursor: grab;
user-select: none;
touch-action: none;
}
.floating-window--headerless .task-detail-content--embedded > .modal-header:active {
cursor: grabbing;
}
.floating-window--chat.floating-window--headerless .floating-window__body {
overflow: hidden;
}
.floating-window--chat .chat-view {
border-radius: inherit;
overflow: hidden;
}
/*
FNXC:ModalTouchGeometry 2026-07-26-12:19:
A bare `(pointer: coarse)` rule hid task-detail handles on every touch-primary device,
including tablets that must be movable and resizable. The JS tablet-touch discriminator
composes physical screen class, touch capability, and the 768px boundary, so phone sheets
and desktop hybrids retain their existing geometry while tablet touch gets active targets.
The shared scrollbar gutter stays in place: it keeps hosted scrollbars clear of east handles.
*/
.floating-window--touch-geometry {
overflow: visible;
}
/*
FNXC:ModalTouchGeometry 2026-07-26-12:19:
The painted borders remain mouse-sized, but tablet fingers receive FN-8602's shared
44px effective target outside the panel. Edges begin beyond corner targets so they do
not cover close controls, hosted footer actions, or scrollable content.
*/
.floating-window--touch-geometry .floating-window__resize-handle {
z-index: 3;
touch-action: none;
}
/*
FNXC:ModalTouchGeometry 2026-07-26-12:34:
A headerless window delegates dragging to a caller-owned header, so that resolved element must
receive the same tablet-only target floor as FloatingWindow's built-in header. Generic windows
retain that established 44px layout contract.
*/
.floating-window--touch-geometry .floating-window__delegated-drag-handle {
box-sizing: border-box;
min-block-size: var(--modal-resize-touch-target);
cursor: grab;
user-select: none;
touch-action: none;
}
/*
FNXC:ModalTouchGeometry 2026-07-26-15:30:
Task-detail pop-outs are the only delegated-header surface with the reported tablet density
regression. Their 44px drag contract is hit-area-only: an absolute pseudo-element extends the
pointer target without making the painted task header taller. Keep the target below controls so a
close or action button receives its tap. Do not widen this selector because FloatingWindow also
hosts unrelated chat, import, workflow, and utility panels.
*/
.floating-window--task-detail.floating-window--touch-geometry .floating-window__delegated-drag-handle {
min-block-size: 0;
position: relative;
z-index: 0;
}
.floating-window--task-detail.floating-window--touch-geometry .floating-window__delegated-drag-handle::before {
content: "";
position: absolute;
z-index: 0;
inset-inline: 0;
block-size: var(--modal-resize-touch-target);
inset-block-start: 50%;
transform: translateY(-50%);
}
/*
FNXC:ModalTouchGeometry 2026-07-26-16:00:
The invisible tablet drag extension must stay beneath actionable task-header descendants.
Explicit stacking preserves the 44px parent hit area while buttons and links retain their normal
activation instead of beginning a window drag.
*/
.floating-window--task-detail.floating-window--touch-geometry .floating-window__delegated-drag-handle :is(button, a, input, select, textarea, [role="button"]) {
position: relative;
z-index: 1;
}
.floating-window--touch-geometry .floating-window__delegated-drag-handle:active {
cursor: grabbing;
}
.floating-window--touch-geometry .floating-window__resize-handle--n,
.floating-window--touch-geometry .floating-window__resize-handle--s {
left: var(--modal-resize-touch-target);
right: var(--modal-resize-touch-target);
height: var(--modal-resize-touch-target);
}
.floating-window--touch-geometry .floating-window__resize-handle--n {
top: calc((var(--modal-resize-touch-target) - var(--space-sm)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--s {
bottom: calc((var(--modal-resize-touch-target) - var(--space-sm)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--e,
.floating-window--touch-geometry .floating-window__resize-handle--w {
top: var(--modal-resize-touch-target);
bottom: var(--modal-resize-touch-target);
width: var(--modal-resize-touch-target);
}
.floating-window--touch-geometry .floating-window__resize-handle--e {
right: calc((var(--modal-resize-touch-target) - var(--space-sm)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--w {
left: calc((var(--modal-resize-touch-target) - var(--space-sm)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--ne,
.floating-window--touch-geometry .floating-window__resize-handle--nw,
.floating-window--touch-geometry .floating-window__resize-handle--se,
.floating-window--touch-geometry .floating-window__resize-handle--sw {
width: var(--modal-resize-touch-target);
height: var(--modal-resize-touch-target);
}
.floating-window--touch-geometry .floating-window__resize-handle--ne {
top: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
right: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--nw {
top: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
left: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--se {
bottom: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
right: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
}
.floating-window--touch-geometry .floating-window__resize-handle--sw {
bottom: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
left: calc((var(--modal-resize-touch-target) - var(--space-lg)) * -1);
}
/*
FNXC:ModalTouchGeometry 2026-07-26-12:19:
Phone sheets are strictly below 768px. At exactly 768px this query must not match so
`isTabletTouchViewport` can expose the active tablet touch targets without a cascade conflict.
FNXC:ChatModal 2026-06-22-14:49:
On mobile/narrow app viewports, opening Quick Chat should present the full Chat modal as a full-screen sheet instead of a small draggable desktop window. Scope this to the chat FloatingWindow and override the inline desktop geometry only at the mobile breakpoint; desktop pop-out behavior remains movable/resizable.
*/
/*
FNXC:TaskDetailLayout 2026-08-17-23:47:
FN-8766 gave the desktop Task Detail popup a symmetric painted edge by removing the shared body
gutter here and moving its east resize hit areas OUTSIDE the painted shell, so the scrollbar stays
usable and the header-owned close control does not sit behind a resize target. The shared gutter is
now gone for every caller, so the local zeroing that used to accompany these rules is deleted; the
outboard targets remain, and they are the pattern to copy wherever a hosted scrollbar and a resize
hot zone actually collide. Tablet retains its existing touch geometry and phones remain full-screen
sheets below.
*/
/*
FNXC:FloatingWindow 2026-08-18-00:26:
OUTBOARD EAST TARGETS ARE THE SHARED DESKTOP CONTRACT, not a task-detail special case. With
FN-8015's body gutter deleted, a hosted scrollbar sits flush against the painted right edge, where
the east/north-east/south-east hit zones used to cover it — the exact grab conflict (issue #2140)
the gutter existed to prevent. FN-8766 proved the better remedy on Task Detail: let the panel keep
its full width and move the hit areas OUTSIDE the painted shell, so the scrollbar is fully grabbable
and nothing is inset. Promoted here for every desktop window rather than waiting for each caller to
rediscover the conflict.
Requires `overflow: visible` on the host, which stops the window clipping its children — so the body
and its direct child inherit the corner radius below, since only 8 of ~30 callers used to set that
themselves and the rest would paint square corners over the rounded shell. Phones re-assert clipping
(they hide every resize handle, so they need no outboard room) and tablets keep touch geometry.
*/
.floating-window:not(.floating-window--tablet-viewport) {
overflow: visible;
}
.floating-window:not(.floating-window--tablet-viewport) .floating-window__resize-handle--e {
right: calc(var(--space-sm) * -1);
}
.floating-window:not(.floating-window--tablet-viewport) .floating-window__resize-handle--ne,
.floating-window:not(.floating-window--tablet-viewport) .floating-window__resize-handle--se {
right: calc(var(--space-lg) * -1);
}
/* The window no longer clips, so clipping moves to the surfaces that actually paint. */
.floating-window__body,
.floating-window__body > * {
border-radius: inherit;
}
/*
FNXC:ModalTouchGeometry 2026-07-26-13:25:
FN-8606 migrated core and workflow dialogs share FloatingWindow's pointer contract. Their
caller-owned headers remain the drag target on desktop/tablet; true phones and short sheets hide
all floating affordances so persisted desktop geometry cannot imply a draggable sheet.
*/
.floating-window--activity-log .floating-window__body,
.floating-window--scripts .floating-window__body,
.floating-window--add-node .floating-window__body,
.floating-window--connect-node .floating-window__body,
.floating-window--node-detail .floating-window__body,
.floating-window--workflow-add-step .floating-window__body,
.floating-window--group-task .floating-window__body,
.floating-window--changes-diff .floating-window__body,
.floating-window--model-onboarding .floating-window__body,
.floating-window--git-manager .floating-window__body,
.floating-window--settings .floating-window__body,
.floating-window--planning-mode .floating-window__body,
/* FNXC:ModalTouchGeometry 2026-07-26-19:25: Create Room shares migrated dialogs' host-owned body sizing while its nested member list retains picker scrolling. */
.floating-window--create-room .floating-window__body {
overflow: hidden;
}
.floating-window--activity-log .modal,
.floating-window--scripts .modal,
.floating-window--add-node .modal,
.floating-window--connect-node .modal,
.floating-window--node-detail .modal,
.floating-window--group-task .modal,
.floating-window--workflow-add-step .wf-add-step-dialog,
.floating-window--changes-diff .changes-diff-modal,
.floating-window--model-onboarding .model-onboarding-modal,
.floating-window--git-manager .gm-modal,
.floating-window--settings .settings-modal,
.floating-window--planning-mode .planning-modal,
.floating-window--create-room .create-room-modal {
width: 100%;
height: 100%;
max-width: none;
max-height: none;
border-radius: inherit;
}
@media (max-width: 767.98px), (max-height: 480px) {
/*
FNXC:FloatingWindow 2026-08-18-00:26:
Phones re-assert clipping for EVERY window. The desktop rule above drops `overflow: hidden` so the
east resize targets can sit outside the painted shell, and its `:not(--tablet-viewport)` predicate
also matches phones — where those handles are hidden anyway, so the unclipped host buys nothing and
a sheet child could paint past the shell. Task Detail already needed this reassertion for its own
FN-8766 rule; promoting the outboard targets to every window promotes this with them.
*/
.floating-window {
overflow: hidden;
}
/*
FNXC:FloatingWindow 2026-07-12-17:35:
Mobile keeps the global `styles.css` pan-y lockdown so the dashboard cannot drift, but movable FloatingWindow headers must still resolve to an effective `touch-action: none`. Reassert the drag-handle contract at the mobile breakpoint, excluding full-screen sheet variants, so a single-finger header drag stays on the captured pointermove stream instead of being intersected back into page pan by the ancestor chain. Desktop drag/resize and mobile sheet variants are unchanged.
*/
.floating-window:not(.floating-window--chat):not(.floating-window--github-import-detail):not(.floating-window--task-detail):not(.floating-window--workflow-editor):not(.floating-window--automation):not(.floating-window--mission-interview):not(.floating-window--file-browser):not(.floating-window--pr-create):not(.floating-window--activity-log):not(.floating-window--scripts):not(.floating-window--add-node):not(.floating-window--connect-node):not(.floating-window--node-detail):not(.floating-window--workflow-add-step):not(.floating-window--group-task):not(.floating-window--changes-diff):not(.floating-window--model-onboarding):not(.floating-window--git-manager):not(.floating-window--settings):not(.floating-window--planning-mode):not(.artifacts-gallery-window) .floating-window__header {
touch-action: none;
}
/* FNXC:ModalTouchGeometry 2026-07-26-16:10: Automations shares every FN-8606 sheet breakpoint: a short viewport must not expose desktop geometry even when it remains wider than a phone. */
/*
FNXC:ModalTouchGeometry 2026-07-28-14:30:
FN-8607's agent, onboarding, mailbox, interview, and subtask modal classes use the
same phone/short-viewport sheet contract as the earlier FloatingWindow migrations.
FN-8620's New Task, right-dock, and terminal hosts share that contract and must remain
included. Geometry suspension in React must also change the panel, rather than leaving
a fixed desktop-sized dialog on a short or true-phone viewport.
*/
.floating-window.new-task-modal,
.floating-window.right-dock-expand-modal--floating,
.floating-window.terminal-modal--floating,
.floating-window--agent-list,
.floating-window--agent-import,
.floating-window--agent-generation,
.floating-window--agent-onboarding,
.floating-window--experimental-agent-onboarding,
.floating-window--setup-wizard,
.floating-window--native-shell-onboarding,
.floating-window--docker-node-onboarding,
.floating-window--agent-detail,
.floating-window--github-import,
.floating-window--mailbox,
.floating-window--milestone-slice-interview,
.floating-window--subtask-breakdown,
.floating-window--automation,
.floating-window--activity-log,
.floating-window--scripts,
.floating-window--add-node,
.floating-window--connect-node,
.floating-window--node-detail,
.floating-window--workflow-add-step,
.floating-window--group-task,
.floating-window--changes-diff,
.floating-window--model-onboarding,
.floating-window--git-manager,
.floating-window--settings,
.floating-window--planning-mode,
.floating-window--create-room,
.floating-window--chat {
inset: 0 !important;
width: 100vw !important;
height: 100dvh !important;
min-width: 0 !important;
min-height: 0 !important;
max-width: 100vw !important;
max-height: 100dvh !important;
border: none;
border-radius: 0;
box-shadow: none;
}
.floating-window.new-task-modal .floating-window__resize-handle,
.floating-window.right-dock-expand-modal--floating .floating-window__resize-handle,
.floating-window.terminal-modal--floating .floating-window__resize-handle,
.floating-window--agent-list .floating-window__resize-handle,
.floating-window--agent-import .floating-window__resize-handle,
.floating-window--agent-generation .floating-window__resize-handle,
.floating-window--agent-onboarding .floating-window__resize-handle,
.floating-window--experimental-agent-onboarding .floating-window__resize-handle,
.floating-window--setup-wizard .floating-window__resize-handle,
.floating-window--native-shell-onboarding .floating-window__resize-handle,
.floating-window--docker-node-onboarding .floating-window__resize-handle,
.floating-window--agent-detail .floating-window__resize-handle,
.floating-window--github-import .floating-window__resize-handle,
.floating-window--mailbox .floating-window__resize-handle,
.floating-window--milestone-slice-interview .floating-window__resize-handle,
.floating-window--subtask-breakdown .floating-window__resize-handle,
.floating-window--automation .floating-window__resize-handle,
.floating-window--activity-log .floating-window__resize-handle,
.floating-window--scripts .floating-window__resize-handle,
.floating-window--add-node .floating-window__resize-handle,
.floating-window--connect-node .floating-window__resize-handle,
.floating-window--node-detail .floating-window__resize-handle,
.floating-window--workflow-add-step .floating-window__resize-handle,
.floating-window--group-task .floating-window__resize-handle,
.floating-window--changes-diff .floating-window__resize-handle,
.floating-window--model-onboarding .floating-window__resize-handle,
.floating-window--git-manager .floating-window__resize-handle,
.floating-window--settings .floating-window__resize-handle,
.floating-window--planning-mode .floating-window__resize-handle,
.floating-window--create-room .floating-window__resize-handle,
.floating-window--chat .floating-window__resize-handle {
display: none;
}
/*
FNXC:QuickAddAttachments 2026-07-16-00:00:
Pending task-attachment images open in a movable, resizable FloatingWindow on desktop. On narrow touch viewports the dedicated image-preview variant must instead fill the screen and remove desktop resize handles.
*/
.floating-window--image-preview {
inset: 0 !important;
width: 100vw !important;
height: 100dvh !important;
min-width: 0 !important;
min-height: 0 !important;
max-width: 100vw !important;
max-height: 100dvh !important;
border: none;
border-radius: 0;
box-shadow: none;
}
.floating-window--image-preview .floating-window__resize-handle {
display: none;
}
/*
FNXC:GitHubImport 2026-07-15-16:00:
Import details use FloatingWindow for desktop drag and resize; on mobile this scoped variant becomes a full-screen sheet
and hides desktop resize affordances.
*/
.floating-window--github-import-detail {
inset: 0 !important;
width: 100vw !important;
height: 100dvh !important;
min-width: 0 !important;
min-height: 0 !important;
max-width: 100vw !important;
max-height: 100dvh !important;
border: none;
border-radius: 0;
box-shadow: none;
}
.floating-window--github-import-detail .floating-window__resize-handle {
display: none;
}
.floating-window--chat .chat-view {
border-radius: 0;
}
/*
FNXC:MobileTaskPopups 2026-06-29-00:00:
Mobile board-card popup opens reuse the existing task-detail FloatingWindow, but the shell must behave like a full-screen mobile sheet. Scope the override to task-detail pop-outs, hide resize handles, and remove drag/resize cursors so no invisible desktop affordances remain on touch layouts.
*/
.floating-window--task-detail {
inset: 0 !important;
overflow: hidden !important;
width: 100vw !important;
height: 100dvh !important;
min-width: 0 !important;
min-height: 0 !important;
max-width: 100vw !important;
max-height: 100dvh !important;
border: none;
border-radius: 0;
box-shadow: none;
}
.floating-window--task-detail .floating-window__resize-handle {
display: none;
}
/*
FNXC:MobileTaskPopups 2026-08-17-23:47:
The mobile task-detail and standalone GitHub Import sheets each carried a local
`margin-inline-end: 0` to undo the shared body gutter, which left them shifted left with an uneven
right inset on every row (FN-8722, and the mobile task-detail left-shift fix). The shared gutter is
deleted, so both overrides are gone: `.detail-body` and the import shell's own symmetric padding
now define both insets directly.
*/
.floating-window--task-detail .task-detail-content--embedded,
.floating-window--task-detail .task-detail-content--embedded > .modal-header {
border-radius: 0;
cursor: default;
touch-action: auto;
}
}
/*
FNXC:FloatingWindow 2026-07-15-18:20:
Long titles (e.g. the GitHub import detail's "#2159 — <issue title>") must truncate with an ellipsis rather than hard-cut mid-word against the close button.
display MUST stay block, not flex: every caller passes a plain string, so under flex the text became an anonymous flex item, which text-overflow cannot act on — the ellipsis declared here silently never rendered. A block box makes the existing overflow/text-overflow/white-space trio work as written.
Reintroducing flex here (e.g. to lay out an element title) re-breaks truncation for all callers; give the title an inner element instead.
*/
.floating-window__title {
display: block;
flex: 1;
min-width: 0;
overflow: hidden;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.floating-window__close {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-xs);
border: none;
border-radius: var(--radius-sm);
background: transparent;
color: var(--text-muted);
cursor: pointer;
}
.floating-window__close:hover {
background: var(--status-todo-bg, var(--surface));
color: var(--text);
}
/*
FNXC:FloatingWindow 2026-06-22-20:45:
Body is a flex host that lets its single child stretch to the full panel width/height (min-width/min-height:0 so a wide child cannot collapse the flex line, and the child's own overflow can engage).
FNXC:FloatingWindow 2026-08-17-23:47:
THE SHARED BODY HAS NO INLINE-END GUTTER. FN-8015 / issue #2140 previously reserved
`margin-inline-end: var(--space-lg)` here so a hosted vertical scrollbar stayed inboard of the east,
north-east, and south-east resize hot zones. Operator removed it: one shared reservation that every
caller had to know about produced a recurring class of asymmetric-right-inset bugs instead — the
window's own surface showed through beside each child panel, so callers read it as unexplained
padding on the right. It was zeroed piecemeal five separate times (desktop task detail FN-8766,
mobile task detail, mobile GitHub import FN-8722, phone Git Manager FN-8702, and every tablet
window), and the callers that instead LEANED on it for their right inset had to restore it locally
whenever one of those predicates fired. FN-8630 / FN-8634 and the 2026-08-01 tablet fix are the same
bug recurring.
The accepted trade-off: a hosted scrollbar now sits under the east/corner resize targets. Where that
conflict is actually felt, fix it the FN-8766 way — move that caller's east targets outboard of the
painted shell (`overflow: visible` plus negative `right` offsets on --e/--ne/--se) — rather than
reintroducing a shared gutter. Do not re-add `margin-inline-end` to this rule.
*/
.floating-window__body {
display: flex;
flex: 1;
min-width: 0;
min-height: 0;
overflow: auto;
}
.floating-window__body > * {
flex: 1;
min-width: 0;
min-height: 0;
min-block-size: 0;
}
/*
FNXC:FloatingWindow 2026-06-22-20:45:
Edge + corner resize handles. touch-action:none keeps the drag from being hijacked by scroll/gestures so resizing stays smooth.
*/
.floating-window__resize-handle {
position: absolute;
z-index: 2;
touch-action: none;
}
.floating-window__resize-handle--n,
.floating-window__resize-handle--s {
left: var(--space-sm);
right: var(--space-sm);
height: var(--space-sm);
cursor: ns-resize;
}
.floating-window__resize-handle--n { top: 0; }
.floating-window__resize-handle--s { bottom: 0; }
.floating-window__resize-handle--e,
.floating-window__resize-handle--w {
top: var(--space-sm);
bottom: var(--space-sm);
width: var(--space-sm);
cursor: ew-resize;
}
.floating-window__resize-handle--e { right: 0; }
.floating-window__resize-handle--w { left: 0; }
.floating-window__resize-handle--ne,
.floating-window__resize-handle--nw,
.floating-window__resize-handle--se,
.floating-window__resize-handle--sw {
width: var(--space-lg);
height: var(--space-lg);
}
.floating-window__resize-handle--ne { top: 0; right: 0; cursor: nesw-resize; }
.floating-window__resize-handle--nw { top: 0; left: 0; cursor: nwse-resize; }
.floating-window__resize-handle--se { bottom: 0; right: 0; cursor: nwse-resize; }
.floating-window__resize-handle--sw { bottom: 0; left: 0; cursor: nesw-resize; }