Commit Graph

85 Commits

Author SHA1 Message Date
landon d29aa664da Parse a scene's summary as a real list of bullet points
A frontmatter `summary:` list written as a multi-line flow sequence
(one bullet per line, closing bracket on its own line — the natural
way to hand-write several sentences) used to vanish: no line in it
has a colon or a leading `-`, so the parser read none of the lines
after the opening `summary: [` and the field ended up holding a
literal "[". Even written on one line, `flow_list_items` split on
every comma, which cuts a prose bullet in two if it has one of its
own.

`Header` now has a `summary: Vec<String>`, read by a parser that
joins a flow sequence's continuation lines before splitting it on
commas outside of quotes, so each bullet survives whole. Also added
`summary: []` to the bundled scene template and the in-app default
new-file template, so new scenes offer the field.

No new UI surfaces this yet (corkboard/synopsis still show only the
slug) — out of scope for the parsing fix itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-05 20:00:36 -05:00
landon 3d6e134a33 Lock the scene template against accidental edits
_template.md is a reference writers copy from by hand, not something
the app reads programmatically, so an accidental edit to it is easy
to make and easy to miss. The editor now shows it read-only with a
lock banner; an "Unlock" button allows editing for the rest of the
session, resetting back to locked the next time a file is selected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 10:46:52 -05:00
landon a700eed63c Refresh Story Bible reference ranges when the header folds or unfolds
Folding/unfolding the header moves the frontmatter in or out of
`buffer`, shifting every char offset into it, but after_view_change()
never recomputed current_references — so the cached ranges stayed
pointing at pre-toggle positions and highlights landed on unrelated
words. Recompute them alongside the other view-state invalidations it
already does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 15:32:17 -05:00
landon 80f3d50fc4 Stop the file tree from centering short folder/file names
ui.add_sized() centers a widget narrower than the box it's given, and
Button/SelectableLabel size themselves to their actual text — so a
short name in a wide column (a folder with no trailing badges, most
visibly) drifted away from its own indent, landing to the right of
its own deeper children. Pin main_align to the start so every name
sits right after its indent regardless of length.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 15:32:13 -05:00
landon 8b352d25ed Let the Story Bible Editor rename/move files
Right-clicking a file row now offers "Rename / Move…", mirroring the
manuscript file panel's combined rename-and-move pattern: editing the
name renames, editing the folder part moves it across the tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 14:08:40 -05:00
landon 7e4932176b Let the Story Bible Editor create new entries, not just edit existing ones
Right-click a folder in the tree (a top-level category like
01-Characters, or any subfolder like cities) for "Add <type> entry…" —
the type is guessed from the folder's own name (singularized: cities
-> city, Objects -> object), editable afterward like any other field.
Names a file, writes minimal id/name/aliases/summary/type/status
frontmatter, and loads it straight into the editor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 08:51:33 -05:00
landon 6627ae8000 Give the User Guide real breathing room between paragraphs
Every paragraph, note and entry was a separate label stacked with only
egui's default item spacing between them, so sections didn't read as
clear breaks. Scope a larger item_spacing to the User Guide's own Ui
(not the shared section/note/body helpers) so the Markdown Cheatsheet,
which uses the same helpers, is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-03 08:07:06 -05:00
landon 1cd560d954 help documentation 2026-09-30 17:48:03 -05:00
landon 6134cc90ff story bible resize 2026-09-30 17:21:51 -05:00
landon 2f3780a578 Give Misc Scenes a stub file so it actually shows in the file panel
The panel infers folders from markdown file paths; a folder with only
a .gitkeep never gets a row. Act 1/2/3 avoid this with their own
scene01.md stubs — Misc Scenes was missing one, so new projects
shipped it invisible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-30 12:22:14 -05:00
landon ab009e0a19 Let a folder be excluded from export without hiding it
Misc Scenes (05-First Draft) is a dumping ground for scenes written
but not yet placed in the book. It should stay visible, editable and
counted everywhere in the app, but never end up in an exported
document. hidden_folders/archive_folder both hide a folder outright,
so add a narrower export_exclude_folders setting (defaults to
["Misc Scenes"]) that only the three export paths honor, with chapter
numbers renumbered afterward so excluded scenes don't leave gaps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-30 11:53:48 -05:00
landon 7854a961a9 Bundle the project template into the binary; add outline-to-draft
links, a sprint timer, placeholder scanning, corkboard, and a
quick-capture inbox

- Compile templates/snowflake into the binary via include_dir, so New
  project… never depends on files installed beside the executable;
  Settings ▸ Template becomes an optional external override, and a
  broken saved override now falls back to the bundled template
  instead of failing project generation.
- Outline beats now link to a matching chapter for a one-click jump.
- Sprint window: a daily whole-manuscript word goal plus a
  Pomodoro-style writing sprint timer, with a compact countdown in the
  status bar while in Distraction-free mode.
- Placeholders window: find [TODO]/[TK]/[FIXME]/[XXX]/[NOTE]/[???]
  markers left in the draft and jump straight to any of them.
- Snowflake progress strip in the file panel: a nudge-only readout of
  which snowflake stage folders actually have content.
- Corkboard window: drag-reorderable synopsis cards, one per chapter.
- Quick-capture popup (Ctrl+Shift+N): jot a stray idea straight into
  the project's Scratch Pad without losing your place in the draft.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-30 11:38:31 -05:00
landon 6ca8681d08 Add workspace search, synopsis view, snapshot diffs, typewriter mode, and a writing streak
Five productivity features: search/replace across the whole workspace
(with a safety commit before Replace all), a Synopsis window compiling
every chapter's slug, a Diff button per Auto-Snapshot to compare against
an older snapshot instead of only HEAD, typewriter-style cursor centering
and paragraph dimming gated on Distraction-free mode, and a per-day
word-count streak persisted to word_log.json alongside order.json/titles.json.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 15:55:15 -05:00
landon 229e2413c4 Add summary: to character templates; fix two bugs that were hiding it
Add a summary: field (with a short explanatory comment) to
01_character_template.md, and fill the field in on the two shipped
examples, 01_protagonist.md and 02_antagonist.md. Documented in the
Story Bible README's "Adding a New Character" example and its new
"Automatic Linking in the Draft" section, and in the main README's
Story Bible docs.

While wiring this up against the real bundled template rather than a
hand-written test fixture, found two bugs that were silently defeating
last session's auto-linking feature for every project made from that
template:

- extract_from_frontmatter() only read a title: field for an item's
  name, but every bundled Story Bible template — character, location,
  lore, object, all of them — writes name:, never title:. Every real
  item's display name (and therefore everything matched against it in
  prose) was falling back to a garbled, uncapitalized filename guess
  instead. Now name: is read (title: still works too, for a sheet that
  uses that word instead).

- build_editor_job()'s fast path skipped straight to an unformatted
  run whenever there were no grammar/spelling issues and no active
  search — without checking whether there was a Story Bible reference
  to highlight. A name with no spelling issue of its own (which is
  most of them) never got its purple background, and with it no visible
  way to find the hover card that depends on it.

