Repository navigation
Add comic book Fountain extensions, outline-manuscript sync, and quality fixes - #6
Merged
Merged
Conversation
…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
- 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
…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
- 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
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
marked this pull request as ready for review
September 20, 2026 23:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Comic Book Fountain Extensions
fountain+comic-dc), Dark Horse (fountain+comic-dh), Image/Indie (fountain+comic-indie) — that actually affect parsed output.Outline Node Graph — Comic Nodes & Sync
Adversarial Review Fixes (ef481ee)
format_idnever reaching exporters from UI; fixed PDF using hardcoded font size instead offont_sizeparameter; fixed EPUB crash onBlankLine/PageBreakelements; removed double-escaping of SFX/Caption in EPUB; fixed ODT mimetype compression per ODF spec.str); added Spread pattern recognition to fallback parser; guarded againstNonelabels; capped synopsis accumulation; converted recursive topo-sort to iterative.str.unwrap()with proper error propagation._format_idinitialization; fixed page count to account for Spread elements.Quality & Polish (from earlier commits)
Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_01NoacdztkMbQ5BREp9rVqNw