Skip to content

Add comic book Fountain extensions, outline-manuscript sync, and quality fixes - #6

Merged
ErikChevalier merged 22 commits into
mainfrom
claude/zealous-newton-nnkg5k
Sep 20, 2026
Merged

ErikChevalier merged 22 commits into
mainfrom
claude/zealous-newton-nnkg5k

Conversation

@ErikChevalier

@ErikChevalier ErikChevalier commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Comic Book Fountain Extensions

  • Rust parser: Add PAGE/PANEL headers, SFX, and Caption element types with per-page auto-numbering of lettered elements (dialogue, captions, SFX share a counter that resets per page). Panel numbering also resets per page.
  • Publisher presets: Replace placeholder comic dialects with three real publisher presets — DC/Marvel (fountain+comic-dc), Dark Horse (fountain+comic-dh), Image/Indie (fountain+comic-indie) — that actually affect parsed output.
  • Caption subtypes: Recognize and normalize CAP, CAPTION, BANNER, VOICE OVER, VO, NARRATION, INTERNAL, EDITORIAL, TIME-PLACE input forms.
  • Syntax highlighting: Comic elements highlighted in manuscript editor with format-aware Tab cycling (page_header, panel_header, action, character, dialogue, parenthetical, caption, sfx).
  • Export pipeline: PDF, ODT, and EPUB exports all render comic elements with proper formatting and lettering numbers.

Outline Node Graph — Comic Nodes & Sync

  • Comic node types: Add Page, Panel, and Spread nodes to the outline canvas with distinct visual rendering (Page/Spread as wide accent bars, Panel as content cards with synopsis).
  • Format-aware context menus: Right-click shows comic nodes (Page/Panel/Spread/Note) in comic mode, screenplay nodes (Scene/Act Break/Note/Transition) in standard mode.
  • Comic structure templates: 22-Page Issue, 5-Page Short, Graphic Novel Chapter — each generates a page-by-page outline with panel beats.
  • Outline ↔ Manuscript sync: "Generate Script" converts outline nodes into a Fountain script skeleton. "Import Script" parses the manuscript back into outline nodes. Works for both comic and screenplay formats with round-trip fidelity.

Adversarial Review Fixes (ef481ee)

  • Export pipeline: Fixed format_id never reaching exporters from UI; fixed PDF using hardcoded font size instead of font_size parameter; fixed EPUB crash on BlankLine/PageBreak elements; removed double-escaping of SFX/Caption in EPUB; fixed ODT mimetype compression per ODF spec.
  • Outline sync: Fixed node ID type mismatches (now consistently str); added Spread pattern recognition to fallback parser; guarded against None labels; capped synopsis accumulation; converted recursive topo-sort to iterative.
  • Structure templates: Fixed overlapping node positions in comic templates; aligned node ID types to str.
  • Rust parser: Fixed dialogue balloon numbering discarded in comic mode; fixed caption subtype whitespace normalization; replaced all unwrap() with proper error propagation.
  • Editor: Added missing _format_id initialization; fixed page count to account for Spread elements.

Quality & Polish (from earlier commits)

  • Theme fixes, data persistence, Find & Replace, accessibility, cloud export honesty, collaboration fixes, text size controls.

Test plan

  • 976 tests pass (882 existing + 50 comic fountain + 44 comic outline)
  • Comic parser correctly handles PAGE/PANEL/SFX/Caption elements
  • Per-page lettering numbering resets correctly
  • All caption subtypes recognized and normalized
  • Backward compatibility: standard mode unaffected by comic extensions
  • Comic outline templates generate correct node structures
  • Outline → Script → Outline round-trip preserves page/panel/scene counts
  • Format-aware template combo switches between comic and screenplay templates
  • Adversarial review: 17 findings across export, sync, Rust parser — all fixed
  • Visual QA: comic outline nodes render correctly in both themes
  • Visual QA: comic script displays properly in manuscript view (both themes)
  • Visual QA: imported script creates correct outline node structure
  • All three export formats (PDF, ODT, EPUB) work with comic scripts
  • Script stats correctly report comic metrics (pages, panels, SFX, captions)

