## Summary Gives dashboard integrators (plugin views, embedded panels, theming tools) a supported way to match the dashboard's look and to layer overlay UI correctly — instead of scraping computed styles and guessing z-index values. This implements the CSS-token bridge slice of `docs/proposals/2026-07-01-dashboard-theme-plugin-system.md`. Two additions, both inert unless used: 1. **Documented theme-token contract.** A "Theme tokens" section in `docs/dashboard-guide.md` (referenced from `docs/PLUGIN_AUTHORING.md`) declares the stable set of CSS custom properties — colors, surfaces, status colors — that integrators may read. Tokens resolve to raw color strings (e.g. `#161b22`) in every theme, including the newer ones. A sync test (`theme-token-contract-docs.test.ts`) parses the doc's token table and asserts each documented token has a real definition in the dashboard CSS, so the contract cannot silently drift from the code. 2. **Overlay layering surface.** Overlay-style UI (palettes, pickers, floating panels) currently has no supported way to sit above the floating-window stack — the effective max z-index is runtime state inside `floatingWindowStack.ts`. This PR exposes it: - `--fusion-max-z` on `:root` — kept in sync by `floatingWindowStack` (written at module load and after every `nextFloatingZ()` claim), so it always reflects the true top of the dashboard-managed stack. Boot/floor value is `11001`, chosen to clear the highest statically-declared layer (the body-portaled model-combobox dropdown at `z-index: 11000`). - `#plugin-overlay-root` — an empty, `pointer-events: none` sibling of `#root` stacked at `calc(var(--fusion-max-z) + 1)`. React never renders into it, so it is hydration-safe; integrators portal into it and re-enable pointer events on their own elements. - The layer bands (base UI / floating windows / toasts / dropdown / overlay root) are documented in `styles.css` and the guide, and a guard test (`dashboard-max-z-guard.test.ts`) scans the structural + component CSS and fails if any static `z-index` is ever introduced above the floor — keeping the contract honest as the codebase evolves. ## Behavior No visual or behavioral change for existing users: `floatingWindowStack` still returns the same values from `nextFloatingZ()`; the overlay root is empty and click-through; tokens were already defined — this only documents and guards them. ## Tests - `theme-token-contract-docs.test.ts` — docs ↔ CSS sync (non-tautological: anchored matching against real definitions). - `floatingWindowStack.max-z.test.ts` — `--fusion-max-z` boot value and live tracking as the stack claims z-indexes. - `dashboard-max-z-guard.test.ts` — no static dashboard z-index above the floor (decorative `public/theme-data.css` INT_MAX scanline overlay deliberately excluded; it's non-interactive grain, documented in the test). - Changeset included (`minor`, `category: feature`). Typecheck clean. ## Open question for maintainers The token is named `--fusion-max-z`. The existing scale uses `--z-*` names (`--z-dropdown`, `--z-modal`) on a lower band — happy to rename to `--z-max` / `--z-plugin-overlay` or anything that fits your convention; the name is the only bikeshed here, the sync mechanism is independent of it. ## AI assistance disclosure Parts of this change were authored with AI assistance (Anthropic's Claude); the commit carries a `Co-authored-by` trailer accordingly. Everything was human-reviewed before submission, and the test suite and typecheck were run locally against the current `main`. If squash-merging with a rewritten message, please keep the attribution: ``` Co-authored-by: Claude <noreply@anthropic.com> ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added stable dashboard theme tokens for consistent plugin/integration styling. - Introduced a dedicated plugin overlay mount point with click-through defaults and an overlay stacking ceiling. - Overlay z-index now stays in sync with floating window layering automatically. - **Documentation** - Added an explicit stable “theme token contract” and “overlay layering contract,” including interaction and z-index usage rules and deprecation expectations. - **Bug Fixes** - Improved reliability of plugin overlay stacking so overlay content renders above intended dashboard layers. - **Tests** - Added guards validating CSS z-index ceilings and enforcing the documented theme token contract. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude <noreply@anthropic.com>
62 lines
3.9 KiB
TypeScript
62 lines
3.9 KiB
TypeScript
/*
|
|
FNXC:FloatingWindow 2026-06-22-21:30:
|
|
SHARED floating-utility z-index stack. This is the ONE source of z-index for utility floating modals in the dashboard (FloatingWindow utility callers, the right-dock pop-out, the floating terminal, the floating New Task dialog) so they interoperate in a SINGLE stack instead of each type owning a private counter. Utility windows claim `nextFloatingZ()` on mount/open and again on every panel pointerdown/focus, so the most-recently-interacted utility window is always on top REGARDLESS of type.
|
|
|
|
FNXC:FloatingWindow 2026-06-22-22:30:
|
|
Base band sits at 10100+ — ABOVE the page overlay/popover band (log viewer, workflow-editor modal, selection popover, static fullscreen fallbacks at z 10000-10001) so a utility floating window the user is dragging is never painted over by those. Transient top-right toasts are bumped to 10500 (styles.css) so system feedback still shows above a dragged utility window. The workflow prompt fullscreen overlay is itself a floating utility surface and claims `nextFloatingZ()` when opened, because a static z 10000 fallback is hidden by the workflow editor's full-screen mobile FloatingWindow sheet. The counter is module-level and intentionally monotonic: it only ever climbs, which is fine for a session-length dashboard. All floating overlays are `pointer-events: none` (click-through) so raising panels into this shared band never traps clicks on the page behind them. CRITICAL: every floating modal must be portaled to document.body so this shared z is compared in ONE root stacking context (an inline panel cannot beat siblings outside its own context no matter its z).
|
|
|
|
FNXC:TaskPopupLayer 2026-07-17-15:55:
|
|
Task-detail popups and Quick Chat are interaction-stack peers in this lower board-layer band: the
|
|
most recently mounted or pointer/focus-interacted peer is on top. Terminal, right-dock expand,
|
|
Files, New Task, and other utility surfaces continue to use the separate 10100+ utility band.
|
|
|
|
FNXC:PluginOverlayLayering 2026-07-23-01:21:
|
|
Plugins need a stable layer above every dashboard-managed utility window even though this stack is
|
|
session-monotonic and unbounded. Keep `--fusion-max-z` at the 11001 boot floor until this utility
|
|
counter exceeds it, then raise the inline root value after each claim. The floor sits one above the
|
|
tallest static dashboard overlay — the body-portaled model-combobox dropdown at 11000 — so it
|
|
dominates every fixed layer. The lower 220+ task-detail band is intentionally excluded; only
|
|
utility claims can grow past the dashboard's static layers.
|
|
*/
|
|
/** Boot value for `--fusion-max-z`: one above the tallest static dashboard layer (the body-portaled model-combobox dropdown at 11000). */
|
|
export const FUSION_MAX_Z_FLOOR = 11001;
|
|
|
|
let topZ = 10100;
|
|
let taskDetailTopZ = 220;
|
|
let lastSyncedFusionMaxZ: number | undefined;
|
|
|
|
/** Publish the current dashboard-managed z-index ceiling to `--fusion-max-z` on `:root`, skipping redundant writes. No-op outside a DOM. */
|
|
function syncFusionMaxZ(): void {
|
|
if (typeof document === "undefined") return;
|
|
|
|
const value = Math.max(topZ, FUSION_MAX_Z_FLOOR);
|
|
if (value === lastSyncedFusionMaxZ) return;
|
|
|
|
document.documentElement.style.setProperty("--fusion-max-z", String(value));
|
|
lastSyncedFusionMaxZ = value;
|
|
}
|
|
|
|
syncFusionMaxZ();
|
|
|
|
/** Claim the front of the shared floating-utility stack. Monotonic, session-length. */
|
|
export function nextFloatingZ(): number {
|
|
const nextZ = ++topZ;
|
|
syncFusionMaxZ();
|
|
return nextZ;
|
|
}
|
|
|
|
/** Current top of the floating-utility stack (read-only). Lets a utility window skip a needless bump when already on top. */
|
|
export function currentFloatingZ(): number {
|
|
return topZ;
|
|
}
|
|
|
|
/** Claim the front of the task-popup peer stack (task details and Quick Chat). Monotonic, session-length. */
|
|
export function nextTaskDetailFloatingZ(): number {
|
|
return ++taskDetailTopZ;
|
|
}
|
|
|
|
/** Current top of the task-popup peer stack (read-only). */
|
|
export function currentTaskDetailFloatingZ(): number {
|
|
return taskDetailTopZ;
|
|
}
|