Skip to content

Menu System Overhaul: Foundation Locked & Verified#21

Merged
ScottMorris merged 38 commits intomainfrom
feature/menu-system-overhaul
Apr 6, 2026
Merged

Menu System Overhaul: Foundation Locked & Verified#21
ScottMorris merged 38 commits intomainfrom
feature/menu-system-overhaul

Conversation

@ScottMorris
Copy link
Copy Markdown
Contributor

@ScottMorris ScottMorris commented Apr 6, 2026

Summary

Lock and verify the foundation for Spindle's new scene-driven menu system. This overhaul replaces the legacy flat button model with a high-fidelity, extensible MenuDocument architecture.

User-facing changes

Scene Editor: Rebuild the MenusPage as a professional three-pane document editor (Layers, Canvas, Inspector).
Rich Node Tools: Add support for authoring Text, Image, and Shape nodes with full dimension and styling control.
DVD Preview: Implement an opt-in toggle that simulates the DVD target aesthetic with saturation and contrast filters.
Undo/Redo: Add a robust 50-entry snapshot-based undo history for the menu design workspace.
Unified Modes: Clean up the authoring workflow into Design, Bind, and Compile modes.

Workflow runtime

Decoupled Compiler: Implement the AuthorableMenuRef interface in Rust, allowing the DVD compiler to prefer rich scene data while maintaining legacy compatibility.
Schema Sync: Synchronize Rust and TypeScript models to ensure 100% property parity for all node types (dimensions, fonts, colors).
Standard Awareness: Automate canvas and node sizing based on the project's VideoStandard (NTSC vs PAL).
WYSIWYG Rendering: Extend the compiler to render authored Text and Shape nodes into the final DVD MPEG output.

Documentation

