Files
fusion/packages/dashboard/app/hooks/useEmbeddedPresentation.ts
gsxdsm cc2753c8a2 fix(dashboard): resizers, overlaps, dock pop-out, dedup refactors, live tweaks
Resize fixes (root causes):
- List view: clamp ResizeObserver collapsed the pane to min when container measured 0; harden + rewrite drag to pointer events with teardown.
- Mailbox: split was a CSS grid whose auto track ignored the inline width; switch to flex (pane flex:0 0 auto) + pointer-event drag. List + mailbox panes narrower mins.

Layout fixes:
- Memory Working-Memory overlap: real cause was a cascade collision (MemoryView imports SettingsModal.css, whose .memory-editor-section flex:1 won, overflowing the fixed-height editor onto siblings). Scope MemoryView rules under .memory-working-tab + intrinsic height.
- Agent detail header no longer overlaps (flex-wrap; identity flex:1 1 auto).
- Git Manager dock tabs wrap so all sections are visible (no single-tab swipe).
- Terminal shortcuts+arrows on one line; Skills full width; Skills refresh + Add Goal button heights matched; mailbox divider matches chat divider; Todos in-view header removed.

Features:
- Right-dock pop-out is now a floating, draggable, smoothly resizable, non-blocking window (transparent overlay, interact behind it).
- Command Center Overview gains an always-visible 'AI Engine' panel with View Board / View Agents.
- Default load lands on board, never the Dashboard.

Refactors (behavior-preserving):
- Extract useBoardWorkflows hook (Board + Planning dedup).
- Shared useEmbeddedPresentation hook collapses the 7-way embedded copy-paste; add embedded-presentation test coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 04:18:35 -07:00

48 lines
2.8 KiB
TypeScript

/*
FNXC:EmbeddedPresentation 2026-06-22-12:00:
Seven modal components (ActivityLogModal, GitManagerModal, GitHubImportModal, ScheduledTasksModal, PlanningModeModal, SettingsModal, WorkflowNodeEditor) each independently grew the same "embedded vs modal" presentation switch for the right-dock / main-content-area redesign. Each derived `isEmbedded = presentation === "embedded"` locally and gated the same modal-only behaviors off it: mobile scroll lock, modal resize-persist, Escape-to-close, and overlay click-dismiss.
This hook collapses that copy-pasted pattern into one place. The returned booleans are the enabled-arg for the hooks/handlers the components already call (e.g. `useMobileScrollLock(open && scrollLockEnabled)`), so the gating stays a single boolean expression and the underlying hooks remain CALLED UNCONDITIONALLY (React hook rules) — only their enabled arg flips.
Embedded surfaces are persistent main-content destinations owned by the dock/router, so all four modal-only affordances are disabled when embedded; every flag is simply `!isEmbedded`. Modal presentation (the default) keeps every affordance on, byte-identical to the historical behavior.
*/
/** Presentation surface for a component that can render as a fixed dialog overlay or inline in the main content area. */
export type ModalPresentation = "modal" | "embedded";
/**
* Derived presentation flags shared by the embedded-capable modal components.
*
* - `isEmbedded` / `isModal` — the raw mode test.
* - `scrollLockEnabled` — gate for `useMobileScrollLock`; off when embedded (the host page owns scrolling).
* - `resizePersistEnabled` — gate for `useModalResizePersist`; off when embedded (the view fills its container).
* - `escapeEnabled` — gate for Escape-to-close handlers; off when embedded (the dock/router owns lifecycle).
* - `overlayDismissEnabled` — gate for backdrop click-to-dismiss; off when embedded (no overlay backdrop exists).
*/
export interface EmbeddedPresentation {
isEmbedded: boolean;
isModal: boolean;
scrollLockEnabled: boolean;
resizePersistEnabled: boolean;
escapeEnabled: boolean;
overlayDismissEnabled: boolean;
}
/**
* Resolve the shared embedded-presentation flags for a component.
*
* @param presentation - The component's `presentation` prop. Defaults to "modal" so callers that omit it keep full modal behavior.
*/
export function useEmbeddedPresentation(presentation: ModalPresentation = "modal"): EmbeddedPresentation {
const isEmbedded = presentation === "embedded";
// Every modal-only affordance is disabled in embedded mode; embedded surfaces are persistent and host-owned.
return {
isEmbedded,
isModal: !isEmbedded,
scrollLockEnabled: !isEmbedded,
resizePersistEnabled: !isEmbedded,
escapeEnabled: !isEmbedded,
overlayDismissEnabled: !isEmbedded,
};
}