feat(dashboard): shared bring-to-front z-stack for all floating modals; fix TaskChatTab tests

- Floating modals (FloatingWindow, right-dock pop-out, terminal, New Task) now share one z-index stack (floatingWindowStack) — tapping any one raises it above all others regardless of type. Floating overlays reset to z-index:auto so panels interleave in the shared 4000+ band.
- TaskChatTab tests: the test i18n now resolves the entryCount/toolCallCount plural forms (production already had them) so '2 entries'/'7 tool calls' render; fixed stale 'Tool call → result/error' casing to match the rendered 'Result'/'Error'. All 179 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-22 05:33:10 -07:00
parent 590e064e37
commit 19be91c9f8
12 changed files with 163 additions and 16 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Floating modals (the reusable FloatingWindow, the right-dock pop-out, the floating terminal, and the floating New Task dialog) now share a single z-index stack, so tapping any of them brings it to the front above all the others regardless of type.

View File

@@ -9,6 +9,7 @@ import {
} from "react"; } from "react";
import { createPortal } from "react-dom"; import { createPortal } from "react-dom";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { nextFloatingZ, currentFloatingZ } from "./floatingWindowStack";
import "./FloatingWindow.css"; import "./FloatingWindow.css";
/* /*
@@ -46,13 +47,9 @@ const DEFAULT_MIN_HEIGHT = 280;
const VIEWPORT_PADDING = 16; const VIEWPORT_PADDING = 16;
/* /*
FNXC:FloatingWindow 2026-06-22-20:45: FNXC:FloatingWindow 2026-06-22-21:30:
Base z-index band sits at 4000+, above ordinary page content and interoperable with the existing terminal/right-dock pop-out band. `nextZ()` bumps the shared counter so a freshly mounted or freshly clicked window comes to the front. The counter is module-level and intentionally monotonic — it only ever climbs, which is fine for a session-length dashboard. Z-index now comes from the SHARED `floatingWindowStack` module (`nextFloatingZ`/`currentFloatingZ`) so FloatingWindow stacks in ONE counter with the right-dock pop-out, the floating terminal, and the floating New Task dialog — tapping ANY of them raises it above all the others regardless of type. The local `topZ`/`nextZ` counter this file previously owned is gone.
*/ */
let topZ = 4000;
function nextZ(): number {
return ++topZ;
}
type ResizeDirection = "n" | "s" | "e" | "w" | "ne" | "nw" | "se" | "sw"; type ResizeDirection = "n" | "s" | "e" | "w" | "ne" | "nw" | "se" | "sw";
const RESIZE_DIRECTIONS: ResizeDirection[] = ["n", "s", "e", "w", "ne", "nw", "se", "sw"]; const RESIZE_DIRECTIONS: ResizeDirection[] = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
@@ -113,8 +110,8 @@ export function FloatingWindow({
const initialSize = clampSize(defaultSize ?? { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT }, resolvedMinSize); const initialSize = clampSize(defaultSize ?? { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT }, resolvedMinSize);
return defaultPosition ? clampPosition(defaultPosition, initialSize) : defaultPositionFor(windowKey, initialSize); return defaultPosition ? clampPosition(defaultPosition, initialSize) : defaultPositionFor(windowKey, initialSize);
}); });
// FNXC:FloatingWindow 2026-06-22-20:45: Each window owns its z-index; mounting claims the front of the stack. // FNXC:FloatingWindow 2026-06-22-21:30: Each window owns its z-index; mounting claims the front of the SHARED cross-type stack.
const [zIndex, setZIndex] = useState<number>(() => nextZ()); const [zIndex, setZIndex] = useState<number>(() => nextFloatingZ());
/* /*
FNXC:FloatingWindow 2026-06-22-20:45: FNXC:FloatingWindow 2026-06-22-20:45:
@@ -122,12 +119,12 @@ export function FloatingWindow({
*/ */
const dragTeardownRef = useRef<(() => void) | null>(null); const dragTeardownRef = useRef<(() => void) | null>(null);
// FNXC:FloatingWindow 2026-06-22-20:45: Focus-to-front. Pointerdown/focus anywhere on the panel raises this window above the rest. // FNXC:FloatingWindow 2026-06-22-21:30: Focus-to-front. Pointerdown/focus anywhere on the panel raises this window above ALL other floating modals (any type) via the shared stack.
const bringToFront = useCallback(() => { const bringToFront = useCallback(() => {
setZIndex((current) => { setZIndex((current) => {
// Only claim a new z if we are not already on top, to avoid needless counter churn on every move. // Only claim a new z if we are not already on top, to avoid needless counter churn on every move.
if (current > topZ) return current; if (current >= currentFloatingZ()) return current;
return nextZ(); return nextFloatingZ();
}); });
}, []); }, []);