🤖 Generated with Claude Code

https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw

…odes, page width

- Enable high-DPI scaling and font antialiasing on QApplication for crisp rendering
- Increase font sizes across the entire stylesheet (+1-2px per element)
- Move home button from title bar to floating bottom-right corner
- Hide home button on dashboard (where it's redundant)
- Rewrite Tab cycling to properly rotate through all six Fountain element types
  (scene heading, action, character, dialogue, parenthetical, transition)
- Widen manuscript page frame from 680px to 816px to match standard script width
- Make outline graph nodes fully interactive: drag to move, double-click to edit
  label/synopsis, right-click for context menu with edit/link/delete, keyboard
  delete, visual selection highlight, and connection creation mode with crosshair

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Persist outline nodes to outline.json and plan documents to
  plan_documents.json so they survive project reload and phase switches
- Replace hardcoded inline theme colors in dashboard with objectName-based
  stylesheet rules that respond to theme changes (Nocturne/Organic)
- Make cloud export dialog honest: disable export button with "Coming Soon"
  label and explain provider integrations are planned for a future release
  instead of returning fake success URLs
- Update tests to match new cloud export behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
@ErikChevalier ErikChevalier changed the title Fix UI polish: rendering, fonts, home button, Tab cycling, graph nodes, page width Fix UI polish, data persistence, dashboard theming, and cloud export honesty Sep 16, 2026
- Find & Replace now updates match count on every find operation and
  shows current/total (e.g., "3 / 12")
- Replace operations now respect case sensitivity and regex checkboxes
  instead of hardcoding case-sensitive literal matching
- Convert ~20 inline theme.current() calls in overlays and editor phases
  to objectName-based stylesheet rules so they update on theme switch
- WeekStatsCard now shows Scenes and Characters (real data from parser)
  instead of Sessions and Streak (which had no backing data)
- Remove unused theme imports from find_replace, command_palette,
  marketplace, and collaboration overlays

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Replace ~40 inline setStyleSheet() calls that baked theme colors at
widget construction time with objectName-based CSS rules in the global
stylesheet. This fixes theme toggle responsiveness — widgets now pick up
new colors when the stylesheet is regenerated on theme change.

Added 8 new CSS rule groups to stylesheet.py: formGroup, kbdKey,
countBadge, loglinePreview, scratchpadEditor, diffView, moodboardTile,
and readingMode. Converted 25 files across overlays and screens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Wire FountainHighlighter._update_formats(), ScriptCompleter._apply_theme(),
and TitleBar.set_wordmark_accent() into the main _apply_theme() method so
these components refresh their colors on theme toggle. Also remove unused
theme imports from dashboard.py and presence_indicators.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
@ErikChevalier ErikChevalier changed the title Fix UI polish, data persistence, dashboard theming, and cloud export honesty Fix theme responsiveness, data persistence, and UI polish Sep 16, 2026
…security

- Fix CollabMessage.operation() receiving dict instead of Operation,
  causing AttributeError when relaying ops to other peers
- Rewrite OT transform to correctly handle overlapping DELETE ranges,
  INSERT splitting a DELETE range, and DELETE straddling an INSERT point
- Propagate actual P2P server port to LAN discovery so peers connect
  to the right address instead of hardcoded default 8741
- Increase P2P session ID entropy from 48 bits to 128 bits
- Remove premature mark_synced for companion devices before actual download

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Move window controls (close/minimize/maximize) to the right side and
settings button to the left side of the title bar. Replace all Unicode
symbol icons (×, ⚙, ⌂, arrows) across the app with Heroicons (MIT,
Tailwind Labs) rendered as QIcon via QSvgRenderer. Add IconButton
widget that auto-refreshes icon color on theme change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Add setAccessibleName() to all interactive widgets across 29 files
  (title bar, status bar, dashboard, editor, and all 22 overlays)
- Add keyboard focus indicators (2px accent border) in stylesheet
- Wire Ctrl+=/-/0 zoom shortcuts for manuscript editor (8-36pt range)
- Add UI font size control (12-18px) in Settings > Editor tab
- Persist theme and font size preferences across sessions
- Set NoFocus policy on decorative labels to prevent keyboard traps
- Add logical tab order for title bar and status bar widgets
- Fix theme.set_theme() to accept both strings and ThemeTokens objects

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
@ErikChevalier ErikChevalier changed the title Fix theme responsiveness, data persistence, and UI polish Fix theme responsiveness, data persistence, accessibility, and UI polish Sep 16, 2026
- Replace Qt.UserRole with Qt.ItemDataRole.UserRole in settings.py
  (3 occurrences) to prevent potential AttributeError on strict PySide6
- Remove unused QPushButton import from main.py
- Remove unused QGridLayout import from settings.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Fix OT transform expanding delete to swallow concurrent inserts,
  causing silent data loss in collaborative editing
- Fix case-insensitive Replace All interpreting backslash sequences
  in replacement text as regex escapes (\n, \t, \1)
- Fix regex find match-index using pattern length instead of actual
  match length for variable-length patterns
- Fix CRDT delete-subsumed position to use op2.position consistently
- Skip outline/plan saves when no data exists to avoid unnecessary I/O
- Add regression test for CRDT insert-inside-delete-range

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Fix LAN peer join always failing: include session_id in LAN discovery
  broadcasts so peers can authenticate with the P2P host
- Fix OT transform for insert-inside-delete: expand delete length to
  cover the gap created by concurrent insert, ensuring all originally
  targeted characters are removed
- Add threading lock to P2P _clients dict to prevent concurrent
  modification crashes between accept_loop and broadcast threads
- Cap pending ops list at 100 entries to prevent unbounded memory growth
- Bridge local editor changes to CRDT broadcast via contentsChange signal
  so local edits are actually sent to collaboration peers
- Debounce remote collab disk writes with 2-second QTimer instead of
  synchronous save on every keystroke
- Fix Replace All to use QTextCursor-based replacement instead of
  setPlainText, preserving the undo history stack

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
…e, sync

- Fix collab host race condition: pass actual document content to
  session.host() instead of empty string, preventing early-connecting
  guests from receiving a blank document
- Fix title template duplication: default end index to len(lines) so
  content without blank-line separators replaces the entire title page
  instead of duplicating it
- Fix single Replace regex mismatch: convert U+2029 paragraph separators
  to newlines before regex matching, consistent with Replace All
- Fix DeviceSync class attribute sharing: create per-instance handler
  subclass so concurrent transfer servers don't overwrite credentials
- Fix double stat() calls in project loading: cache stat results to
  halve syscalls on large project folders

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Add threading lock to CollabSession to protect CRDT and pending_ops
  from concurrent access between recv thread and main Qt thread
- Fix _detect_element_type to return 'dialogue' for lowercase-starting
  text, restoring the full Tab cycle through all Fountain elements
- Replace setPlainText with cursor-based text insertion in collab text
  handler to preserve the undo history stack during collaboration
- Simplify tautological ternary in CRDT OT delete-delete transform
- Remove unused home_clicked signal from TitleBar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Add user_id-based tie-breaking for concurrent same-position inserts in
  OT transform, preventing permanent document divergence
- Fix ProofreadPhase issue navigation: use findBlockByNumber (logical
  block index) instead of findBlockByLineNumber (visual line index)
- Prevent duplicate signal connections in collab by calling _on_collab_left
  before connecting new signals in _on_collab_started
- Add concurrency guard to _auto_sync_if_enabled to prevent spawning
  multiple simultaneous git push threads
- Log auto-export failures instead of silently swallowing them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
…and test coverage, and update docs

- Replace 16+ except-Exception-pass patterns with specific exception types
  (OSError, ValueError, json.JSONDecodeError, etc.) and proper logging at
  debug/warning levels across app_state, module_system, session, snapshots,
  remote_sync, dashboard, new_project, and all exporters
- Replace cloud export fake-success stubs with honest _StubExporter base
  that returns False/empty — no more fake URLs or fake auth
- Add 112 new tests across 5 files: plugin API/marketplace, companion
  sync bundles/reader export, name generator, LAN/P2P networking, and
  cloud export config/stubs
- Fix README: correct test count (879), build command (maturin develop),
  add maturin prerequisite, clarify cloud export as scaffolding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
…rrupt config guard

- Add AttributeError/TypeError to collab session message handler to
  prevent thread crash on non-dict JSON from malicious/buggy peers
- Add TypeError to new_project ecrit_core fallback for FFI edge case
- Remove redundant FileNotFoundError (subclass of OSError) in
  remote_sync and snapshots
- Guard load_cloud_configs against corrupt cloud.json files
- Replace vacuous DeviceSync test with meaningful PairedDevice and
  init tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
- Fix OT transform: shift DELETE ops right on concurrent INSERT at same
  position, preventing corruption of shared document text
- Fix pending ops overflow: request full sync instead of silently
  truncating, which caused document divergence between peers
- Fix missing logging import in epub_export that caused NameError when
  export failed
- Fix proofread false positive: exclude uppercase action lines ending in
  sentence punctuation from "character cue with no dialogue" check
- Fix deliver preview: reuse existing _parse_title_page() instead of
  reimplementing with a simpler inline parser that missed fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Replace placeholder comic dialects with three real publisher presets
(DC/Marvel, Dark Horse, Image/Indie) that affect parsed output. The Rust
parser now recognizes PAGE/PANEL headers, SFX, and caption elements when
a comic format_id is active, with per-page auto-numbering of lettered
elements (dialogue, captions, SFX share a counter that resets per page).

Add comic-specific node types (Page, Panel, Spread) to the outline canvas
with distinct visual rendering and format-aware context menus. Add three
comic structure templates (22-Page Issue, 5-Page Short, GN Chapter).

Build bidirectional outline-manuscript sync: "Generate Script" converts
outline nodes into a Fountain skeleton, "Import Script" parses the
manuscript back into outline nodes. Works for both comic and screenplay
formats.

94 new tests (50 comic fountain + 44 comic outline), 976 total passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
@ErikChevalier ErikChevalier changed the title Fix theme responsiveness, data persistence, accessibility, and UI polish Add comic book Fountain extensions, outline-manuscript sync, and quality fixes Sep 17, 2026
Export pipeline (8 fixes):
- Thread format_id from UI _export_script() to all three exporters
- Thread format_id through ODT pipeline (export_odt, _create_odt_bytes, export_odt_to_path)
- Add ecrit_core parsing to EPUB exporter for comic element support
- Fix double HTML escaping in EPUB Sfx/Caption rendering
- Fix PDF PageHeader uppercase overwrite that undid style-requested casing
- Add format_id to export_pdf_to_path()
- Fix PDF normal_fmt hardcoding 12pt instead of using font_size param
- Fix ODT mimetype entry using ZIP_DEFLATED instead of ZIP_STORED per ODF spec

Outline sync (9 fixes):
- Normalize node IDs to strings everywhere (templates, sync, UI all match now)
- Fix Spread nodes lost during script-to-outline roundtrip (fallback parser)
- Guard against None label/synopsis values in outline_to_script
- Fix overlapping node positions when Page+Spread share same page number
- Remove dead connections variable in generate_outline_nodes
- Cap accumulated synopsis to 200 chars in comic script_to_outline
- Convert recursive _topo_sort to iterative stack to avoid RecursionError
- Fix comic context menu page count to account for Spread = 2 physical pages
- Initialize _format_id on EditorScreen.__init__

Rust parser (3 fixes + cleanup):
- Add balloon number to Dialogue elements in comic mode
- Fix VOICE OVER caption subtype normalization with multi-space input
- Replace bare unwrap() with proper error handling in PyO3 bindings
- Remove dead TitlePage element variant and TitlePagePair struct

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
The CI pipeline doesn't build the Rust extension, so
test_comic_fountain.py needs to use pytest.importorskip instead of a
bare import. This matches the pattern already used in
test_comic_outline.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
Remove the marketplace command palette entry, dialog instantiation,
and related handlers from the main window. The backend code and overlay
module remain intact for when the remote registry is implemented.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw
@ErikChevalier
ErikChevalier marked this pull request as ready for review September 20, 2026 23:17
@ErikChevalier
ErikChevalier merged commit 3897456 into main Sep 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants