mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-28 01:16:21 +00:00
Narrowing a window moved the panels back and forth: at 800 px the right panel closed while the left one jumped back from 220 to 340 px, then shrank a second time; at 600 px the left panel closed and the right one reappeared, only to close again at 580 px. Widening repeated the sequence in reverse with its own swaps. Narrowing now shrinks the conversation to its minimum, then the right panel to its minimum, where it hides, then the left panel to its minimum, where it hides. Each panel shrinks once and hides at the end of its own compression, so the other panel never changes width at that moment and only the conversation takes the room; widening retraces exactly the same layouts. Each side panel keeps whether the user wants it (opened, or closed while docked) and docks while the window has its room, in a fixed order: the session panel first, the right panel only beside the full session panel. A wanted panel without room hides and returns with the room, so the layout follows the window width alone; a minimizing window's tiny viewport hides both panels and restoring it brings them back. A panel opened without room floats over the conversation; dismissing it keeps the docked choice, and it docks once it has room. A panel closed while docked stays closed. Dismissing a floating panel by clicking outside leaves focus on the clicked control; a panel the layout hides hands focus to its toggle when it held focus. Touch tablets remember the wanted panels per orientation. A floating panel never docks while the window already has its new shape but the orientation has not changed yet, so rotation memory records only the user's choices; the shape is read from the orientation media query, which stays reliable on zoomed, near-square windows. Implementation: `resolveEmbeddedDrawers` docks the right panel only beside the left one at its chosen width and caps each panel's chosen width by the room. `useDrawerChrome` replaces the open state, the "awaiting room" marks and their effects with a wanted and a floating signal per panel; docked and shown states derive from them and the window width. The public API keeps its shape (`leftOpen` means shown; `setLeftOpen(true)` opens docked or floating, `setLeftOpen(false)` dismisses silently). Validation: a shell-navigation sweep narrows 1400 to 400 px in 20 px steps, requiring panels never to grow back or reappear, then widens and requires the same layout at every width. Regressions cover outside dismissal focus, rotation with a floating panel, a wanted panel docked by restored room across rotations, and rounded near-square viewports. drawer-layout unit tests cover each panel hiding at the end of its compression. Every browser file that drives the panels passes; the user validated the behaviour in a separate Electron dev instance; four gatekeeper rounds ended with no P0-P2 finding. Help is updated in all ten languages.
19 lines
9.5 KiB
Markdown
19 lines
9.5 KiB
Markdown
# Application shell
|
|
|
|
Panel toggles, floating drawers, project/right-panel tabs, the session sidebar and native application menus.
|
|
|
|
## Agent conventions
|
|
|
|
- Session rows keep actions inline until their measured title, badges, and controls no longer fit. Keep responsive action styles in `styles/components/session-row-actions.css`; hidden inline controls remain measurable but inert, and an open overflow menu stays mounted until dismissal.
|
|
- A subsession that starts working is revealed only below the session active in that instance (`revealWorkingSubsession`); other families stay collapsed. A collapsed parent's own expander chevron carries its descendants' most urgent visible state (input steady, working/compacting pulsing, idle steady green) with counted states in its label (`session-child-activity.ts`); idle follows the existing badge visibility, unseen-subagent preference and seen/fade lifecycle, and never outranks input or work. Never add a separate badge, and an expanded parent's chevron stays plain. Automatic reveals are transient: excluded from restorable expansion state until the user toggles or explicitly reveals the row, and a manual collapse dismisses the branch for subsessions running at that moment. Styles live in `styles/components/session-child-activity.css`.
|
|
- Conversation header actions (inline and in their overflow menu) offer the web preview on every platform, with Full screen added on mobile. Remote Control clients hide the preview entry while their host keeps web previews off: `/api/meta` then reports `previewsUnavailable` (`previewsUnavailable()` in `lib/server-meta.ts`). Validate with `tests/browser/header-windows.test.ts`.
|
|
- Session hierarchy geometry lives in `styles/components/session-tree.css`; connector axes follow the parent expander at every depth, including selection mode, RTL and touch layouts.
|
|
- Session search/filter mode uses flat per-session results with an optional subsession switch; filters, sorting, worktree badges and selection use each result's own identity. Normal browsing retains the session hierarchy.
|
|
- Project and right-panel tabs share `components/tab-scroll.tsx` and `styles/components/tab-scroll.css`. Keep their native scrollbar above upright content without mirrored transforms, negative border overlaps or permanent compositing hints. Validate shared scrollbar styling and adjoining edges at fractional zoom in the browser and isolated Electron renderer fixtures (`tests/browser/tab-chrome.test.ts`).
|
|
- Built-in right-panel tabs are constructed directly in `right-panel/core-runtime.tsx` (never add a first-party plugin loader for them); external sandboxed panels keep their independent consent/loading path.
|
|
- Embedded floating session drawers use `instance/shell/HostedDrawer.tsx` so their SUID modal roots are direct children of the existing geometry host. A Solid Portal wrapper must not become an ARIA-hidden ancestor of visible controls. Preserve SUID's modal stacking, sibling isolation, focus policy, Escape/backdrop handling and scroll restoration; validate with `tests/browser/drawer-accessibility.test.ts` rather than overriding ARIA or disabling modal enforcement.
|
|
- Floating-panel outside-pointer dismissal must leave ARIA menus, listboxes and dialogs to their own selection/dismissal handlers: their portals are outside the drawer DOM, and closing the drawer on pointerdown destroys a menu before touch pointerup selects its action. Keep this guard shared in the shell, not in individual menu actions; validate the real session Delete confirmation with `tests/browser/session-delete.test.ts`.
|
|
- Each side panel has exactly one open/close control: `instance/shell/ShellPanelToggle.tsx` (`PanelLeft`/`PanelRight`, `.icon-toggle` with `aria-expanded`/`aria-controls`), absolutely placed at the shell's outer header corners (`--space-xs` from the window edge) in `.session-shell-frame`, outside the drawer host so modal sibling isolation never hides it, and above floating drawers. The frame is an isolated stacking context: toggles stay above in-frame drawers but below body-portaled dialogs, scrims and utility windows. Both toggles precede the shell content in tab order and use the fixed View-menu labels (`window.menu.leftPanel`/`rightPanel`) with `aria-expanded`, never an Open/Close label flip. They are strictly fixed: no horizontal or vertical movement between docked, floating and closed states in any layout; the sidebar header, right-panel tab bar (both edges when floating) and the conversation header (`data-{left,right}-toggle-reserve`) reserve their footprint instead. Every header's action row (session list header row, right-panel `.tab-strip-shortcuts`, conversation side groups and status indicators, with or without the title row) and the toggles centre on one shared line, `--panel-header-action-center` in `styles/tokens.css`; never measure per-state offsets. The session list header holds a single action row; never add a keyboard-shortcut footer below the session list. Opening one floating drawer closes the other; the outside-pointer handler ignores the toggles. Styles live in `styles/panels/shell-panel-toggles.css`; validate with `tests/browser/shell-navigation.test.ts`.
|
|
- Each side panel keeps whether the user wants it (opened, or closed while docked) and docks while the window has its room, in a fixed order: the session panel first, the right panel only beside it (`instance/shell/useDrawerChrome.ts`, `drawer-layout.ts`). Narrowing shrinks the conversation to its minimum, then the right panel to its minimum, where it hides, then the left panel to its minimum, where it hides: each panel hides at the end of its own compression, so the other never changes width at that moment and only the conversation takes the room. A hidden wanted panel returns with the room, so the layout follows the window width alone, narrowing and widening alike (a minimizing window's tiny viewport included). A panel opened without room floats over the conversation; dismissing it keeps the docked choice, and it docks once it has room. On touch tablets each orientation remembers the wanted panels. A panel hidden by the layout hands focus to its toggle when it last held focus, even when the focused control already left the DOM. Choosing from the floating session panel's list or its Info entry closes that panel, the shown session included; Back/Forward keep it open. Validate with `tests/browser/shell-navigation.test.ts`, whose narrowing/widening sweep requires panels never to grow back or reappear while narrowing and widening to retrace the same layouts.
|
|
- The session sidebar header ends with Back/Forward (`ArrowLeft`/`ArrowRight`, mirrored in RTL; history wording "Back"/"Forward" with conversation tooltips, distinct from the palette's Previous/Next Session list cycling) over a browser-like in-memory history per project tab and window (`stores/session-navigation-history.ts`, `instance/shell/useSessionNavigationHistory.ts`, at most 50 entries). It records every displayed conversation whatever selected it; Info and the no-session draft are not entries (Back first returns to the cursor entry). Navigating moves the cursor and selects through the ordinary `handleSessionSelect` path without pushing; a new selection truncates the forward stack; unavailable/deleted sessions are skipped and determine the disabled states. Do not add keyboard shortcuts or persistence to this history.
|
|
- Native Electron/Tauri application menus follow the UI locale: renderers translate `NATIVE_MENU_LABEL_KEYS` (`lib/native/menu-labels.ts`) and send them with the View menu state. Hosts keep English until a renderer reports labels, keep the last locale for windows without one, and escape `&` outside macOS. Electron takes its defaults from `lib/native/menu-labels-en.ts`; Tauri's `menu_labels.rs` defaults are checked against the English messages by `menu-labels.test.ts`. Tauri retitles retained items in place; Electron rebuilds its menu on a label change. Local application and Preferences windows draw their own title bar (`components/native-titlebar.tsx`). On Windows and Linux they are frameless: the main window's title bar presents the menus, and both carry the window controls. On macOS the menus stay in the global menu bar and the hosts keep the native traffic lights at their standard place over the title bar's left end (Tauri overlay title bar in `title_bar.rs`, Electron `titleBarStyle: "hidden"`); the title bar takes the native 28 px title bar height so they stay centred, undoes the host's page zoom because the traffic lights never zoom (CSS `--host-page-zoom`/`--host-page-unzoom` on the document: Tauri's host publishes them after each load and zoom change through `title_bar::publish_page_zoom`, since WebKit hides the zoom from the page; Electron's renderer sets them from the preload's `getZoomFactor()`, re-read on resize), and holds the brand or title, the drag region and, in the main window, the app actions (Settings, notification history and Remote Access, `components/app-actions.tsx`), which the project tab bar holds on the other platforms and in browsers (`appActionsInTitlebar`). Never move the traffic lights through the hosts' position options: Tauri loses its position once the window is shown and resized. Tauri keeps the native menu bar of local application and Preferences windows attached for its accelerators but hidden through `menu_bar::hide` (`menu_bar.rs`), which on Linux also excludes the GTK bar from `show_all` (run when a window appears) and lets its accelerators fire while hidden. Remote windows keep their native decorations and menu bar. About CodeNomad runs the `open-about` command (Settings → Info), falling back to a native panel only when no application window can receive it.
|