Spec Alignment: Update SPEC.md and Spindle_Menu_System_Spec.md to reflect the completed scene-driven behaviour.
Strategic Roadmap: Finalize Milestones 1–3 and transition the Automated Generation extension to a separate workstream (Issue #20).

Test plan

  • Unit Coverage: 47 Vitest tests passing, including 18 new tests for scene editor components and state sync.
  • Backend Integrity: 96 Rust tests passing in the dev container, including new round-trip and migration proofs.
  • Full-Stack Round-trip: Verified that rich design properties survive the serialise/deserialise cycle through the Rust layer.
  • Regression: Verified that legacy projects continue to compile into valid DVD outputs via the non-destructive schema expansion.
  • Compiler Proof: Verified that authored Text and Shape nodes are correctly rendered into the MPEG output.

🤖 Generated with Gemini CLI

ScottMorris and others added 30 commits April 4, 2026 21:40
**What:**
- add a shared tooling environment note for agent context
- record the container-first verification rule in the current sprint broadcast
- capture the same environment constraint in Franklin's durable memory

**Why:**
- help agents avoid assuming host-installed Rust tooling is available
- keep verification planning aligned with the current laptop environment
**What changed**
- Add a canonical `docs/Spindle_Menu_System_Spec.md` that defines the authored-menu direction for Spindle without leaning on tool-specific inspiration framing.
- Add `docs/menu-system-implementation-plan.md` with a phased delivery plan tied to the current frontend, schema, planner, renderer, and diagnostics code paths.

**Why**
- The menu work needed one source of truth for the product direction before implementation starts.
- A codebase-grounded execution plan makes it easier to sequence schema, UI, render-pipeline, and validation changes without breaking the current DVD build path.
This introduces the `authoredDocument` seam in both Rust and TypeScript, allowing for a phased transition from flat menus to scene-based menus.

- Add `MenuDocument` and sub-types to `models.rs` and `project.ts`.
- Implement in-memory migration from legacy menu fields to the new scene model.
- Preserve backward compatibility with the existing DVD build pipeline by keeping legacy fields.
- Add unit tests for menu migration.
- update VideoStandard with default_resolution helper for NTSC/PAL
- ensure Menu::migrate_to_document respects project VideoStandard
- expand MenuDocument and SceneNode::Button schemas to preserve highlights, background mode, and button video during migration
- align TypeScript types with Rust model expansions
**Summary**

This prepares the frontend architecture for Milestone 2 of the menu-system overhaul by introducing mode-aware state and a robust synchronization layer.

**Details**

- Add **MenuEditorMode** ('design', 'bind', 'remote', 'compile') to `types/project.ts`.
- Refactor `project-store.ts` to include `selectedMenuId` and `menuEditorMode` for cross-pane synchronization.
- Implement `updateMenuDocument` with a **Sync Layer** that reflects scene changes back to legacy DVD fields (`buttons`, `backgroundAssetId`), preserving buildability during the transition.
- Update `MenusPage.tsx` to use the new store-based selection and mode state.
- Add a **Mode Switcher** to the menu editor and initialize `authoredDocument` on new menu creation.
- Ensure all comments and documentation use Canadian spelling.
This completes the backend decoupling requested by Kyle.

- Updated `AuthorableMenuRef` to provide a unified interface for both legacy and `MenuDocument` backed menus.
- Refactored `planner.rs`, `menu.rs`, and `authoring.rs` to consume this interface.
- Enabled the compiler to utilize new scene-backed menus while maintaining full legacy fallback.
- Verified that the authoring pipeline still builds and produces valid XML/commands.
…end scene-aware

**Summary**

Following Franklin's directive, this refactors the menu system to treat the `MenuDocument` as the sole primary source of truth, removing redundant manual synchronization layers.

**Details**

- Update backend `auto_generate_navigation` to directly update the `authored_document` interaction graph.
- Simplify `updateMenuDocument` and `autoGenerateMenuNav` in `project-store.ts` by removing manual scene-to-button property mirroring.
- Refactor `MenusPage.tsx` components (Canvas, Preview, Editor) to prioritize `authoredDocument` data for all rendering and update operations.
- Move `previewMode` and `showSafeArea` to the project store to ensure these settings persist across menu selections.
- Maintain basic metadata synchronization (name, background, colours) back to legacy fields for backward compatibility with immediate UI components.
**Summary**

Adds unit tests for the `updateMenuDocument` action and restores basic button sync-back to maintain structural integrity across the UI.

**Details**

- Add Vitest unit tests in `project-store.test.ts` to cover menu document state transitions.
- Verify that legacy menus are correctly lifted into `MenuDocument` with standard-aware resolutions (720x480 for NTSC, 720x576 for PAL).
- Restore basic button synchronization (id, label, bounds, action) in the store to keep legacy-dependent pages like Planner and Logs accurate.
- Ensure `authoredDocument` remains the primary source of truth while supporting immediate UI compatibility.
- refactor menu_button_overlay_filter to use authored highlight colours
- add unit tests verifying AuthorableMenuRef prioritises authoredDocument
Decompose the 1355-line `MenusPage.tsx` monolith into composable
components for the Milestone 2 scene editor:

- `SceneCanvas`: artboard viewport with node rendering, drag/resize,
  snap guides, safe-area overlays, Honest Preview filter, and
  navigation preview mode
- `LayersPanel`: z-ordered scene node list with type icons, selection
  highlight, and collapsible icon-rail mode
- `InspectorPanel`: contextual property editor for selected nodes
  (geometry, action, highlight mode, overlay colours) with collapsible
  panel support

The `MenusPage` becomes a thin orchestration shell with a three-pane
layout (Layers | Canvas | Inspector) gated by editor mode. Navigation
lines are scoped to Remote mode; Honest Preview is a toggleable
canvas-local filter.

Incorporates Kyle's `SceneNode::Button` schema fields
(`highlightMode`, `highlightKeyframes`, `videoAssetId`) throughout.

Adds `SceneNode` import to `project-store.ts` to fix type narrowing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
18 tests covering LayersPanel, InspectorPanel, and SceneCanvas:

- LayersPanel: reverse z-order rendering, selection highlight,
  `onSelectNode` callback, collapsed icon-rail state, empty state
- InspectorPanel: empty state, button property fields, label change
  dispatches `onUpdateButton`, remove button callback, collapsed state
- SceneCanvas: node rendering, selection class, `onSelectNode` on
  click, Honest Preview class and badge, safe-area guides, navigation
  preview mode, background click deselection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added `SetAudioStream`, `SetSubtitleStream`, and `Sequence` variants to `PlaybackAction` in Rust and TypeScript.
- Implemented recursive action validation to support nested sequences.
- Added a 36-button hard limit and 18-button "safe zone" warning to the project validator.
- Added authored scene validation to ensure all scene node assets (images, videos) exist in the project.
- Implemented `count_scene_buttons` and `validate_scene_nodes` helpers in `desktop.rs`.
- Updated DVD navigation command resolution to translate the new actions into VM commands.
This ensures a clean build in the dev container.

- Fix `unused_mut` warning in `navigation.rs`.
- Add `#[allow(dead_code)]` to intentional `AuthorableMenuRef` seams awaiting Phase 1c.
- Implement `Default` for `MenuButton`, `FocusNode`, `MenuTiming`, and `MenuCompilePolicy`.
- Fix type mismatch and missing arguments in unit tests.
- Verify 96 tests passing and zero warnings.
**Summary**

Fixes TypeScript compiler errors where `actionToString` lacked an ending return statement, failing to account for all possible branches of the `PlaybackAction` union.

**Details**

- Add `default` return to `actionToString` in `InspectorPanel.tsx`.
- Add `default` return to `actionToString` in `BindMode.tsx`.
- Verified that `pnpm build` now passes without errors.
… Preview

Stop `click` event propagation on canvas buttons so the viewport's
deselect handler doesn't fire immediately after `mouseDown` selects.
Rename the user-facing "Honest Preview" label to "DVD Preview".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show a colour swatch next to the background dropdown when no asset is
assigned, wired to `scene.background.colour`. Render the chosen colour
on the canvas viewport.

Label the two safe-area guide rectangles "Action Safe" and "Title Safe"
so users can distinguish them at a glance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add toolbar buttons for Text, Image, and Shape node creation in Design
mode alongside the existing Button tool.

Remove Remote as a standalone editor mode. Nav Lines and Keyboard Nav
toggles are now available directly in the Design mode toolbar. Bind mode
gets a mini canvas preview alongside its action table so users can see
the layout while binding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete or Backspace removes the selected node from the canvas (skips
when focus is inside an input field).

Undo/redo implemented as a snapshot stack in the project store (capped
at 50 entries). Ctrl+Z undoes, Ctrl+Shift+Z redoes. The redo stack
clears on any new edit, matching standard behaviour.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The collapse/expand affordance on both panels was confusing without a
clear purpose. Both panels are now always visible in the editor layout.

Removes collapsed state, toggle handlers, and associated CSS. Updates
tests to match the simplified interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend `SceneNode` types with `width`/`height` (plus `fontSize`,
`colour` for text and `fill` for shape) so all positioned nodes can
be rendered, dragged, resized, and snap-aligned on the design canvas.

Non-button scene nodes get their own visual treatment: text renders
its content inline, shapes show their fill colour, images show a
placeholder badge. All share the same drag/resize/snap infrastructure
as buttons.

The Delete key handler now removes any selected node type, not just
buttons. A new `handleUpdateSceneNode` routes position/size updates
for non-button nodes through the authored document.

Note: the Rust `SceneNode` enum in `models.rs` needs matching
`width`/`height` fields on text, image, and shape variants — that
change is out of scope here and will be handed off.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the placeholder GenericNodeInspector with dedicated editors:
- Text: content, font size, colour, position/size
- Image: asset picker dropdown, position/size
- Shape: fill colour, position/size

All non-button inspectors include a Remove button and wire through
`onUpdateSceneNode` / `onRemoveNode` from the MenuEditor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This aligns the Rust models with the new frontend scene editor properties.

- Add `width` and `height` to `Text`, `Image`, and `Shape` scene node variants.
- Add optional `font_size` and `colour` to `Text` nodes.
- Add optional `fill` to `Shape` nodes.
- Mark optional fields with `#[serde(default)]` for backward-compatible deserialization.
- Verify that compiler and validation logic remains compatible via `..` pattern usage.
**Summary**

Adds an automated proof that non-button scene nodes (`Text`, `Image`, `Shape`) are correctly preserved during the project serialization round-trip.

**Details**

- Add Vitest test case to `project-store.test.ts` that mocks the `serialise_project` command.
- Verify that design-rich properties like `fontSize`, `assetId`, and `fill` are correctly passed to the Rust backend without data loss.
- Mock `@tauri-apps/plugin-store` to ensure the project save action completes successfully during tests.
- Provide verification that Nicholas's new scene features are safe for the upcoming pagination engine.
- implement AuthorableMenuRef::scene_nodes to expose all nodes
- update build_ffmpeg_menu_command to render Text and Shape nodes using FFmpeg filters
- add integration test verifying scene node inclusion in FFmpeg command
- close 'Full Loop' authoring gap where non-button nodes were missing from build
…tion

**Summary**

- **Strategic Realignment**: Updated `SPEC.md` and `docs/Spindle_Menu_System_Spec.md` to reflect the "implement it all" holistic approach to the menu-system overhaul.
- **Auto-Pagination and Menu Sets**: Formally integrated the "Menu Set" concept and auto-pagination requirements for large collections into the product specification, including a **Safe Zone** of 12-18 buttons per page.
- **State-Setting Actions**: Expanded the playback action model to include audio and subtitle selection, enabling the creation of functional setup menus.
- **Roadmap Acceleration**: Re-aligned the implementation roadmap to pull motion menu groundwork and automated generation into the v1 authoring core.
- **Milestone 4 Definition**: Updated the current sprint context to include the **Automated Generation Engine** and **Pagination Heuristics** as active workstreams.
Commented out unused image overlay logic in `menu.rs` to resolve build warnings while preserving the code for future filtergraph expansion.
This commit refines the layout of the Bind mode in the menu system and ensures all titles are lowercase-friendly by removing forced uppercasing.

**Key changes:**
- Moved the `SceneCanvas` preview above the action bindings panel in the Bind mode layout.
- Switched `bind-mode-layout` from a grid to a vertical flex column for better visual rhythm.
- Removed `text-transform: uppercase` from all headers and titles in the menu system components, including Bind mode, Compile mode, Inspector, and Layers panels.
- Preserved original source code casing (Title Case / Sentence Case) to allow for more natural and intentional typographic control.
- This change ensures the interface feels more composed and less mechanically repetitive, aligning with our premium visual direction.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Spindle JS tests

46 tests  +20   46 ✅ +20   0s ⏱️ ±0s
 5 suites + 2    0 💤 ± 0 
 1 files   ± 0    0 ❌ ± 0 

Results for commit 2a212ce. ± Comparison against base commit 7fa0283.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

Spindle Rust tests

96 tests  +2   96 ✅ +2   1s ⏱️ ±0s
 1 suites ±0    0 💤 ±0 
 1 files   ±0    0 ❌ ±0 

Results for commit 2a212ce. ± Comparison against base commit 7fa0283.

This pull request removes 1 and adds 3 tests. Note that renamed tests count towards both.
tauri-plugin-spindle-project ‑ build::menu::tests::overlay_images_use_outline_boxes
tauri-plugin-spindle-project ‑ build::menu::tests::authorable_menu_ref_prefers_authored_document
tauri-plugin-spindle-project ‑ build::menu::tests::build_ffmpeg_menu_command_includes_scene_nodes
tauri-plugin-spindle-project ‑ models::tests::menu_migration_lifts_legacy_fields

♻️ This comment has been updated with latest results.

@ScottMorris
Copy link
Copy Markdown
Contributor Author

@codex review

@ScottMorris ScottMorris added enhancement New feature or request frontend Frontend UI, styling, and webview behaviour backend Rust backend, commands, and native runtime behaviour plugin Plugin work rust Rust tooling, code, or integration work testing Tests, fixtures, and test tooling updates documentation Improvements or additions to documentation tauri Tauri platform or plugin-specific work labels Apr 6, 2026
@ScottMorris ScottMorris marked this pull request as ready for review April 6, 2026 05:24
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8a6f63b2a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ScottMorris and others added 2 commits April 6, 2026 09:15
Address all three failing CI checks on PR #21:

- Rust clippy: remove unused test imports in `menu.rs`, replace
  `match` with `if let` for single-pattern arms, collapse `if let`
  into outer match in `validate_scene_nodes`, move helper functions
  before `#[cfg(test)]` module in `desktop.rs`, use struct literal
  initialisation instead of field-reassign-with-default in tests,
  add `#[allow(clippy::too_many_arguments)]` for `validate_action`,
  derive `Default` for `FocusNode` instead of manual impl
- Rust fmt: reformat all Rust sources via `cargo fmt`
- JS fmt: reformat all JS/TS/CSS sources via Prettier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Clear `undoStack` and `redoStack` in `createProject`, `openProject`,
  and `closeProject` to prevent cross-project state rollback
- Sync `authoredDocument.scene.background.assetId` when the background
  `<select>` changes, so compiled menus use the correct background

Addresses Codex P1 comments on PR #21.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ScottMorris ScottMorris merged commit 2059879 into main Apr 6, 2026
8 checks passed
@ScottMorris ScottMorris deleted the feature/menu-system-overhaul branch April 6, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Rust backend, commands, and native runtime behaviour documentation Improvements or additions to documentation enhancement New feature or request frontend Frontend UI, styling, and webview behaviour plugin Plugin work rust Rust tooling, code, or integration work tauri Tauri platform or plugin-specific work testing Tests, fixtures, and test tooling updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant