Added \ key to toggle the editor panel open/closed:
- Opens to 'document' tab when panel is closed
- Closes panel when it's open
- Updated button tooltips to indicate the keyboard shortcut
Works in all contexts (main document and iframe).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add Cmd+S (Mac) or Ctrl+S (Windows/Linux) keyboard shortcut to save CSS
- Detect platform to display correct shortcut symbol (⌘ or Ctrl)
- Prevent default browser save behavior
- Translate tooltips to French
- Show shortcut in button tooltip
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed TextSettings fields not updating the stylesheet and preview after
the store refactoring that made content a computed property.
- Add missing font watcher in TextSettings.vue
- Update useCssUpdater.js to use store.replaceBlock() instead of
writing to readonly store.content
- Update createRule() to append to store.customCss instead of store.content
All TextSettings fields (font, size, margins, padding, alignment) now
correctly update the stylesheet and preview.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add two improvements to StylesheetViewer:
1. Scrollable CSS sections
- Add max-height (500px) and overflow-y to custom CSS editor
- Applies to both read-only and editable modes
- Improves UX when content exceeds viewport
2. Complete stylesheet export button
- Exports merged base CSS + custom CSS to single file
- Filename format: <narrative-slug>-style.print.css
- Includes informative comments:
* Header with narrative title and download date
* Section markers for base CSS and custom CSS
- Full-width button below custom CSS section
- Download via blob + automatic cleanup
Export file structure:
- Header comment (narrative info, date)
- Base CSS section with comment
- Custom CSS section with comment
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add CssFileImport component to StylesheetViewer allowing users to
import CSS files to replace custom CSS content.
Features:
- Click to select file via file dialog
- Drag & drop support with visual feedback
- File validation (.css only, max 1MB)
- Error messages for invalid files
- Direct replacement of customCss content
New component:
- src/components/ui/CssFileImport.vue
Integration:
- Added at top of StylesheetViewer
- Emits 'import' event with file content
- Content replaces customCss in store
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace content watcher with customCss watcher and add isEditing watcher
to properly sync field values when CSS is edited in StylesheetViewer.
Changes:
- Watch customCss instead of content for real-time updates
- Watch isEditing to reload values when exiting edit mode
- Use isUpdatingFromStore + nextTick to prevent circular updates
- Ensure popup fields stay in sync with stylesheet changes
Now when editing CSS manually in the Code tab, ElementPopup fields
update automatically when exiting edit mode.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add ability to lock/unlock inheritance for element styles while preserving
custom values. Locked styles are commented in the CSS and restored when unlocked.
New utilities:
- Create css-comments.js with comment/uncomment functions
- Add parseValueWithUnit to css-parsing.js for value parsing
- Add getBlockState, commentCssBlock, uncommentCssBlock to stylesheet store
ElementPopup improvements:
- Detect inheritance state from CSS block state (active/commented/none)
- Capture computed styles from iframe when unlocking with no custom CSS
- Comment/uncomment CSS blocks instead of deleting them on lock toggle
- Use nextTick to prevent race condition with watchers during popup init
- Extract values from both active and commented CSS blocks
Workflow:
1. First unlock: Capture computed styles → create CSS block
2. Lock: Comment the CSS block (styles preserved in comments)
3. Unlock again: Uncomment the block (styles restored)
Fixes issue where CSS rules were created on popup open due to
watcher race conditions during initialization.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement complete custom CSS management system:
- Separate base CSS (readonly) and custom CSS (editable)
- Save custom CSS to Kirby backend per narrative
- Visual save button with state indicators (dirty/saving/success/error)
- CSRF-protected API endpoint for CSS operations
- Dual-editor StylesheetViewer (base + custom with edit mode toggle)
- Auto-format custom CSS with Prettier on edit mode exit
Backend changes:
- Add web2print Kirby plugin with POST/GET routes
- Add customCss field to narrative blueprint
- Add CSRF token meta tag in header
- Include customCss and modified timestamps in JSON template
- Install code-editor plugin for Kirby panel
Frontend changes:
- Refactor stylesheet store with baseCss/customCss refs
- Make content a computed property (baseCss + customCss)
- Add helper methods: replaceBlock, replaceInCustomCss, setCustomCss
- Update all components to use new store API
- Create SaveButton component with FAB design
- Redesign StylesheetViewer with collapsable sections
- Initialize store from narrative data on app mount
File changes:
- Rename stylesheet.css → stylesheet.print.css
- Update all references to new filename
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Replace MarginEditor component with individual fields (top/bottom/left/right)
- Add link/unlink button with SVG icons to sync margin values
- When linked, all fields share the same value
- Auto-detect linked state when loading from stylesheet
- Match PageSettings UI pattern for consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Preview now includes all field values, not filtering defaults
- Commented CSS shows what would be applied with current field values
- Applies to both ElementPopup and PagePopup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Unlocking now creates CSS block with current field values
- Locking removes CSS block to restore general styles
- Show commented CSS preview when inheritance is locked
- Preview displays what would be applied if unlocked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Store unlock state in data-inheritance-unlocked attribute on DOM element
- Each element/page now remembers its own inheritance state
- Re-locking removes element-specific CSS block to restore inheritance
- Elements revert to general styles from TextSettings/PageSettings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create useDebounce composable to avoid code duplication
- Apply debounce to TextSettings margin/padding inputs
- Harmonize debounce delay to 500ms across all components
- Fix input lag when typing values like "30mm"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add JSON content representation template (recit.json.php)
- Create virtual /print page plugin for recit pages
- Add recit.php base template for content representation
- Create Pinia store for recit data management
- Add block components (text, heading, image, list, quote, video, map)
- Update PagedJsWrapper for dynamic content rendering with data-page-type
- Modify header.php to pass recit JSON URL via data attribute
- Update App.vue to load recit data on mount
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix element label positioning using getBoundingClientRect + scroll offset
- Filter out state classes from element selectors (element-hovered, etc.)
- Add cursor pointer on hovered elements and pages
- Prevent elements from having both hovered and selected classes
- Fix issue where closing popup left previous element in hovered state
Ensures only one visual state per element at a time and cleaner
selector display in labels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --color-purple variable (#7136ff)
- Hover: dashed purple outline on content elements
- Selected: purple background with 30% opacity
- ElementPopup: purple label and instance count
- Track hovered and selected elements separately from pages
- Clear element selection when popup closes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Filter clicks to only trigger ElementPopup for semantic content
elements (paragraphs, headings, images, lists, tables, etc.).
Clicking on generic divs or structural PagedJS elements now
closes popups instead of opening ElementPopup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add templateName extracted from data-page-type attribute
- When unlocked: edits create/update @page <templateName> block
- When re-locked: remove template block, preview returns to @page
- Fields retain their values when re-locking (for user convenience)
- Display dynamic template name in popup header
- Show template-specific CSS block when unlocked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use the same regex parsing logic as PageSettings to extract margin
and background values from the @page CSS block. This ensures
PagePopup displays the correct inherited values when the inheritance
is locked.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --color-page-highlight CSS variable (#ff8a50)
- Change page edge highlight from blue to orange
- Keep border visible while PagePopup is open
- Highlight all pages using the same template (data-page-type)
- Display dynamic page count in PagePopup header
- Emit close event from PagePopup for proper cleanup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add usePopupPosition composable for smart popup positioning
(bottom-right → bottom-left → top-right → top-left fallback)
- Refactor ElementPopup with complete controls matching mockup
- Add stylesheet sync: popups initialize from and watch store changes
- Add click-to-close behavior: clicking another element closes popup
- Add CSS property tooltips on all form labels (editor panel + popups)
with dotted underline and monospace code tooltip on hover
- Add field--view-only class and disabled attribute on locked fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Toggle between locked/unlocked states
- Dynamic text: "Déverrouiller l'héritage" / "Verrouiller l'héritage"
- Show appropriate lock/unlock icon based on state
- Style as simple text button with icon (matching design)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace custom classes with global .field and .settings-subsection classes to ensure proper styling from _forms.scss.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add rem unit option to all margin controls
- Implement proper margin and background updates with debouncing
- Integrate Coloris properly like PageSettings
- Add highlight.js syntax highlighting for readonly CSS
- Add Prettier formatting when exiting edit mode
- Use global unit toggle styles instead of custom
- Sync isEditing state with stylesheet store
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements page edge hover detection and click handling:
- Hover near page edges (30px threshold) shows light outline
- Click on page edge opens PagePopup for template customization
- PagePopup component similar to ElementPopup but for @page rules
- Both popups coordinate to show only one at a time
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a close button in the top-right corner of the editor panel with double arrow icon. The button closes the panel when clicked.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Disable auto-formatting while in editing mode
- Format CSS only when exiting editing mode
- Auto-disable editing mode when switching tabs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Moves loader HTML and CSS from App.vue to dedicated PreviewLoader component for better code organization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds 'rem' as an available unit for margin inputs in both components. Updates useCssSync composable to parse rem values from CSS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes margin/padding field initialization when CSS contains 4-value shorthand (e.g., margin: 0mm 0mm 24mm 0mm). Now properly populates both simple and detailed fields, and auto-opens detailed editor when values differ.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major refactoring to improve code quality and reduce duplication:
TextSettings.vue: 1127 → 269 lines (-76%)
New composables:
- useCssUpdater.js: generic CSS update/remove functions
- useCssSync.js: CSS parsing to form fields
New UI components:
- UnitToggle.vue: reusable unit selector buttons
- InputWithUnit.vue: number input with unit toggle
- MarginEditor.vue: simple/detailed margin editor with sync
Benefits:
- Reusable components for other settings panels
- Centralized CSS manipulation logic
- Better separation of concerns
- Easier to test and maintain
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements functional text settings panel with real-time CSS updates:
- Text styling: italic, weight, alignment applied to body selector
- Typography: font-size applied to paragraph selector
- Colors: text color and background with Coloris picker
- Margins/padding: simple and detailed modes for paragraphs
- Smart sync between simple and detailed margin fields
- Automatic CSS property cleanup (removes conflicting properties)
- Parses existing stylesheet values including 4-value shorthand
- Default margins: 0mm top/left/right, 24mm bottom for paragraphs
Technical details:
- Uses Pinia store extractBlock() for CSS manipulation
- Implements isSyncingFromSimple flag to prevent watcher conflicts
- Syncs detailed fields when modifying simple field (even when expanded)
- Removes shorthand properties when using detailed, and vice versa
- Fixed background color alpha channel (0 → 1) for Coloris visibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Adjust transition duration to 200ms for smoother crossfade
- Add explicit default values for iframe transform properties
- Simplify transition with 'all' shorthand
- Set explicit transition duration for editor panel
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When editor panel tab-content is open, the preview iframe now scales down (0.7) and repositions with appropriate margins to provide optimal viewing alongside the editor panel. This creates a smooth transition between full-width preview and side-by-side editing mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Wrap all field sections in .settings-subsection divs to match PageSettings structure
- Properly nest margin outer and inner collapsed sections within their parent subsections
- Replace custom color picker with Coloris integration for color and background fields
- Simplify reactive state by removing unused format and picker properties
- Initialize Coloris with dark theme, alpha support, and format toggle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add click handler on iframe to close Coloris color picker when user clicks
in the preview area. Also improve editor panel spacing and styling consistency.
Coloris Close Fix:
- Import Coloris in App.vue
- Add click listener on iframe document to call Coloris.close()
- Fixes issue where Coloris remained open when clicking in preview iframe
(clicks in iframes don't bubble to parent document)
Editor Styling Improvements:
- Increase EditorPanel width from 30rem to 35rem for better readability
- Add CSS variable --space-xs (0.5rem) for consistent spacing
- Improve form field spacing with gaps and better padding
- Add row-gap to margins subsection
- Fix input-with-color width and button height (1.1rem)
- Add gap to unit-toggle buttons
- Better visual hierarchy with margins on h2 and h3
CSS Changes:
- _forms.scss: Consistent spacing, better input padding, gap utilities
- _variables.scss: Add --space-xs variable
- Compiled CSS updated with new styles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>