- Add activeSpellbook to GrimoireState
- Display active spellbook title in header with clear button
- Add 'Update Layout' and 'Save as new' to SpellbookDropdown
- Highlight active spellbook in dropdown list
- Add 'Manage Spells' link to dropdown
- Refine dropdown styles (muted hover, no accent color)
- Separate preview mode to /preview/:actor/:identifier
- Direct links (/:actor/:identifier) now load layout immediately
- Add SpellbookDropdown to header center for quick layout switching
- Restore accidentally deleted wallet-related files
- Remove obsolete SpellbookLoader.tsx
- Update spellbook icon to BookHeart across the app
- Implement Preview mode with routing /:actor/:identifier
- Add Preview banner in Home component with Apply/Discard actions
- Add Preview and Share buttons to Spellbook renderers
- Clean up unused imports
- Ensure SaveSpellbookDialog passes content to PublishSpellbookAction
- Refactor SpellDialog to use PublishSpellAction instead of manual logic
- Ensure published events are added to local EventStore for immediate UI update
- Clean up unused imports and variables in SpellDialog
- Repository name in both feed and detail views now links to the repository (kind 30617)
- Uses same link styling as issues: cursor-crosshair, underline decoration-dotted
- Fetches repository event to display proper name instead of identifier
- Only "pushed <commit>" part opens the state event itself
- Repository link opens in new window using addWindow("open")
Implement renderers for NIP-34 repository state announcements:
- Add helper functions in nip34-helpers.ts to parse HEAD refs, branches, and tags
- Create RepositoryStateRenderer for compact feed view showing push notifications
- Create RepositoryStateDetailRenderer for detailed view with all refs
- Register both renderers in the kind registry
Feed view shows: "pushed <commit> to <branch> in <repo>"
Detail view shows: HEAD info, all branches, all tags with copyable commit hashes
Set up automated quality assurance checks:
- Lint: runs ESLint
- Build: runs TypeScript + Vite build
- Test: runs Vitest in CI mode
All jobs run in parallel on push/PR to main/master.
Verify that grid preset correctly handles uneven window counts:
- 2 windows → 1×2 grid
- 3 windows → 1×3 single row
- 4 windows → 2×2 perfect grid
- 5 windows → 2×3 grid (last row expands to fill)
- 7 windows → 2×4 grid (last row expands)
- 9 windows → 3×3 perfect grid
- 11 windows → 3×4 grid (last row expands)
Key behaviors verified:
- No empty space (last row windows expand to 100% width)
- All windows preserved in layout
- Window order maintained (depth-first traversal)
- Works with any N ≥ 2 windows
Also tests:
- Side-by-side with 2-4 windows (and error for 5+)
- Main + Sidebar with various window counts
- Error handling for too few/many windows
All 22 tests pass ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Major redesign of preset system from static templates to adaptive algorithms:
**Grid preset** (fully adaptive):
- Now arranges ALL windows in best-fit grid (2x2, 2x3, 3x3, etc.)
- Minimum: 2 windows (down from 4)
- Algorithm calculates optimal rows/cols for any N windows
- Examples:
* 4 windows → 2×2 grid
* 6 windows → 2×3 grid
* 9 windows → 3×3 grid
**Side-by-side preset** (adaptive with limit):
- Arranges 2-4 windows in single horizontal row
- Maximum 4 windows (prevents unusably narrow splits)
- Equal splits: 50%, 33%, 25%
- Clear error message if >4 windows
**Main + Sidebar preset** (unchanged):
- Already worked correctly (intentionally hierarchical)
- One main window (70%) + sidebars stacked (30%)
- Adapts naturally to any N ≥ 2 windows
Architecture changes:
- Replace static MosaicNode templates with generate() functions
- Add minSlots/maxSlots instead of single slots field
- Implement buildHorizontalRow(), buildVerticalStack(), buildGridLayout()
- Remove fillLayoutTemplate() (no longer needed)
- Remove extra window stacking (all windows now equal)
Benefits:
- No arbitrary hierarchy (no "featured" vs "extra" windows)
- Matches tiling window manager patterns (Amethyst, yabai, etc.)
- More intuitive: "grid" grids ALL windows, not just first 4
- Better UX: Works with any number of windows in range
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Animation improvements:
- Use professional easing curve: cubic-bezier(0.25, 0.1, 0.25, 1)
- Reduce duration from 200ms to 150ms for snappier feel
- Enable animations only during preset application (not manual resize/drag)
- Add CSS layout containment for better performance
- Add/remove 'animating-layout' class to control when animations occur
UI consolidation:
- Merge layout preset dropdown and insertion settings into single control
- Create unified LayoutControls component with sections:
* Presets (apply existing layouts)
* Insert Mode (balanced/horizontal/vertical)
* Split ratio slider with +/- buttons
- Remove separate icons, now just one SlidersHorizontal button
- Cleaner, more discoverable interface
Benefits:
- Smoother, more natural-feeling animations
- No animation jank during manual operations
- Single unified control reduces UI clutter
- All layout settings in one place
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove success notification when applying layouts (keep only errors)
- Add CSS transitions for smooth window resizing/repositioning
- Replace large settings Dialog with compact Popover
- Reduce settings UI from ~220 lines to ~97 lines
- Remove verbose descriptions and preview section
- Make settings match site's minimal UI patterns
- Settings now update live without Save/Cancel buttons
- Create popover.tsx component using Radix UI primitives
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Added TODO comment about IndexedDB unavailability in test runtime
- Moved layout preset selector to rightmost side of bottom bar
- Moved window settings button to rightmost side as well
- Removed per-workspace settings icon (now global button on right)
- Added spacer to separate workspace tabs from layout controls
- Changed preset descriptions to show "4+ windows" instead of "4 windows"
to clarify that presets work with more windows than the minimum
- Changed dropdown alignment from "start" to "end" for right-aligned opening
The bottom bar now has clearer visual separation:
[Workspace Tabs] [+] ................ [Layout Presets] [Settings]
Generated with [Claude Code](https://claude.com/claude-code)
Simplified layout system based on user feedback:
**1. Global Layout Config (Simpler)**
- Moved layoutConfig from per-workspace to global state
- One configuration applies to all workspaces (easier to understand)
- Updated state migration v8→v9 to move config to global level
- Updated WorkspaceSettings UI to edit global config
- Renamed updateWorkspaceLayoutConfig → updateLayoutConfig
**2. Preserve Extra Windows (Fixed Bug)**
- Fixed applyPresetToLayout to keep windows beyond preset slots
- When applying 4-window grid to 6 windows, windows 5-6 are preserved
- Extra windows stacked vertically on right side (70/30 split)
- No more window loss when applying presets
**3. Layout Dropdown in TabBar (Better UX)**
- Added dropdown menu next to workspace tabs
- Shows all available presets with icons (Grid2X2, Columns2, Split)
- Displays window requirements and availability
- Disables presets that need more windows than available
- One-click preset application with toast feedback
- More accessible than /layout command
All tests passing (457 passed). State migration handles v6→v7→v8→v9 correctly.
Generated with [Claude Code](https://claude.com/claude-code)
Phase 3 implementation:
- Created layout-presets.ts with 3 built-in presets (side-by-side, main-sidebar, grid)
- Implemented fillLayoutTemplate() for recursive template filling with window IDs
- Added collectWindowIds() for depth-first traversal of layout trees
- Created applyPresetToLayout() to reorganize existing windows
- Created layout-parser.ts for /layout command argument parsing
- Added layout command to man.ts with documentation and examples
- Built LayoutViewer component with:
* Visual preset gallery with diagrams
* Window count validation
* Apply preset functionality
* Error handling for insufficient windows
* Command-line preset specification support
- Wired LayoutViewer into WindowRenderer with lazy loading
- Added "layout" to AppId type definition
- Exposed applyPresetLayout in useGrimoire hook
Presets allow users to quickly reorganize multiple windows into
common layouts: 50/50 splits, 70/30 main+sidebar, or 2×2 grids.
Generated with [Claude Code](https://claude.com/claude-code)
Add per-workspace layout configuration UI with visual controls:
**Core Changes:**
- Add updateWorkspaceLayoutConfig() function to logic.ts for updating workspace layout settings
- Expose updateWorkspaceLayoutConfig in useGrimoire hook
**UI Components:**
- Create WorkspaceSettings dialog with three sections:
* Insertion Mode selector (Balanced/Horizontal/Vertical) with icons
* Split Percentage slider (10-90%) with real-time preview
* Insertion Position toggle (Left-Top/Right-Bottom)
- Add settings icon (SlidersHorizontal) to workspace tabs that appears on hover
- Settings button opens configuration dialog for that workspace
**UX Details:**
- Settings icon only visible on hover to reduce visual clutter
- Clear visual feedback for selected options with primary color highlights
- Preview section shows current configuration in plain language
- Reset to Defaults button restores smart mode defaults
- Prevents workspace switch when clicking settings icon
**Icons Used:**
- Sparkles: Balanced (smart auto-balancing)
- SplitSquareHorizontal: Horizontal splits
- SplitSquareVertical: Vertical splits
- SlidersHorizontal: Settings access
Each workspace can now have independent layout behavior configured through an intuitive UI.
Add per-workspace layout configuration with smart auto-balancing:
**Core Changes:**
- Add LayoutConfig interface to Workspace type with insertionMode, splitPercentage, insertionPosition
- Create layout-utils.ts with smart direction algorithm that auto-balances horizontal/vertical splits
- Update addWindow() to use workspace layoutConfig instead of hardcoded values
- Migrate state from v7→v8, adding layoutConfig to all workspaces with smart defaults
**Smart Direction Algorithm:**
- Analyzes layout tree to count horizontal vs vertical splits
- Automatically balances by favoring the less-common direction
- Defaults to horizontal (row) for first split
- Provides foundation for "Balanced (auto)" insertion mode
**Testing:**
- Add 30 comprehensive tests for layout-utils.ts (tree analysis, smart direction, window insertion)
- Add 30 tests for logic.ts addWindow() with different layout configs (row/column/smart modes)
- Update migration tests to verify v6→v7→v8 and v7→v8 paths
**Migration:**
- v7→v8 adds layoutConfig with defaults: smart mode, 50% split, second position
- All existing workspaces automatically get smart auto-balancing behavior
Implements orthogonal design: layout behavior = workspace settings (not command flags)