Menu System Overhaul: Foundation Locked & Verified#21
Merged
ScottMorris merged 38 commits intomainfrom Apr 6, 2026
Merged
Conversation
**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.
Spindle Rust tests96 tests +2 96 ✅ +2 1s ⏱️ ±0s 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.♻️ This comment has been updated with latest results. |
Contributor
Author
|
@codex review |
There was a problem hiding this comment.
💡 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".
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
MenuDocumentarchitecture.User-facing changes
• Scene Editor: Rebuild the
MenusPageas 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
AuthorableMenuRefinterface 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.mdandSpindle_Menu_System_Spec.mdto 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
🤖 Generated with Gemini CLI