Skip to content

[cueweb] Add view presets, immersive mode, and split-view workspaces#2449

Open
ramonfigueiredo wants to merge 4 commits into
AcademySoftwareFoundation:masterfrom
ramonfigueiredo:cueweb/workspace-layout-enhancements
Open

[cueweb] Add view presets, immersive mode, and split-view workspaces#2449
ramonfigueiredo wants to merge 4 commits into
AcademySoftwareFoundation:masterfrom
ramonfigueiredo:cueweb/workspace-layout-enhancements

Conversation

@ramonfigueiredo

@ramonfigueiredo ramonfigueiredo commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Related Issues

Summarize your change.

[cueweb] Add view presets, immersive mode, and split-view workspaces

Three web-native replacements for CueGUI window/layout affordances, all sharing the existing localStorage + cross-tab storage-event conventions.

Saveable view presets (CueGUI "Save Window Settings"):

  • New components/ui/views-menu.tsx: per-table "Views" dropdown to Save as…, Apply, Rename, Delete named presets capturing column order/visibility, sort, filters, and page size. Persists under cueweb.views. with the active preset under cueweb.views..active; syncs across tabs. Built-in "Default" entry restores documented defaults and can't be renamed/deleted.
  • Operates on the TanStack table instance, so it's table-agnostic. Wired into SimpleDataTable (hosts, allocations, shows, layers, frames) and the Jobs data-table toolbar.

Immersive (full-screen) mode (CueGUI Toggle Full-Screen):

  • New app/utils/use_immersive_mode.ts + components/ui/app-shell.tsx: hides the header, sidebar, and status bar so the active table gets the full viewport.
  • Toggled via F / Cmd-Ctrl+Shift+F, the Other menu, and a floating Exit-immersive button. Persists under cueweb.layout.immersive; syncs tabs.

Multi-pane split workspaces (CueGUI Window > "Add new window"):

  • New /split route + components/ui/split-view.tsx: open two pages side-by-side in resizable iframe panes, each with its own URL (/split?left=/jobs&right=/hosts/server-01). Drag/keyboard divider resize (ratio persisted under cueweb.split.ratio); in-pane navigation syncs back to the URL so reload restores both panes. AppShell hides chrome inside panes.

  • Also: register Immersive + Split view in the menu registry (Help search), add Suspense around useSearchParams on /split, and document all three in README + docs/_docs/reference/cueweb.md.

  • Tests: views-menu, immersive_mode, and split_view_utils unit suites (jest green; tsc + lint clean; production build passes).

LLM usage disclosure

Parts of this solution's implementation were developed with assistance from Claude Opus.

Three web-native replacements for CueGUI window/layout affordances, all sharing the existing localStorage + cross-tab `storage`-event conventions.

Saveable view presets (CueGUI "Save Window Settings"):
- New components/ui/views-menu.tsx: per-table "Views" dropdown to Save as…, Apply, Rename, Delete named presets capturing column order/visibility, sort, filters, and page size. Persists under cueweb.views.<page> with the active preset under cueweb.views.<page>.active; syncs across tabs. Built-in "Default" entry restores documented defaults and can't be renamed/deleted.
- Operates on the TanStack table instance, so it's table-agnostic. Wired into SimpleDataTable (hosts, allocations, shows, layers, frames) and the Jobs data-table toolbar.

Immersive (full-screen) mode (CueGUI Toggle Full-Screen):
- New app/utils/use_immersive_mode.ts + components/ui/app-shell.tsx: hides the header, sidebar, and status bar so the active table gets the full viewport.
- Toggled via `F` / Cmd-Ctrl+Shift+F, the Other menu, and a floating Exit-immersive button. Persists under cueweb.layout.immersive; syncs tabs.

Multi-pane split workspaces (CueGUI Window > "Add new window"):
- New /split route + components/ui/split-view.tsx: open two pages side-by-side in resizable iframe panes, each with its own URL (/split?left=/jobs&right=/hosts/server-01). Drag/keyboard divider resize (ratio persisted under cueweb.split.ratio); in-pane navigation syncs back to the URL so reload restores both panes. AppShell hides chrome inside panes.

Also: register Immersive + Split view in the menu registry (Help search), add Suspense around useSearchParams on /split, and document all three in README + docs/_docs/reference/cueweb.md.

Tests: views-menu, immersive_mode, and split_view_utils unit suites (jest green; tsc + lint clean; production build passes).
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9c98b5b2-6ceb-4adf-b2e9-8334b9cbc21b

📥 Commits

Reviewing files that changed from the base of the PR and between ffc04c4 and 0dce7af.

📒 Files selected for processing (3)
  • cueweb/app/__tests__/utils/immersive_mode.test.ts
  • cueweb/app/utils/use_immersive_mode.ts
  • cueweb/components/ui/views-menu.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • cueweb/app/tests/utils/immersive_mode.test.ts
  • cueweb/app/utils/use_immersive_mode.ts
  • cueweb/components/ui/views-menu.tsx

