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>
Added Escape key handler to close ElementPopup or PagePopup when open.
The handler checks which popup is visible and calls its close method.
Works in all contexts (main document and iframe) using the existing
handleKeyboardShortcut function.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added keyboard event listener to iframe document to capture shortcuts
when user is focused inside the preview. Previously, keyboard events
inside iframes didn't bubble up to the parent document.
Changes:
- Add handleKeyboardShortcut function in App.vue
- Attach keydown listener to main document (for focus outside iframe)
- Attach keydown listener to iframe document (for focus inside iframe)
- Clean up listener on unmount
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix grid layout: add second column for input (grid-template-columns: var(--input-h) 1fr)
- Ensure color picker button is clickable with cursor pointer and pointer-events auto
- Set color picker z-index to 10000 to display above all UI elements
- Add global styles to ensure Coloris button is always clickable
Fixes issues where:
- Color picker appeared behind ElementPopup
- Button was not consistently clickable
- Grid layout was missing second column definition
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>
Enable automatic deletion of remote files that no longer exist locally.
This ensures the production server stays in sync with the repository,
removing obsolete files like the renamed stylesheet.css.
Protected directories (accounts, cache, sessions) remain excluded.
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>
- Add Forgejo workflow for automated build and deploy
- Build creates dist/ from public/ then adds Vue app build
- Configure Vite to build to dist/assets/dist with fixed filenames
- Deploy entire dist/ directory to production via FTP
- Add workflow documentation with FTP setup instructions
The workflow mirrors the GitLab CI approach: dist/ is created during build
and synchronized to production root on every push to main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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>
- Remove containers (section, div, article, etc.) from selectable elements
- Remove media elements (img, figure, ul, ol, table) from selection
- Keep text elements: headings, paragraphs, links, formatting tags
- Add figcaption as selectable, remove span
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change popup width from 800px to 71rem for better scaling
- Add DM Sans as default body font
- Set h1 font-size to 3rem
- Reduce paragraph bottom margin from 10mm to 5mm
- Add link styles (purple color, no underline)
🤖 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>
updateCssValue now handles three cases:
- Selector doesn't exist: creates the full block with property
- Selector exists but property missing: adds property to block
- Property exists: updates the value
🤖 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>