Standardize dashboard modal geometry and interaction behavior through the shared FloatingWindow contract. - Migrate New Task, right-dock expansion, terminal, onboarding, and utility modals to FloatingWindow. - Expand shared window resizing, positioning, accessibility, and contract coverage. - Document the modal migration inventory and publish changesets. Files changed: .changeset/fn-8607-floating-modal-contract.md | 7 + .changeset/fn-8620-bespoke-geometry-modals.md | 7 + docs/dashboard-guide.md | 47 ++ docs/dashboard-modal-inventory.md | 6 +- .../app/components/AgentGenerationModal.tsx | 14 +- .../dashboard/app/components/AgentImportModal.tsx | 7 +- .../dashboard/app/components/AgentListModal.tsx | 8 +- .../app/components/AgentOnboardingModal.tsx | 6 +- .../app/components/DockerNodeOnboardingModal.tsx | 14 +- .../ExperimentalAgentOnboardingModal.tsx | 6 +- .../dashboard/app/components/FloatingWindow.css | 21 + .../dashboard/app/components/FloatingWindow.tsx | 62 ++- packages/dashboard/app/components/MailboxModal.tsx | 14 +- .../components/MilestoneSliceInterviewModal.tsx | 12 +- .../app/components/NativeShellOnboardingModal.tsx | 6 +- packages/dashboard/app/components/NewTaskModal.css | 196 +------ packages/dashboard/app/components/NewTaskModal.tsx | 593 +++++++-------------- packages/dashboard/app/components/RightDock.css | 84 +-- .../app/components/RightDockExpandModal.tsx | 333 ++---------- .../dashboard/app/components/SetupWizardModal.tsx | 20 +- .../app/components/SubtaskBreakdownModal.tsx | 6 +- .../dashboard/app/components/TerminalModal.css | 120 +---- .../dashboard/app/components/TerminalModal.tsx | 321 ++--------- .../__tests__/AgentModals.floatingWindow.test.tsx | 26 + .../components/__tests__/FloatingWindow.test.tsx | 27 +- .../app/components/__tests__/NewTaskModal.test.tsx | 66 +-- .../OnboardingModals.floatingWindow.test.tsx | 20 + .../app/components/__tests__/RightDock.test.tsx | 18 +- .../components/__tests__/TerminalModal.test.tsx | 61 +-- .../UtilityModals.floatingWindow.test.tsx | 18 + .../components/__tests__/migratedModalFixtures.tsx | 25 + .../__tests__/modalFloatingWindowContract.test.tsx | 30 ++ 32 files changed, 726 insertions(+), 1475 deletions(-) Fusion-Task-Id: FN-8620 Fusion-Task-Lineage: 2a912260-3292-4b13-bcf0-9de5a3df8ccd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
266 lines
8.8 KiB
CSS
266 lines
8.8 KiB
CSS
/*
|
|
FNXC:Navigation 2026-06-21-00:00:
|
|
The right dock CSS uses a mobile media query as a belt-and-suspenders guard only. The authoritative mobile gate is the JS `rightDockActive` value from `useViewportMode`, which also covers phone classes that a width-only query cannot classify reliably.
|
|
*/
|
|
/*
|
|
FNXC:Navigation 2026-06-22-00:10:
|
|
The right dock OVERLAYS the page content (floats over the right edge) instead of being a flex sibling that shrinks the main content. It is absolutely positioned against the project shell (which is position:relative) so opening/closing or resizing it never reflows the page beneath. z-index sits above content but below the docked terminal/modals.
|
|
*/
|
|
.right-dock {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 20;
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* FNXC:RightDock 2026-06-25-13:15: Define the dock width override hooks on the host so the var() references resolve (token-validity guard); themes/JS can still override them. */
|
|
--right-dock-min-width: calc(var(--space-2xl) * 8);
|
|
--right-dock-max-width: calc(var(--space-2xl) * 40);
|
|
min-width: min(100%, var(--right-dock-min-width, calc(var(--space-2xl) * 8)));
|
|
/*
|
|
FNXC:RightDock 2026-06-23-00:50:
|
|
Raised the right-dock max-width default from calc(var(--space-2xl) * 22) (~704px) to calc(var(--space-2xl) * 40) (~1280px) so the dock can be dragged MUCH wider (matching the raised RIGHT_DOCK_MAX_WIDTH JS clamp) and the Files view has room for its tree|viewer two-pane split. The min(100%, ...) wrapper is kept so the dock can never exceed the viewport regardless of the larger cap.
|
|
*/
|
|
max-width: min(100%, var(--right-dock-max-width, calc(var(--space-2xl) * 40)));
|
|
min-height: 0;
|
|
background: var(--surface);
|
|
/*
|
|
FNXC:RightDockChrome 2026-06-23-19:10:
|
|
Right-dock dividing lines should be invisible by default, including the shell edge, toolbar, and selected-view header. Use theme tokens instead of removing border geometry so themes can re-enable the dividers by setting --chrome-divider-color or the right-dock-specific aliases.
|
|
*/
|
|
border-left: var(--chrome-divider-width, 1px) solid var(--right-dock-shell-divider-color, transparent);
|
|
color: var(--text);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
/*
|
|
FNXC:RightDockPin 2026-06-27-00:00:
|
|
Pinned mode is CSS-only: the project shell is already a flex row, so switching the dock from absolute overlay to relative in-flow reserves the dock width and shrinks `.project-content` without an App shell class. Keep position:relative (not static) so the absolute resize handle remains anchored to the dock, and remove the floating shadow so the pinned dock reads as a sibling pane with the existing tokenized divider. The mobile `.right-dock { display:none }` media guard still wins because this modifier does not override display.
|
|
*/
|
|
.right-dock--pinned {
|
|
position: relative;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.right-dock--with-footer {
|
|
padding-bottom: var(--executor-footer-height);
|
|
}
|
|
|
|
.right-dock--collapsed {
|
|
width: calc(var(--space-2xl) + var(--space-sm));
|
|
min-width: calc(var(--space-2xl) + var(--space-sm));
|
|
max-width: calc(var(--space-2xl) + var(--space-sm));
|
|
}
|
|
|
|
.right-dock__resize-handle {
|
|
position: absolute;
|
|
inset-block: 0;
|
|
inset-inline-start: calc(var(--space-xs) * -1);
|
|
z-index: 2;
|
|
width: var(--space-sm);
|
|
cursor: col-resize;
|
|
touch-action: none;
|
|
}
|
|
|
|
.right-dock__resize-handle::before {
|
|
position: absolute;
|
|
inset-block: 0;
|
|
inset-inline-start: calc(var(--space-xs) - var(--btn-border-width));
|
|
width: var(--btn-border-width);
|
|
background: transparent;
|
|
content: "";
|
|
transition: background var(--transition-fast);
|
|
}
|
|
|
|
.right-dock__resize-handle:hover::before,
|
|
.right-dock__resize-handle:focus-visible::before {
|
|
background: var(--todo);
|
|
}
|
|
|
|
.right-dock__resize-handle:focus-visible {
|
|
outline: none;
|
|
}
|
|
|
|
.right-dock__toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm);
|
|
border-bottom: var(--chrome-divider-width, 1px) solid var(--right-dock-toolbar-divider-color, transparent);
|
|
}
|
|
|
|
.right-dock--collapsed .right-dock__toolbar {
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
height: 100%;
|
|
}
|
|
|
|
.right-dock__tabs,
|
|
.right-dock__actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.right-dock__tabs {
|
|
flex: 1;
|
|
overflow-x: auto;
|
|
scrollbar-width: thin;
|
|
}
|
|
|
|
.right-dock--collapsed .right-dock__tabs,
|
|
.right-dock--collapsed .right-dock__actions {
|
|
flex-direction: column;
|
|
overflow: visible;
|
|
}
|
|
|
|
.right-dock__tab {
|
|
flex-shrink: 0;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.right-dock__tab--active,
|
|
.right-dock__tab[aria-selected="true"] {
|
|
background: var(--status-todo-bg);
|
|
color: var(--todo);
|
|
}
|
|
|
|
.right-dock__header {
|
|
display: flex;
|
|
flex-shrink: 0;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-bottom: var(--chrome-divider-width, 1px) solid var(--right-dock-view-header-divider-color, transparent);
|
|
color: var(--text);
|
|
}
|
|
|
|
.right-dock__title {
|
|
min-width: 0;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
color: inherit;
|
|
font: inherit;
|
|
font-weight: 600;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/*
|
|
FNXC:RightDockEmbedded 2026-06-22-00:00:
|
|
The dock body is the shared narrow host (~280-420px) for embedded tool views while the viewport stays desktop.
|
|
Make it an inline-size query container named `right-dock-body` so each hosted view can mirror its phone-width
|
|
(@media max-width:768px) single-column layout off the DOCK width instead of the viewport, which the view's own
|
|
@media rules can never see here. Views without their own `--embedded` variant (DevServerView, SecretsView,
|
|
PullRequestView) attach their @container rules to this container; ActivityLog/GitManager use their own embedded
|
|
container. NOTE: the wide right-dock expand modal is NOT this container, so its layout is unaffected.
|
|
*/
|
|
.right-dock__body {
|
|
display: flex;
|
|
flex: 1;
|
|
min-height: 0;
|
|
min-width: 0;
|
|
overflow: auto;
|
|
container-type: inline-size;
|
|
container-name: right-dock-body;
|
|
}
|
|
|
|
/*
|
|
FNXC:RightDockEmbedded 2026-06-22-15:30:
|
|
The hosted view is a flex child of the dock body; without min-height:0 it cannot shrink below its content height, so a tall view (e.g. DevServerView) blows past the dock and its own overflow-y:auto never engages. min-height:0 + min-block-size:0 lets the child bound itself so the view (or the dock body) actually scrolls vertically.
|
|
*/
|
|
.right-dock__body > * {
|
|
flex: 1;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
min-block-size: 0;
|
|
}
|
|
|
|
.right-dock-expand-modal {
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/*
|
|
FNXC:ModalTouchGeometry 2026-07-27-19:00:
|
|
The expanded dock is now content within FloatingWindow. Retain its visual identity while letting
|
|
its dimensions and position belong exclusively to the shared host rather than a nested fixed panel.
|
|
*/
|
|
.right-dock-expand-modal--floating {
|
|
width: 100%;
|
|
height: 100%;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
max-width: none;
|
|
max-height: none;
|
|
resize: none;
|
|
pointer-events: auto;
|
|
box-shadow: var(--floating-window-shadow, var(--shadow-lg));
|
|
}
|
|
|
|
/*
|
|
FNXC:RightDock 2026-06-22-17:40:
|
|
Header is the drag handle; grab/grabbing cursor and non-selectable text signal and protect the drag.
|
|
|
|
FNXC:RightDock 2026-06-22-18:50:
|
|
Touch dragging was janky because the browser claimed the header's touch stream for scroll/pan gestures. `touch-action: none` on the drag handle (matching the resize handles) hands the whole gesture to our pointer handlers so a finger drag stays smooth and never scrolls the page behind it. `cursor: grab/grabbing` is desktop-only signal; `touch-action` is what makes touch work. A comfortable `min-height` makes the header a forgiving touch target.
|
|
*/
|
|
.right-dock-expand-modal__header--draggable {
|
|
border-bottom-color: var(--right-dock-expand-header-divider-color, transparent);
|
|
cursor: grab;
|
|
user-select: none;
|
|
touch-action: none;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.right-dock-expand-modal__header--draggable:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.right-dock-expand-modal__header,
|
|
.right-dock-expand-modal__title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.right-dock-expand-modal__title {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/*
|
|
FNXC:RightDockFiles 2026-06-22-01:00:
|
|
The expand body is a flex host that must let its single rendered view stretch to the FULL body width and height,
|
|
not shrink to content. The child (e.g. .dock-files-view) gets flex:1 + min-width:0 so its CSS container query
|
|
measures the real pop-out width and can flip to the wide two-pane layout. Keep min-width:0/min-height:0 here so a
|
|
wide child cannot collapse the flex line.
|
|
*/
|
|
.right-dock-expand-modal__body {
|
|
display: flex;
|
|
flex: 1;
|
|
min-height: 0;
|
|
min-width: 0;
|
|
overflow: auto;
|
|
}
|
|
|
|
.right-dock-expand-modal__body > * {
|
|
flex: 1;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
min-block-size: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.right-dock {
|
|
display: none;
|
|
}
|
|
}
|