Both were invisible in the unit tests added last session because they
exercised find_references() directly against a hand-built index, never
through StoryBibleIndex::build() against real files, and the manual
smoke test happened to use a name ("Bixby") that the spell checker
flagged anyway — which incidentally kept the fast path from short-
circuiting. Added a disk-built-index test and a fast-path regression
test so both stay caught.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 11:32:56 -05:00
landon 0da52c3054 Fix Story Bible Editor's content panel rendering a sliver wide
ui.set_width() was called on the shared horizontal-layout Ui before the
tree and editor panels were split off, so it shrank the whole row —
tree, separator, and editor panel together — down to a third of the
window's width instead of just the tree column. The editor's own
heading (the open item's path) was narrow enough to wrap one letter
per line as a result. Moving the width constraint onto the tree
panel's own child Ui, where it belongs, leaves the editor panel free
to take the rest of the row.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 11:10:02 -05:00
landon ab9aa2afce Auto-link Story Bible mentions in the draft, with a hover card to the entry
Bare proper nouns in prose ("Bixby walked in") now resolve against the
Story Bible automatically, the same as the existing [[wiki-links]] and
@mentions markup — no manual linking required. Matching is case-sensitive
and requires a capitalized, 3+ character match to a sheet's name or alias,
which is what keeps a character named "Hope" or "Will" from lighting up
on every unrelated use of the word; the longest phrase wins an overlap
("Bixby Reynolds" over "Bixby"), and a bare match never overrides the
author's own explicit markup.

Hovering a highlighted mention shows a card with the item's name, type
(Person/Place/Thing) and a summary (from a new summary:/description:
frontmatter field), with a link that opens the entry in the Story Bible
editor. The card appears after a short delay and stays open while the
pointer crosses onto it to reach the link.

Along the way:
- StoryBibleReference now carries absolute byte offsets (start/end) and
  the resolved item_id, replacing column/length, which were actually
  per-line and were being used as whole-buffer offsets — every reference
  past a file's first line highlighted and jumped-to in the wrong place.
  find_references() also now walks the buffer one char at a time rather
  than one byte, which could previously land mid-character and panic on
  any manuscript with accented letters or an em dash.
- build_story_bible_index() had its own, separately hardcoded list of
  Story Bible directory names that had drifted from
  find_story_bible_directory()'s list and no longer included
  03-Story-Bible, the bundled template's actual folder — so the index,
  and everything built on it, was silently empty for every project made
  from that template. It now reuses the one list.
- Story Bible Editor window: its tree and content ScrollAreas (and the
  content TextEdit) had no id_salt, so egui treated them as the same
  widget — fixed with explicit salts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 11:01:54 -05:00
landon ef8032b300 Remove orphaned template folders and the dead create_scene.py script
07-Revisions/08-Final/09-Covers/10-Archive were leftovers from before
the stage renumbering to 06-Revisions/07-Final/08-Covers/09-Archive —
identical content under a second set of numbers that every new
project was scaffolding twice.

scripts/create_scene.py (and its README) hasn't been runnable since
project_template.rs stopped executing template hooks — the native
"+ New Scene" button replaced it — and its docs already pointed at
folder names (05-Plot, 06-First Draft) that predate the current
layout. Dropping it removes a Python-interpreter dependency the app
itself never used.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 10:39:41 -05:00
landon 0f144737a5 Finish bundled-template migration; simplify frontmatter; de-duplicate toolbar/menu
Completes the move off the external cookiecutter tool: drops
src/cookiecutter.rs and the cookiecutter_bin/Template-path setting now
that scaffolding always runs through the built-in MiniJinja engine,
and rewrites default_project_template() to walk up from the executable
without panicking when the bundled template can't be found. Rounds
out the snowflake template with the remaining 06-Revisions,
07-Final, 08-Covers and 09-Archive folders.

Simplifies the scene/chapter YAML frontmatter (drops type/word_count_min/
word_count_max in favor of status + a flat characters list) and updates
the new-file template, header parsing, outline, file list and Story
Bible editor to match.

Separately, de-duplicates the toolbar and menu bar: settings that only
existed as always-visible toolbar controls (draft marker, zero-pad,
spell-check toggle, dictionary picker) move into their Settings
windows, toolbar buttons that exactly duplicated a menu item are
dropped, and toolbar-only actions with no menu path (open project
folder, git sync/init) gain one under File. Also fixes two ambiguous
menu labels: the Story Bible editor now reads "Story Bible editor…"
next to "Story Bible…" in Tools instead of sharing a label with Edit,
and Settings' "New project…" is now "New-project settings…" so it
doesn't read like File's actual create action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 10:35:52 -05:00
landon c61a7b605a fix: Add effective_project_template() to fallback to bundled template
- Add effective_project_template() method that checks if saved template path exists
- If not, falls back to default_project_template() (bundled template)
- This handles users who have old config.json pointing to deleted Cookiecutters path
- Update project.rs to use effective_project_template() when creating new projects

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 18:49:46 -05:00
landon 3392b7e4d3 fix: Start walking from executable directory, not file path
The previous fix started walking from the executable file itself, but we need to
start from its parent directory. Changed:
  let mut current = exe_path.as_path();
to:
  let mut current = exe_path.parent().unwrap_or_else(|| exe_path.as_path());

This ensures we check directories, not the file itself.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 18:45:01 -05:00
landon 341202f87c fix: Improve bundled template path resolution and add tests
- Walk up directory tree from executable to find templates/snowflake
- Handles various build configurations (release, debug, cargo run, installed)
- Add test to verify bundled template path contains 'templates/snowflake'
- Add test to verify template has 05-First Draft (merged) and not old 05-Plot/06-First Draft

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 18:38:03 -05:00
landon 2471312f05 feat: Remove fallback to Cookiecutters, use bundled template only
- Remove fallback to ~/Documents/Cookiecutters/snowflake from default_project_template()
- Panic with clear error if bundled template not found
- Update tests to use 05-First Draft instead of 06-First Draft
- Update comments to reflect merged structure (05-First Draft contains planning+draft)

The code now only uses the bundled template at templates/snowflake/ relative to the
executable, ensuring all new projects use the merged 05-First Draft structure.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 18:27:54 -05:00
landon aab943de6b manual renumbering of template files 2026-09-16 18:14:17 -05:00
landon 114f4ff697 docs: Update all references from old 05-Plot/06-First Draft to merged 05-First Draft
- Update project_template test to verify old directories are NOT created
- Update hover text and comments in project.rs
- Update comments and code in file_list.rs
- Update comment in spelling.rs
- Add test assertions to verify 05-Plot and 06-First Draft don't exist in new projects

This ensures all code references match the merged directory structure where
planning and draft content are combined in 05-First Draft with YAML frontmatter.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 15:34:36 -05:00
landon 0583bc077c feat: Add aliases support to Story Bible for flexible linking
- Add aliases field to StoryBibleItem struct
- Add items_by_alias HashMap to StoryBibleIndex for quick alias lookup
- Add find_by_alias() method and update find_by_partial_name() to search aliases
- Parse aliases from YAML frontmatter (list and comma-separated formats)
- Update find_references() to match aliases in addition to IDs and names
- Fix @mention pattern matching to properly stop at spaces
- Add comprehensive tests for aliases functionality
- Update README.md with aliases documentation and usage examples

This allows Story Bible entries to have alternative names that can be
referenced in draft text using wiki-style [[...]] or mention-style @ syntax.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 14:18:57 -05:00
landon a0d1c3c206 feat: Remove ### Rough Draft: marker, use YAML frontmatter only for draft files
- Everything after YAML frontmatter closing --- is now considered the draft content
- No ### Rough Draft: marker needed
- Chapter titles are handled by the program on export based on file order
- Draft content should not include chapter titles
- Updated default_new_file_template to use YAML frontmatter
- Updated preprocess.rs to parse YAML frontmatter for metadata
- Updated all relevant tests
- Updated documentation in help.rs
- Updated template files

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 13:33:23 -05:00
landon aafe1786b5 feat: Add auto-snapshot feature with git commit every N words
- Add new module src/app/auto_snapshot.rs for auto-snapshot functionality
- Add configuration options: auto_snapshot_enabled (default: false) and
  auto_snapshot_threshold (default: 250 words)
- Automatically create git commits when word count crosses threshold boundaries
- Add 'Edit -> Snapshots' menu option to open snapshots window
- Snapshots window shows: enable/disable toggle, threshold setting, current
  word count, next snapshot target, list of all snapshots with rollback buttons
- Manual snapshot creation via button in snapshots window
- Rollback to any previous snapshot with one click
- Snapshot info includes: word count, timestamp, commit message, commit hash
- Make run_git in gitsync.rs public for use by auto_snapshot module
- Update README.md with Auto-Snapshots documentation

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 12:11:35 -05:00
landon dcb97da518 feat: Add Story Bible breakout editor window
- Add new module src/app/story_bible_editor.rs for dedicated Story Bible editing
- Add 'Edit -> Story Bible' menu option to open the editor
- Tree view (left 1/3) shows Story Bible directory structure with collapsible folders
- Editor (right 2/3) allows editing selected Story Bible items
- Auto-load first file on open, support Ctrl+S to save
- Rebuild Story Bible index after saving to pick up changes
- Update README.md with Story Bible Editor documentation

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 11:21:15 -05:00
landon 55bf006216 docs: Update README for merged 05-First Draft with YAML frontmatter
- Update all references from 06-First Draft to 05-First Draft
- Remove outdated reference to 05-Plot
- Add new section explaining YAML frontmatter support for planning metadata

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 10:08:20 -05:00
landon 449ad4439f feat: Merge 05-Plot and 06-First Draft into 05-First Draft with YAML frontmatter
- Add YAML frontmatter support with split_yaml_frontmatter() in preprocess.rs
- Update create_scene_pair to create single merged files with YAML frontmatter
- Rename 06-First Draft to 05-First Draft and merge with 05-Plot content
- Update default_project_open_subdir to '05-First Draft'
- Add capitalizes filter to MiniJinja environment for template rendering
- Fix template placeholder escaping in CONSISTENCY.md
- Update all references from 06-First Draft and 05-Plot to 05-First Draft
- Update test expectations to match new folder structure

The merged approach uses YAML frontmatter (--- delimiters) at the top of
each draft file to store planning metadata, allowing it to be collapsed
in the editor while keeping all planning and draft content in one file.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 10:06:32 -05:00
landon 477c59cb79 docs: Add Story Bible documentation to README
- Document Story Bible feature in the Tools section
- Add Story Bible to the reference material cross-reference
- Describe reference syntaxes (wiki-style and mention-style)
- Document highlighting and navigation features

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 08:58:13 -05:00
landon ca3e476a14 feat: Implement bidirectional Story Bible links with draft text
- Add story_bible.rs module with StoryBibleIndex, StoryBibleItem, and
  StoryBibleReference types for indexing Story Bible content
- Support wiki-style [[...]] and mention-style @... syntax for references
- Highlight Story Bible references in editor with purple background
- Add Story Bible window (View > Tools > Story Bible) to browse items
- Show references from current draft file in Story Bible window
- Allow clicking references to jump to their location in the editor
- Auto-index Story Bible items from common directories (03-Characters,
  02-Story Bible, Characters, etc.)
- Extract metadata from YAML frontmatter or filenames
- Add walkdir dependency for directory scanning
- Fix pre-existing format string errors in workspace.rs

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 08:56:28 -05:00
landon 511a7588fa docs: Update README with heading-based nesting outlines and changelog
- Updated Outline section to document heading hierarchy preservation
- Added Changelog section documenting latest features
- Documented linked scene/chapter templates
- Documented automatic workspace refresh

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 14:58:48 -05:00
landon cd8663349a feat: Implement heading-based nesting outlines in Outline window
- Add depth and path fields to Beat struct to track heading hierarchy
- Rewrite beats() parser to use heading stack and process_headings() helper
- Add BeatTree enum with Heading and Beat variants for tree structure
- Add build_beat_tree() to convert flat beats to nested tree
- Add render_beat_tree() for recursive tree rendering with collapsible headers
- Add count_beat_tree() helper to count beats in subtree
- Replace flat list rendering with tree-based rendering in outline_window()

The implementation currently groups beats by section, with infrastructure
in place for full hierarchical nesting based on beat.path.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 14:56:35 -05:00
landon 921f3e74c5 feat: Add 'New Scene' button to file list menu for creating linked planning+draft files
- Added create_scene_pair() method to workspace.rs
- Added '+ New Scene' button in file_list.rs UI
- Button creates both planning file (05-Plot/act_N/scene_XX.md) and draft file (06-First Draft/Act N/sceneXX.md)
- Files are automatically linked via YAML frontmatter (draft_file and planning_template fields)
- Auto-detects current act from folder context
- Auto-generates next scene number
- Selects the draft file after creation (ready to edit)
- Includes hover text explaining the feature

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 14:27:30 -05:00
landon b183237d59 feat: Add linked scene/chapter templates with planning-draft file references
- Updated Plot scene template with YAML frontmatter for metadata tracking
- Updated First Draft scene template with YAML frontmatter
- Added _template.md files in both Plot and First Draft folders
- Added create_scene.py script for automatic file creation and linking
- Planning files reference draft files via 'draft_file' field
- Draft files reference planning files via 'planning_template' field
- Word count targets tracked in planning, current count in draft
- Added comprehensive scripts/README.md with usage guide
- Supports --from-planning and --from-draft for creating missing counterparts

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 14:20:44 -05:00
landon d311e87836 feat: Transform 03-Characters into comprehensive Story Bible
- Renamed 03-Characters to 03-Story-Bible with expanded structure
- Added enhanced character templates with YAML frontmatter for metadata
- Created new sections: Locations, Lore, Objects, Relationships, Timeline, Themes
- Added templates for: regions, cities, buildings, historical events, factions
- Added templates for: artifacts, weapons, character relationships
- Added world rules document for magic/technology/society tracking
- Added CONSISTENCY.md for continuity tracking
- Added comprehensive README with usage guide
- Added index files for each section with cross-linking
- Implemented wiki-link syntax [[file]] for cross-references
- Added Mermaid diagram support for relationship maps and timelines
- Tagging system for filtering and organization

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 13:58:56 -05:00
landon fbccad3a86 ui: Limit file name display length and ensure left alignment in file list
- Add MAX_DISPLAY_NAME_LEN constant (30 characters)
- Add truncate_display_name helper to truncate with ellipsis
- Apply truncation to folder names and file names
- Apply truncation to context menu file names
- Maintain left alignment through existing layout

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-14 16:13:43 -05:00
landon f64536ee9e feat: Add automatic workspace refresh with file watcher
Implement a polling-based file watcher that automatically refreshes
the file list when markdown files are created, deleted, or renamed.

- Add background thread that polls workspace every 2 seconds
- Watcher starts when workspace opens, stops when app exits
- Uses pure Rust std library (no external dependencies)
- Fixes issue where new folders like Act 2 don't appear until manual refresh

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-13 18:41:57 -05:00
landon cea944779e Add outline popup window for folder context menu
Implement the outline file popup window that opens when a user
right-clicks on a folder in the file list (e.g., Act 1 in 06-First Draft)
and selects 'Outline' from the context menu. The window displays the
corresponding outline file (e.g., 04-Outline/scene_breakdown/act_01.md)
in a read-only view.

Changes:
- Add outline_popup_window() function in file_list.rs
- Add window rendering call in mod.rs update loop
- Popup reads file content on open and displays it in scrollable view

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-13 10:47:11 -05:00
landon 9f575c7c7e feat: Ensure 06-First Draft folder is expanded in file list when opening projects
- Add explicit code in open_workspace to ensure manuscript folder is not collapsed
- Update README to clarify that project root is opened with manuscript folder expanded
- Keep workspace at project root but ensure file list shows 06-First Draft expanded

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-13 09:08:50 -05:00
landon caa0001922 feat: Replace cookiecutter with built-in MiniJinja templating
- Add MiniJinja dependency for Jinja2-compatible templating
- Create new project_template.rs module for template rendering
- Bundle snowflake template in templates/snowflake/ directory
- Remove dependency on external cookiecutter Python tool
- Handle Gitea publishing natively in Rust instead of Python hooks
- Update default template path to use bundled template with fallback
- Update README to reflect new built-in templating
- All tests pass

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-13 08:37:18 -05:00
landon 981cd853b9 Cleanup: Remove test push file
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-10 15:28:17 -05:00
landon e77d29047c Test: Verify push access from Mistral Vibe
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-10 15:28:02 -05:00
landon d6ccf12f51 Don't report a first sync as failed
A repository being pushed for the first time has no upstream, so the
pull leg failed with "There is no tracking information for the current
branch" and dragged the whole sync's status down — the app said "Sync
finished with errors" even though the push had gone through and set the
upstream itself.

The upstream is now checked up front. Without one there is nothing to
rebase onto, so the pull is skipped with a note in the log and the push
sets the upstream directly, rather than being run once to fail and then
retried. The argument choice moves to push_args so it can be asserted
without a repository on disk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-09 19:41:21 -05:00
landon c42f11b86c Point a created repo's SSH URL at a host we can reach
Gitea builds ssh_url from its own SSH_DOMAIN, which is whatever the
server was told to advertise. A server announcing a public hostname
whose SSH port is only open on the LAN hands out a remote that connects
from nowhere — and because "Create and attach" does not push, the
failure surfaces later, at the first Sync:

    ssh: connect to host <wan-name> port 2228: Connection refused

The app already knows an address that works, having just made an API
call over it, so the SSH host is taken from the configured server URL
instead. The port Gitea reports is kept, as are the user and path, and
both URL shapes are handled (ssh://user@host:port/path and the scp-like
user@host:path). Setting the server to a tailnet address therefore gets
a remote that works on and off the LAN alike.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-09 19:36:15 -05:00
landon c4391155cd Create the Gitea repository from the app, not a template hook
Repository creation was delegated entirely to a cookiecutter template's
post-gen hook. That only ever covered projects made through New project,
and when it went wrong it went wrong inside someone else's Python, where
all this app could report was that a hook exited non-zero.

The app now talks to Gitea itself. Settings > Git remote... gains a
"Create and attach" button: it POSTs to /api/v1/user/repos with the
configured server, user and token, then points origin at the clone URL
that comes back. A name that is already taken is fetched and attached to
rather than raised as an error, so retrying after a failure works.

The call runs on a worker thread with the existing channel-and-poll
pattern, so the UI does not block on the network. Errors are mapped by
status code, because a rejected token, a URL that is not a Gitea API and
a name the server refuses each need a different fix.

Uses ureq's send_string rather than send_json, whose feature this build
does not enable; no new dependency, and ldd still shows only
libc/libgcc/libm.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-09 18:13:21 -05:00
landon 91718f7487 Let the git remote be set from Settings
Attaching a workspace to an existing repository meant leaving for a
terminal: gitsync could init, commit and push, and could tell whether an
'origin' existed, but had no way to create one, and nothing in the
settings held a remote URL. Sync just logged "no 'origin' remote
configured" and committed locally.

Settings > Git remote... now shows the workspace repository's origin and
lets it be set, repointed or cleared. The URL is read from and written
to the repository, never to config.json, because a remote belongs to the
checkout — so each workspace shows its own, and the box is refreshed
whenever the repository in play changes.

This is separate from the Gitea publishing in New project, which is a
cookiecutter post-gen hook and only ever applies to generated projects.

Also factors out first_output_line, which repo_root and the new
origin_url were both open-coding, and origin_args, so the add-vs-set-url
decision can be tested without a repository on disk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-09 17:54:27 -05:00
landon aa050cf411 Add a right-click context menu to file rows
Renaming, archiving and deleting a file all lived in buttons under the
list, and all of them required selecting the file first. The same
operations are now on the row itself: right-click a file for Rename,
Copy path, Archive and Delete.

The menu records what was picked rather than acting on it, because the
list is drawn inside a closure still borrowing `self` — the same
deferred pattern the click and drag-drop handling already use. Right-
clicking selects the row first, so the menu always acts on the file you
clicked; selecting is a no-op when it is already current, so this cannot
reload the buffer out from under an unsaved edit.

Rename opens a focused dialog with the path pre-filled, and stays open
if the name is rejected so a clash does not cost what was typed;
rename_selected reports success for that. Delete asks first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-07 11:57:49 -05:00
landon 70046b0382 Add a Ctrl+/ whole-line comment toggle to the editor
The app already treats `<!-- ... -->` as editorial notes: preprocess
strips them from the export and the word count. Ctrl+/ makes that a
one-key move, so a paragraph can be shelved without being deleted.

It grows the selection out to line boundaries first, and only toggles a
block back off when every non-blank line in it is already commented.
Blank lines are passed through so paragraph breaks survive the round
trip, and pressing it on an empty line opens `<!--  -->` with the caret
inside.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-09-07 11:03:02 -05:00