View File

@@ -16,6 +16,14 @@ The New Task dialog is a FLOATING, DRAGGABLE, RESIZABLE, NON-BLOCKING window (mi
pointer-events: none; pointer-events: none;
} }
/*
FNXC:FloatingWindow 2026-06-22-21:30:
Only the desktop FLOATING New Task dialog joins the shared cross-type floating stack. When the overlay hosts the floating panel, reset the base `.modal-overlay` z-index:100 to auto so it does NOT establish a stacking context; the panel's inline z-index (from floatingWindowStack, 4000+) then interleaves at the root with the terminal, the right-dock pop-out, and FloatingWindow. The mobile full-screen sheet (no `--floating` panel) keeps the base overlay z-index:100 so it still paints above page content.
*/
.modal-overlay.new-task-modal-overlay:has(.new-task-modal--floating) {
z-index: auto;
}
/* /*
FNXC:NewTask 2026-06-22-20:30: FNXC:NewTask 2026-06-22-20:30:
Floating panel positioned by state-driven inline left/top/width/height. min/max keep content usable and the panel on-screen; `resize: none` because the corner/edge handles own resizing (the native grip conflicts with the pointer handlers). `pointer-events: auto` re-enables interaction on the panel only. Desktop only — mobile keeps the full-screen keyboard-aware sheet. Floating panel positioned by state-driven inline left/top/width/height. min/max keep content usable and the panel on-screen; `resize: none` because the corner/edge handles own resizing (the native grip conflicts with the pointer handlers). `pointer-events: auto` re-enables interaction on the panel only. Desktop only — mobile keeps the full-screen keyboard-aware sheet.

View File

@@ -17,6 +17,7 @@ import { useMobileScrollLock } from "../hooks/useMobileScrollLock";
import { useNodes } from "../hooks/useNodes"; import { useNodes } from "../hooks/useNodes";
import { useViewportMode } from "../hooks/useViewportMode"; import { useViewportMode } from "../hooks/useViewportMode";
import { useAgentsMapCache } from "../hooks/useAgentsMapCache"; import { useAgentsMapCache } from "../hooks/useAgentsMapCache";
import { nextFloatingZ, currentFloatingZ } from "./floatingWindowStack";
interface NewTaskModalProps { interface NewTaskModalProps {
isOpen: boolean; isOpen: boolean;
@@ -147,6 +148,11 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
const [size, setSizeState] = useState<FloatSize>(() => readFloatSize()); const [size, setSizeState] = useState<FloatSize>(() => readFloatSize());
const [position, setPositionState] = useState<FloatPosition>(() => readFloatPosition(readFloatSize())); const [position, setPositionState] = useState<FloatPosition>(() => readFloatPosition(readFloatSize()));
const dragTeardownRef = useRef<(() => void) | null>(null); const dragTeardownRef = useRef<(() => void) | null>(null);
// FNXC:FloatingWindow 2026-06-22-21:30: Floating (desktop) New Task dialog shares the SINGLE cross-type floating z-index stack (floatingWindowStack). Mounting claims the front; tapping the panel (pointerdown/focus capture) raises it above every other floating modal regardless of type. Mobile keeps the full-screen sheet so this z-index is harmless there.
const [zIndex, setZIndex] = useState<number>(() => nextFloatingZ());
const bringToFront = useCallback(() => {
setZIndex((current) => (current >= currentFloatingZ() ? current : nextFloatingZ()));
}, []);
const persistSize = useCallback((next: FloatSize) => { const persistSize = useCallback((next: FloatSize) => {
setSizeState(writeFloatSize(next)); setSizeState(writeFloatSize(next));
@@ -737,7 +743,7 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
// FNXC:NewTask 2026-06-22-20:30: Desktop = floating fixed panel positioned by state-driven left/top/width/height. Mobile keeps the keyboard-aware full-screen sheet (no floating). The transparent click-through overlay never dismisses on click; the header X / Cancel / Escape are the only dismissals. // FNXC:NewTask 2026-06-22-20:30: Desktop = floating fixed panel positioned by state-driven left/top/width/height. Mobile keeps the keyboard-aware full-screen sheet (no floating). The transparent click-through overlay never dismisses on click; the header X / Cancel / Escape are the only dismissals.
const panelStyle: CSSProperties = isFloating const panelStyle: CSSProperties = isFloating
? { left: `${position.x}px`, top: `${position.y}px`, width: `${size.width}px`, height: `${size.height}px` } ? { left: `${position.x}px`, top: `${position.y}px`, width: `${size.width}px`, height: `${size.height}px`, zIndex }
: keyboardStyle; : keyboardStyle;
return ( return (
@@ -752,6 +758,8 @@ export function NewTaskModal({ isOpen, onClose, projectId, tasks, onCreateTask,
<div <div
className={`modal modal-lg new-task-modal${isFloating ? " new-task-modal--floating" : ""}`} className={`modal modal-lg new-task-modal${isFloating ? " new-task-modal--floating" : ""}`}
style={panelStyle} style={panelStyle}
onPointerDownCapture={isFloating ? bringToFront : undefined}
onFocusCapture={isFloating ? bringToFront : undefined}
> >
{isFloating && NEW_TASK_RESIZE_DIRECTIONS.map((direction) => ( {isFloating && NEW_TASK_RESIZE_DIRECTIONS.map((direction) => (
<div <div

View File

@@ -170,6 +170,11 @@ The right-dock pop-out is a FLOATING, DRAGGABLE, RESIZABLE, NON-BLOCKING window.
background: transparent; background: transparent;
backdrop-filter: none; backdrop-filter: none;
pointer-events: none; pointer-events: none;
/*
FNXC:FloatingWindow 2026-06-22-21:30:
Reset the base `.modal-overlay` z-index:100 to auto so this click-through overlay does NOT establish a stacking context. The floating panel carries an inline z-index from the SHARED floatingWindowStack (4000+); without this reset that inline z would be clamped inside the overlay's own stacking context and could never interleave with the other floating modal types (terminal, New Task, FloatingWindow) that all draw from the same stack.
*/
z-index: auto;
} }
.right-dock-expand-modal { .right-dock-expand-modal {

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type RefObject } from "react"; import { useCallback, useEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent, type RefObject } from "react";
import { Maximize2, X } from "lucide-react"; import { Maximize2, X } from "lucide-react";
import { findOverflowViewEntry, type OverflowViewEntry, type OverflowViewKey, type OverflowViewRenderProps, type OverflowViewVisibilityOptions } from "./overflowViewRegistry"; import { findOverflowViewEntry, type OverflowViewEntry, type OverflowViewKey, type OverflowViewRenderProps, type OverflowViewVisibilityOptions } from "./overflowViewRegistry";
import { nextFloatingZ, currentFloatingZ } from "./floatingWindowStack";
import "./RightDock.css"; import "./RightDock.css";
const RIGHT_DOCK_EXPAND_MODAL_SIZE_STORAGE_KEY = "fusion:right-dock-expand-modal-size"; const RIGHT_DOCK_EXPAND_MODAL_SIZE_STORAGE_KEY = "fusion:right-dock-expand-modal-size";
@@ -124,6 +125,11 @@ export function RightDockExpandModal({
const [size, setSizeState] = useState<ExpandSize>(() => readExpandSize()); const [size, setSizeState] = useState<ExpandSize>(() => readExpandSize());
const [position, setPositionState] = useState<ExpandPosition>(() => readExpandPosition(readExpandSize())); const [position, setPositionState] = useState<ExpandPosition>(() => readExpandPosition(readExpandSize()));
// FNXC:FloatingWindow 2026-06-22-21:30: The right-dock pop-out shares the SINGLE cross-type floating z-index stack (floatingWindowStack). Mounting claims the front; tapping the panel (pointerdown/focus capture) raises it above every other floating modal regardless of type.
const [zIndex, setZIndex] = useState<number>(() => nextFloatingZ());
const bringToFront = useCallback(() => {
setZIndex((current) => (current >= currentFloatingZ() ? current : nextFloatingZ()));
}, []);
/* /*
FNXC:RightDock 2026-06-22-17:40: FNXC:RightDock 2026-06-22-17:40:
@@ -294,11 +300,17 @@ export function RightDockExpandModal({
top: `${position.y}px`, top: `${position.y}px`,
width: `${size.width}px`, width: `${size.width}px`,
height: `${size.height}px`, height: `${size.height}px`,
zIndex,
} as CSSProperties; } as CSSProperties;
return ( return (
<div className="modal-overlay open right-dock-expand-modal-overlay" role="dialog" aria-modal="false" aria-label={`${entry.label} expanded`} data-testid="right-dock-expand-modal"> <div className="modal-overlay open right-dock-expand-modal-overlay" role="dialog" aria-modal="false" aria-label={`${entry.label} expanded`} data-testid="right-dock-expand-modal">
<div className="modal right-dock-expand-modal right-dock-expand-modal--floating" style={panelStyle}> <div
className="modal right-dock-expand-modal right-dock-expand-modal--floating"
style={panelStyle}
onPointerDownCapture={bringToFront}
onFocusCapture={bringToFront}
>
{EXPAND_RESIZE_DIRECTIONS.map((direction) => ( {EXPAND_RESIZE_DIRECTIONS.map((direction) => (
<div <div
key={direction} key={direction}

View File

@@ -44,6 +44,14 @@ The override MUST out-specify the base `.modal-overlay` (which sets a dimmed bac
pointer-events: none; pointer-events: none;
} }
/*
FNXC:FloatingWindow 2026-06-22-21:30:
Only the FLOATING terminal joins the shared cross-type floating stack. Reset the base `.modal-overlay` z-index:100 to auto so this click-through overlay does NOT establish a stacking context; the floating panel's inline z-index (from floatingWindowStack, 4000+) then interleaves at the root with the right-dock pop-out, the floating New Task dialog, and FloatingWindow. Docked mode keeps the base overlay stacking (full-width bottom panel) and is intentionally excluded.
*/
.modal-overlay.terminal-modal-overlay--floating {
z-index: auto;
}
.modal.terminal-modal { .modal.terminal-modal {
/* Initial dimensions are applied only when no persisted size has been /* Initial dimensions are applied only when no persisted size has been
restored — see :not([style*=...]) selectors below. */ restored — see :not([style*=...]) selectors below. */

View File

@@ -25,6 +25,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { useTerminal } from "../hooks/useTerminal"; import { useTerminal } from "../hooks/useTerminal";
import { useTerminalSessions } from "../hooks/useTerminalSessions"; import { useTerminalSessions } from "../hooks/useTerminalSessions";
import { nextFloatingZ, currentFloatingZ } from "./floatingWindowStack";
import { getPathBasename } from "../utils/pathDisplay"; import { getPathBasename } from "../utils/pathDisplay";
import { import {
DEFAULT_TERMINAL_PREFERENCES, DEFAULT_TERMINAL_PREFERENCES,
@@ -405,6 +406,12 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
const [isMobileTerminal, setIsMobileTerminal] = useState(() => isTerminalMobileViewport()); const [isMobileTerminal, setIsMobileTerminal] = useState(() => isTerminalMobileViewport());
const isDockedMode = !isMobileTerminal && displayMode === "docked"; const isDockedMode = !isMobileTerminal && displayMode === "docked";
const isFloatingMode = !isMobileTerminal && displayMode === "floating"; const isFloatingMode = !isMobileTerminal && displayMode === "floating";
// FNXC:FloatingWindow 2026-06-22-21:30: The FLOATING terminal shares the SINGLE cross-type floating z-index stack (floatingWindowStack) so tapping it raises it above every other floating modal regardless of type. A fresh z is claimed each time the modal opens (see effect below); tapping the panel (pointerdown/focus capture) re-raises it. Docked/mobile modes ignore this z-index (full-width bottom panel / full-screen sheet).
const [floatingZ, setFloatingZ] = useState<number>(() => nextFloatingZ());
const bringFloatingToFront = useCallback(() => {
if (!isFloatingMode) return;
setFloatingZ((current) => (current >= currentFloatingZ() ? current : nextFloatingZ()));
}, [isFloatingMode]);
const terminalRef = useRef<HTMLDivElement>(null); const terminalRef = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(null); const modalRef = useRef<HTMLDivElement>(null);
@@ -711,9 +718,11 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
// Bump open generation whenever the modal opens so the initialCommand // Bump open generation whenever the modal opens so the initialCommand
// effect re-evaluates after a close/reopen cycle (deps may be identical). // effect re-evaluates after a close/reopen cycle (deps may be identical).
// FNXC:FloatingWindow 2026-06-22-21:30: Each open also claims the front of the shared floating-window stack so a freshly-opened floating terminal sits above other floating modals.
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
setOpenGeneration((g) => g + 1); setOpenGeneration((g) => g + 1);
setFloatingZ(nextFloatingZ());
} }
}, [isOpen]); }, [isOpen]);
@@ -1758,6 +1767,8 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
"--terminal-float-y": `${floatingPosition.y}px`, "--terminal-float-y": `${floatingPosition.y}px`,
"--terminal-float-width": `${floatingSize.width}px`, "--terminal-float-width": `${floatingSize.width}px`,
"--terminal-float-height": `${floatingSize.height}px`, "--terminal-float-height": `${floatingSize.height}px`,
// FNXC:FloatingWindow 2026-06-22-21:30: Inline z from the shared cross-type stack; only the floating panel participates.
zIndex: floatingZ,
} }
: {}), : {}),
} as CSSProperties; } as CSSProperties;
@@ -1783,6 +1794,8 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
className={modalClassName} className={modalClassName}
data-testid="terminal-modal" data-testid="terminal-modal"
style={modalStyle} style={modalStyle}
onPointerDownCapture={isFloatingMode ? bringFloatingToFront : undefined}
onFocusCapture={isFloatingMode ? bringFloatingToFront : undefined}
> >
{isDockedMode && ( {isDockedMode && (
<div <div

View File

@@ -0,0 +1,52 @@
import { render, screen, fireEvent } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { FloatingWindow } from "../FloatingWindow";
import { RightDockExpandModal } from "../RightDockExpandModal";
import { nextFloatingZ, currentFloatingZ } from "../floatingWindowStack";
/*
FNXC:FloatingWindow 2026-06-22-21:30:
Cross-type shared-stack contract. Every floating modal type (FloatingWindow, the right-dock pop-out, the floating terminal, the floating New Task dialog) must draw its z-index from the SINGLE module-level `floatingWindowStack` counter so tapping ANY of them raises it above ALL the others REGARDLESS of type. Before this, each type owned a private counter and tapping the terminal could not raise it above a popped-out FloatingWindow. This suite proves two different component types interleave in one monotonic stack and that tapping the older one raises it above the newer one across the type boundary. RightDockExpandModal stands in for the three non-FloatingWindow floating modals (terminal + New Task wire the identical claim-on-mount + bring-to-front-on-pointerdown pattern; they are heavier to mount in JSDOM and assert the same inline-zIndex contract).
*/
const renderProps = { addToast: () => {}, projectId: "project-1" } as const;
describe("floatingWindowStack (cross-type)", () => {
it("hands out a strictly increasing, shared z to every claimant", () => {
const a = nextFloatingZ();
const b = nextFloatingZ();
expect(b).toBeGreaterThan(a);
expect(currentFloatingZ()).toBe(b);
});
it("tapping a FloatingWindow raises it above a right-dock pop-out opened after it (and vice versa)", () => {
render(
<>
<FloatingWindow windowKey="fw" title="FW" onClose={() => {}}>
<div>fw body</div>
</FloatingWindow>
<RightDockExpandModal viewKey="files" renderProps={renderProps} onClose={() => {}} />
</>,
);
const fwPanel = screen.getByTestId("floating-window-fw");
const dockPanel = screen
.getByTestId("right-dock-expand-modal")
.querySelector(".right-dock-expand-modal--floating") as HTMLElement;
// Both carry an inline z-index from the shared stack.
expect(fwPanel.style.zIndex).not.toBe("");
expect(dockPanel.style.zIndex).not.toBe("");
// The dock pop-out mounted last → it starts on top of the FloatingWindow, proving one shared stack.
expect(Number(dockPanel.style.zIndex)).toBeGreaterThan(Number(fwPanel.style.zIndex));
// Tapping the older FloatingWindow raises it above the dock pop-out — across the type boundary.
fireEvent.pointerDown(fwPanel);
expect(Number(fwPanel.style.zIndex)).toBeGreaterThan(Number(dockPanel.style.zIndex));
// Tapping the dock pop-out raises it back above the FloatingWindow.
fireEvent.pointerDown(dockPanel);
expect(Number(dockPanel.style.zIndex)).toBeGreaterThan(Number(fwPanel.style.zIndex));
});
});

View File

@@ -670,7 +670,7 @@ describe("TaskChatTab", () => {
expect(toolGroup).toHaveAttribute("open"); expect(toolGroup).toHaveAttribute("open");
const invocation = screen.getByTestId("task-chat-tool-invocation"); const invocation = screen.getByTestId("task-chat-tool-invocation");
const kicker = screen.getByText("Tool call → result"); const kicker = screen.getByText("Tool call → Result");
expect(invocation).toHaveClass("task-chat-tool-entry", "task-chat-tool-invocation"); expect(invocation).toHaveClass("task-chat-tool-entry", "task-chat-tool-invocation");
expect(kicker).toHaveClass("task-chat-entry-kicker"); expect(kicker).toHaveClass("task-chat-entry-kicker");
expect(kicker).toBeVisible(); expect(kicker).toBeVisible();
@@ -727,7 +727,7 @@ describe("TaskChatTab", () => {
await user.click(within(summary as HTMLElement).getByText("1 tool call")); await user.click(within(summary as HTMLElement).getByText("1 tool call"));
expect(screen.getByText("Tool call → error")).toBeVisible(); expect(screen.getByText("Tool call → Error")).toBeVisible();
expect(screen.getByText("Error")).toBeVisible(); expect(screen.getByText("Error")).toBeVisible();
expect(screen.getByText("stderr")).toBeVisible(); expect(screen.getByText("stderr")).toBeVisible();
}); });

View File

@@ -0,0 +1,17 @@
/*
FNXC:FloatingWindow 2026-06-22-21:30:
SHARED floating-window z-index stack. This is the ONE source of z-index for every floating modal in the dashboard (FloatingWindow, 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. Previously each modal type managed z-index independently, so tapping e.g. the terminal could not raise it above a popped-out task-detail FloatingWindow. Now every floating modal claims `nextFloatingZ()` on mount/open and again on every panel pointerdown/focus, so the most-recently-interacted window is always on top REGARDLESS of type.
Base band sits at 4000+ — above ordinary page content and above the base `.modal-overlay` (z-index 100). 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.
*/
let topZ = 4000;
/** Claim the front of the shared floating-window stack. Monotonic, session-length. */
export function nextFloatingZ(): number {
return ++topZ;
}
/** Current top of the stack (read-only). Lets a window skip a needless bump when already on top. */
export function currentFloatingZ(): number {
return topZ;
}

View File

@@ -16,7 +16,29 @@ await i18next.use(initReactI18next).init({
// Each namespace present (empty) so hasLoadedNamespace() is true — an // Each namespace present (empty) so hasLoadedNamespace() is true — an
// unloaded namespace makes useTranslation() suspend (no Suspense boundary // unloaded namespace makes useTranslation() suspend (no Suspense boundary
// in component tests) even with useSuspense disabled belt-and-braces below. // in component tests) even with useSuspense disabled belt-and-braces below.
resources: { en: { common: {}, app: {}, errors: {} } }, //
// FNXC:TestI18n 2026-06-22-21:40:
// Pluralized count keys must resolve from resources, not the singular inline
// default. t("taskChat.entryCount", "{{count}} entry", { count }) renders the
// singular default for ALL counts when the key is absent — so count=2 became
// "2 entry". Provide the _one/_other forms (as the real en locale does) so the
// correct plural ("2 entries", "7 tool calls") renders in tests too. Only these
// keys resolve from the bundle; every other key still falls back to its inline
// default, preserving existing assertions.
resources: {
en: {
common: {},
app: {
taskChat: {
entryCount_one: "{{count}} entry",
entryCount_other: "{{count}} entries",
toolCallCount_one: "{{count}} tool call",
toolCallCount_other: "{{count}} tool calls",
},
},
errors: {},
},
},
ns: ["common", "app", "errors"], ns: ["common", "app", "errors"],
defaultNS: "common", defaultNS: "common",
interpolation: { escapeValue: false }, interpolation: { escapeValue: false },