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>
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>
- 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>
- 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>
Add visual feedback for hovered page templates:
- Display "@page {templateName}" label on page edge hover
- Label positioned at top-left of page with 30% opacity
- Orange background matching page highlight color
- Automatically removed when hovering elements or clicking
Ensures consistent UX between page and element hover states.
🤖 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 visual feedback for hovered elements:
- Display element selector label (e.g., "p", "h1.title") on hover
- Label positioned at top-left of element with 30% opacity
Refactor all hover/selection styles to use CSS classes instead of inline styles:
- .page-hovered, .page-selected for page states
- .element-hovered, .element-selected for element states
- .element-hover-label for the floating label
This improves maintainability and separation of concerns by moving
styling logic to CSS files instead of JavaScript.
🤖 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>
Instead of trying to print iframe directly, the print button:
1. Collects all styles from the iframe
2. Replaces the main page content with iframe content
3. Triggers window.print()
4. Reloads the page to restore the app
This ensures the PagedJS rendered content prints correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Collect all CSS (inline styles and stylesheet rules) and embed
them directly in the print document. This ensures styles are
available even when printed from a new window.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Open iframe content in a new window for printing to avoid
blank page issues with srcdoc iframes. The window opens,
prints, then closes automatically.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add @media print styles to hide EditorPanel, popups, and loader.
Only the preview iframe content is visible during print.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Override default print behavior to print the active iframe
content (PagedJS rendered preview) instead of the main page.
Works with both Cmd+P (Mac) and Ctrl+P (Windows/Linux).
🤖 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 --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>
Filter clicks to only trigger ElementPopup for elements inside
.pagedjs_page, preventing popups from appearing when clicking
on wrapper elements like .pagedjs_pages.
Also adds lock/unlock SVG icons.
🤖 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>
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>
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>
Displays a spinning loader in the top-right corner of the iframe while PagedJS is rendering. The loader automatically adjusts position when the editor panel is open.
🤖 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>
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>
Add functional page number and running title toggles with proper positioning
for left/right pages. Implement smooth crossfade transitions between iframe
reloads to eliminate visual flicker during PagedJS rendering.
Page Numbers & Running Titles:
- Page numbers toggle: adds counter(page) to @bottom-left (left pages) or
@bottom-right (right pages)
- Running title toggle: adds string(title) from h2 chapter titles
- Combined positioning: both elements appear side-by-side in same margin box
- Left pages: "1 Chapter Title" in @bottom-left
- Right pages: "Chapter Title 2" in @bottom-right
- Automatic CSS rule management: adds/removes @page:left, @page:right, and
string-set rules based on checkbox state
- Bidirectional sync: checkboxes reflect existing CSS state on load
Smooth Iframe Transitions:
- Dual iframe system: two iframes alternate as visible/hidden
- Crossfade technique: hidden iframe loads new content while visible remains
displayed, then smooth 300ms opacity transition
- Scroll preservation: saves scroll percentage from visible iframe, restores
to hidden iframe after PagedJS render
- Collision prevention: isTransitioning flag prevents overlapping renders
- Active frame tracking: computed property ensures ElementPopup always
references the visible iframe
Technical details:
- Uses srcdoc to inject HTML with dynamic CSS
- Z-index and opacity manipulation for layering
- CSS transitions (opacity 0.3s ease-in-out)
- Automatic frame swapping after transition completes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Reorganize editor components into dedicated folder
- Create PageSettings component with page format, margins, background controls
- Create TextSettings component (structure only, to be populated)
- Implement debounced updates (1s delay) to stylesheet store
- Add bidirectional sync between EditorPanel and StylesheetViewer
- Preserve scroll position as percentage when reloading preview
- Move @page rules from App.vue to stylesheet.css for unified management
- Extend css-parsing utils to handle text values (e.g., 'A4', 'portrait')
- Remove unnecessary comments, use explicit naming instead
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Move all popup logic into ElementPopup component (state, positioning, click handling)
- Make EditorPanel autonomous with direct store access
- Simplify App.vue by removing prop drilling and intermediary logic
- Update EditorPanel to control paragraph font-size instead of .about
- Fix CSS parsing: escape selectors in extractCssValue and updateCssValue
- Remove hardcoded .about references from PagedJsWrapper
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Group variables and functions by domain instead of by type
- Split handleIframeClick into smaller focused functions
- Add clear section comments for better navigation
- Sections: Store, PagedJS config, Iframe preview, Editor panel, Element popup, Lifecycle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add Pinia state management
- Create centralized stylesheet store with utility methods
- Extract CSS parsing utilities to src/utils/css-parsing.js
- Refactor ElementPopup to manage state independently via store
- Simplify App.vue by removing prop drilling
- Fix iframe rendering with srcdoc instead of document.write
- Improve API: updateProperty uses object parameter for clarity
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Isolate PagedJS in iframe to avoid DOM/CSS conflicts
- Add EditorPanel for global CSS controls
- Add StylesheetViewer with highlight.js syntax highlighting
- Add ElementPopup for element-specific CSS editing
- CSS modifications update preview reactively
- Support px/rem/em units
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>