📝 Walkthrough

Walkthrough

Adds three CueGUI-parity workspace features to CueWeb: (1) a per-page saveable "Views" dropdown that persists table column/sort/filter presets to localStorage with cross-tab sync; (2) an immersive full-screen mode hook (useImmersiveMode) that hides app chrome, togglable via F/Cmd+Shift+F; and (3) a /split route rendering two iframe panes side-by-side with a draggable, persisted divider. All three features are integrated into the header, keyboard shortcuts overlay, and menu registry.

Changes

Saveable Table View Presets

Layer / File(s) Summary
ViewsMenu types, helpers, and component
cueweb/components/ui/views-menu.tsx
Defines View/ViewColumn/ViewSort types, DEFAULT_VIEW_NAME, loadViews/saveViews/captureView/applyView helpers, and the full ViewsMenu React component with cross-tab storage event sync, dropdown, and save/rename dialog.
SimpleDataTable and Jobs table integration
cueweb/components/ui/simple-data-table.tsx, cueweb/app/jobs/data-table.tsx
Adds optional viewsPageKey prop to SimpleDataTableProps and conditionally renders ViewsMenu in the toolbar; replaces the Jobs data-table Columns dropdown with ViewsMenu.
viewsPageKey wiring across page consumers
cueweb/app/allocations/page.tsx, cueweb/app/hosts/page.tsx, cueweb/app/shows/shows-client.tsx, cueweb/app/jobs/[job-name]/page.tsx, cueweb/components/ui/job-details-inline.tsx
Passes viewsPageKey to each page's SimpleDataTable to namespace preset storage per page.
ViewsMenu unit tests and docs
cueweb/app/__tests__/components/views-menu.test.ts, docs/_docs/reference/cueweb.md, cueweb/README.md
Jest tests for all loadViews/saveViews/captureView/applyView behaviors; documentation section added.

Immersive Full-Screen Mode

Layer / File(s) Summary
useImmersiveMode hook
cueweb/app/utils/use_immersive_mode.ts
Implements readImmersiveFromStorage, writeToStorage, and useImmersiveMode with SSR-safe init, localStorage persistence, custom same-tab event, and cross-tab storage event subscription.
AppShell chrome gating and layout refactor
cueweb/components/ui/app-shell.tsx, cueweb/app/layout.tsx
Adds AppShell that hides sidebar/header/status bar when immersive or iframe-embedded, shows a fixed Exit button in immersive top-level mode; refactors RootLayout to delegate chrome to AppShell.
Keyboard shortcut and menu registry wiring
cueweb/components/ui/shortcuts-overlay.tsx, cueweb/components/ui/app-header.tsx, cueweb/app/utils/use_menu_registry.ts
Adds toggleImmersive to ShortcutAction, wires F/Cmd+Shift+F handler, adds header Other dropdown items and menu registry commands for immersive toggle and split view.
useImmersiveMode unit tests and docs
cueweb/app/__tests__/utils/immersive_mode.test.ts, docs/_docs/reference/cueweb.md, cueweb/README.md
Jest tests covering all hook and storage behaviors; documentation and README updated.

Multi-Pane Split Workspace

Layer / File(s) Summary
split_view_utils: constants, sanitization, and URL helpers
cueweb/app/utils/split_view_utils.ts
Exports ratio constants, PaneSide type, sanitizePanePath (rejects unsafe paths), clampRatio/parseRatio, buildSplitUrl, and readRatio/writeRatio localStorage wrappers.
SplitView component and Pane building block
cueweb/components/ui/split-view.tsx
Implements the Pane iframe with imperative src management and onNavigate reporting, and SplitView with search-param URL state, pointer/keyboard divider resizing, swap/reset, and responsive layout.
SplitPage route and menu entry points
cueweb/app/split/page.tsx, cueweb/app/utils/use_menu_registry.ts, cueweb/components/ui/app-header.tsx
Adds the /split Next.js page wrapping SplitView in Suspense; split view navigation is wired into AppHeader and useMenuRegistry.

Sequence Diagrams

sequenceDiagram
  participant User
  participant AppHeader
  participant useImmersiveMode
  participant AppShell
  participant localStorage

  User->>AppHeader: click "Immersive (full-screen)"
  AppHeader->>useImmersiveMode: toggleImmersive()
  useImmersiveMode->>localStorage: write cueweb.layout.immersive=true
  useImmersiveMode->>useImmersiveMode: dispatch cueweb:immersive-changed
  useImmersiveMode-->>AppShell: immersive=true
  AppShell->>AppShell: hideChrome → unmount sidebar/header/statusbar
  AppShell->>AppShell: render fixed "Exit immersive" button
  User->>AppShell: click "Exit immersive"
  AppShell->>useImmersiveMode: setImmersive(false)
  useImmersiveMode->>localStorage: write cueweb.layout.immersive=false
  useImmersiveMode-->>AppShell: immersive=false
  AppShell->>AppShell: restore chrome
