Commit Graph

21 Commits

Author SHA1 Message Date
landon 5acda8c097 feat: Add file panel width persistence to config
- Add file_panel_width field to Config struct with default of 260.0
- Use stored width as default_width for the file list panel
- Update config when panel is resized by dragging separator
- This ensures panel maintains its width across sessions and only
  changes when user explicitly drags the separator

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-17 08:02:41 -05:00
landon e033579057 fix: Use if show_files with show() instead of show_animated() for proper width persistence
This changes the file list panel to use a conditional if statement with
.show() instead of .show_animated(). This ensures the panel is only created
when visible, avoiding any state conflicts from show_animated that were
causing the panel to rebound to a default width after resizing.

Note: This removes the slide animation when showing/hiding the panel.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-17 07:27:24 -05:00
landon 10ad7644a2 Revert: Back to show_animated, remove row_width cap
Trying show_animated again with width_range(260-600) and no row_width cap.
The width_range should prevent infinite expansion while allowing resize.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:28:58 -05:00
landon 840e70523e fix: Use if show_files with show() instead of show_animated()
This changes the file list panel to use a conditional if statement with
.show() instead of .show_animated(). This should improve width persistence
by ensuring the panel is only created when visible, avoiding any state
conflicts from show_animated.

Note: This removes the slide animation when showing/hiding the panel.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:21:43 -05:00
landon 1e1344567f fix: Remove default_width, use width_range lower bound as default
Using only width_range(260.0..=600.0) with resizable(true) and no
explicit default_width. The lower bound of the range serves as the default.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:20:16 -05:00
landon be49729af2 fix: Cap row_width to prevent panel from expanding infinitely
Capped row_width at 580.0 to prevent the content from pushing the
panel wider than the user intends, breaking the content-width loop.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:14:39 -05:00
landon 1374af8439 fix: Use width_range(260-600) as default, remove explicit default_width
This uses the lower bound of the range as the default width, which should
work better with the resizable panel.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:09:33 -05:00
landon a2f059652d Revert: Set file list width range to 160-600 (was INFINITY)
The INFINITY upper bound caused the panel to expand to fill the window.
Changed back to a reasonable max of 600.0 pixels.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:08:36 -05:00
landon 2ab78acaa6 fix: Allow file list panel to be resized beyond 460px
Changed width_range from 160.0..=460.0 to 160.0..=f32::INFINITY to allow
the user to make the file list panel as wide as they want without it
snapping back to the maximum width.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 20:02:41 -05:00
landon 43279810a5 fix: Change file list panel from show_animated to show
show_animated may have issues with width persistence when resizing.
Using show() instead should allow the panel to remember its width.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-16 19:01:38 -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 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 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 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 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 f35397b49b Add title pages, standard manuscript format and collapsible panels
Five changes to the export and the window chrome:

* Manuscript details gains a contact field and a "Begin exports with a
  title page" option: the title, the author beneath it, and the contact
  details beneath that, laid out line for line as typed. A chapters +
  master export puts the title page on the master, not on each chapter.

* "Standard manuscript format" lays an export out the way an agent or an
  editor expects a submission: 12pt Courier, double-spaced, half-inch
  first-line indents, a Surname / Title / page header on every page but
  the title page, chapters opening a third of the way down, and `---`
  rendered as the conventional centred `#` scene break. The title page
  becomes the submission kind, with contact top-left and an approximate
  word count top-right. Off by default; margins were already the
  standard 1in on US Letter and are unchanged either way.

* A folder button beside Export ODT opens the project folder in the file
  manager -- the enclosing git work tree, including one still awaiting
  confirmation, since showing a folder is a smaller question than
  choosing which repository to commit to.

* The toolbar and the file list each collapse, from the pair of buttons
  at the right of the menu bar or from the View menu, and Ctrl+D folds
  both away together for distraction-free writing. Both choices persist.

* The editor sizes to its viewport instead of a fixed 30 rows, so the
  height a folded panel gives back reaches the page rather than leaving
  grey space below the text box that did not even take focus.

Two traps worth recording. ODF's style:master-page-name is inherited,
and any paragraph style carrying one forces a page break before every
paragraph that uses it -- a four-line contact block came out as four
pages until Contact_20_Line stopped inheriting from Contact_20_Block;
a test now pins which styles may carry one. And the status bar shares
a function with the toolbar, so guarding that function's top rather
than the top panel alone silently took the word counts away with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYSGPDwkSzhm4qLqjCbqxU
2026-08-25 17:58:59 -05:00
landon 043cc692ac Add project-mode tooling: characters, outline, diff and revision status
Rounds out project mode, where the workspace is the project root and one
subfolder holds the manuscript proper:

- Characters and Outline windows, backed by new `characters` and `outline`
  modules that read the cast from character sheets and measure how much of
  the snowflake outline is actually written.
- Edit ▸ Changes… diffs the open file against its last committed version.
- Revision status, per-file and project word counts, an archive action and
  hidden folders in the file panel.
- Chapter-file export alongside the ODT master, richer header parsing, and
  a project word list for names and invented terms.
- The export path now follows the workspace: opening a project points it at
  that project root, keeping a file name you chose yourself and re-deriving
  one that merely echoed the folder it sat in.
- Clicking an issue in the grammar/spelling panel takes the editor to it,
  selecting the words and centring them; applying a suggestion jumps to the
  rewritten text as well.

README covers the new windows and workflows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bSn3Xijp8GofZVUnRX4oq
2026-08-24 19:56:36 -05:00
landon 41c3f088cd Add nested folders to the file panel and File ▸ New project
Two features that arrived together, since the second depends on the first
to show what it generates.

Nested folders
--------------
`App::files` now holds workspace-relative paths (`part-1/ch-03.md`) rather
than bare names, and the workspace scan recurses eight levels, skipping
dot-directories, `target/` and `node_modules/`. `order::tree_order`
normalises the flat order so every folder's files are contiguous and each
folder sits where its earliest-ordered file put it — which is what keeps
the panel and the export in agreement: the export concatenates the tree
read top to bottom.

The panel draws a collapsible tree. Dragging within a folder reorders as
before; dropping onto a folder header, or among another folder's files,
moves the file on disk and carries its title override, session word
baseline and cached header info with it. Emptied folders are pruned. New
files take a path (`part-1/ch-01`) to create folders, and the Rename box
now holds the whole relative path, so editing its folder part moves the
file.

File ▸ New project
------------------
Scaffolds a project from a cookiecutter template and opens its drafting
subfolder (`06-First Draft`) as the workspace. `cookiecutter.rs` resolves
the executable from PATH and the usual per-user Python prefixes — a
desktop launcher inherits neither a conda PATH nor the tools a template's
hooks shell out to, so the resolved binary's directory is prepended for
the child — builds the non-interactive command line, and identifies the
result by diffing the output directory, which works whatever a template
names its root.

Generation runs off the UI thread because hooks can reach the network.
Settings ▸ New project… covers the template path, the subfolder to open,
the cookiecutter path, a hooks toggle, and the Gitea credentials passed
to hooks as GITEA_URL / GITEA_USER / GITEA_TOKEN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011ZGoPiDuZ7vmryNCJWjYSD
2026-08-23 15:18:22 -05:00
landon b71ffc4f20 Suffix the drop-indicator stroke width to avoid the f32 fallback
egui's Stroke::new takes `width: impl Into<f32>`, so the unsuffixed 2.0
literal had no concrete type to infer from and relied on rustc falling
back to f32. That fallback is being removed (rust#154024), and the
float_literal_f32_fallback lint already warns it will become a hard
error, so spell the type out as 2.0_f32.

No behaviour change - the literal was already compiling as f32. The only
other Stroke::new call site passes an f32 variable and was unaffected.
cargo build is now warning-free; clippy is down to the four long-standing
warnings in odt.rs and order.rs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-08-22 09:47:02 -05:00
landon 8fdb9a4cbd Add a template-backed new-file button to the file list
The file list's "+ New" needs a typed name and seeds only '# <name>',
so every new chapter started from a blank header. Add a second button,
"+ New from template", that takes no name: it picks the first free
untitled-N.md (reusing a gap left by a deleted file), seeds it from a
configurable template, appends it to the manuscript order and selects it.

The template lives in config.json and is edited under Settings > New-file
template..., alongside the existing LanguageTool and Mistral dialogs.
Three placeholders expand at creation time: {{name}} (file stem),
{{marker}} (the configured draft marker, so a template keeps working if
the marker changes) and {{date}} (today's UTC date). Blanking the box
falls back to the built-in default, which seeds the header fields the app
already understands followed by the draft marker.

The date is computed with a local civil-from-days conversion rather than
a date crate, keeping the binary self-contained - ldd still shows only
libc/libgcc/libm.

"+ New" is unchanged; both paths now share insert_new_file. Covered by
13 new tests: placeholder expansion, untitled-N gap reuse, civil date
conversion against known dates, config.json written before this field
still deserializing (Config::load falls back to Default on a parse error,
which would otherwise discard the user's workspace and API keys), and a
round trip proving the seeded header parses back into Title/Slug/POV/goal
via the app's own preprocess::parse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBWj9TphFMCoh7VHaSRnvQ
2026-08-22 09:30:32 -05:00
landon 423e93c894 Split app.rs into an app/ module tree
src/app.rs had grown to 3,700 lines, ~2,400 of them a single impl App
block. Move it to src/app/mod.rs and spread the behaviour across eleven
child modules grouped by feature: workspace, grammar, spelling, beats,
find, editor, autocomplete, file_list, ui, style and util.

The new modules are children of app rather than siblings, so they still
reach App's private fields without widening its interface; methods and
free helpers that are now used across module boundaries are marked
pub(super). mod.rs keeps the state types, App::new and the eframe::App
update loop.

This is pure code motion - every non-blank line of the original file
reappears exactly once, and the only edits are the pub(super) markers,
the module scaffolding, and rewrapping five signatures that the added
prefix pushed past 100 columns. Largest file is now editor.rs at 520
lines. Tests still 56/56, and cargo clippy --release reports the same
five warnings as before the split.

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