Loading
sequenceDiagram
  participant User
  participant SplitView
  participant Pane as Pane (iframe)
  participant split_view_utils
  participant localStorage

  User->>SplitView: navigate to /split?left=/jobs&right=/hosts
  SplitView->>split_view_utils: sanitizePanePath(left), sanitizePanePath(right)
  SplitView->>localStorage: readRatio()
  localStorage-->>SplitView: persisted ratio
  SplitView->>Pane: render left iframe src=/jobs
  SplitView->>Pane: render right iframe src=/hosts
  Pane-->>SplitView: onNavigate(newPath) on iframe load
  SplitView->>SplitView: router.replace({left:newPath})
  User->>SplitView: drag divider
  SplitView->>split_view_utils: clampRatio(newRatio)
  SplitView->>localStorage: writeRatio(clampedRatio)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • AcademySoftwareFoundation/OpenCue#2391: Both PRs extend SimpleDataTable and SimpleDataTableProps with additional behavioral props (viewsPageKey/ViewsMenu vs. isHostsTable mode), overlapping on the same component file.
  • AcademySoftwareFoundation/OpenCue#2397: Both PRs modify cueweb/components/ui/simple-data-table.tsx with different prop/behavior extensions (viewsPageKey/ViewsMenu vs. isProcsTable variant).
  • AcademySoftwareFoundation/OpenCue#2410: Both PRs modify cueweb/components/ui/simple-data-table.tsx—the main PR adds viewsPageKey/ViewsMenu, while the retrieved PR adds the isAllocationsTable variant—directly related at the same component level.

Suggested reviewers

  • lithorus
  • DiegoTavares

Poem

🐇 Hop, hop! The workspace grows so wide,
Two panes sit neatly, side by side.
Press F and all the chrome takes flight —
My views are saved, my presets bright!
From split to full, I leap with glee,
localStorage holds my memory. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the three main features added: view presets, immersive mode, and split-view workspaces, directly matching the primary objectives of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cueweb/app/utils/use_immersive_mode.ts`:
- Around line 71-94: The same-tab synchronization re-reads from localStorage
every time instead of propagating the actual value being set, which causes
reliability issues when storage writes fail. Modify the CustomEvent dispatched
in the setImmersive function to include the value being set as part of the event
detail. Then update the handler function (which listens for CHANGE_EVENT) to
extract and use that value from the event detail instead of calling
readImmersiveFromStorage(). Keep the storageHandler as is to continue handling
cross-tab synchronization through actual storage reads.

In `@cueweb/components/ui/views-menu.tsx`:
- Around line 379-420: The rename and delete buttons (with openRenameDialog and
handleDelete callbacks) are nested as interactive elements within the
DropdownMenuItem component, making them difficult to access via keyboard
navigation. Refactor this structure by promoting the rename and delete actions
to first-class menu items instead of nesting them within the view row. You can
achieve this by either creating separate DropdownMenuItem entries for each
view's rename and delete actions, or by implementing a submenu pattern. This
will allow keyboard-only users to navigate and activate these actions using
standard menu keyboard navigation patterns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 147e282a-1802-4a83-aa81-28bdb45645b2

📥 Commits

Reviewing files that changed from the base of the PR and between ca5f70f and ffc04c4.

📒 Files selected for processing (22)
  • cueweb/README.md
  • cueweb/app/__tests__/components/views-menu.test.ts
  • cueweb/app/__tests__/utils/immersive_mode.test.ts
  • cueweb/app/__tests__/utils/split_view_utils.test.ts
  • cueweb/app/allocations/page.tsx
  • cueweb/app/hosts/page.tsx
  • cueweb/app/jobs/[job-name]/page.tsx
  • cueweb/app/jobs/data-table.tsx
  • cueweb/app/layout.tsx
  • cueweb/app/shows/shows-client.tsx
  • cueweb/app/split/page.tsx
  • cueweb/app/utils/split_view_utils.ts
  • cueweb/app/utils/use_immersive_mode.ts
  • cueweb/app/utils/use_menu_registry.ts
  • cueweb/components/ui/app-header.tsx
  • cueweb/components/ui/app-shell.tsx
  • cueweb/components/ui/job-details-inline.tsx
  • cueweb/components/ui/shortcuts-overlay.tsx
  • cueweb/components/ui/simple-data-table.tsx
  • cueweb/components/ui/split-view.tsx
  • cueweb/components/ui/views-menu.tsx
  • docs/_docs/reference/cueweb.md

Comment thread cueweb/app/utils/use_immersive_mode.ts Outdated
Comment thread cueweb/components/ui/views-menu.tsx
- use_immersive_mode: carry toggled value on the CHANGE_EVENT detail (and flip in-memory state) so a failed localStorage write no longer reverts the toggle; cross-tab still reads storage. Adds a regression test.
- views-menu: make each saved view a submenu (Apply / Rename / Delete as real menu items) so rename/delete are keyboard-navigable, not nested buttons.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants