Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fountain export: round-trip synopses, sections, inline notes, meta.extra #188

Description

@stultus

Part of #184 (Fountain import). Spec findings posted in the umbrella's research comment — read that first.

What

Update src-tauri/src/screenplay/fountain.rs so that data preserved during import (synopses, sections, inline notes, meta.extra) is re-emitted on export, AND apply the forcing rules required to keep round-trip lossless against Fountain's parsing precedence.

Forcing rules (critical — without these, round-trip silently corrupts)

Fountain's auto-detection misreads stored elements unless we preemptively force them:

  • Character — must @-prefix when the name:
    • contains any Malayalam, Tamil, Devanagari, or other caseless-script character (caseless scripts can't satisfy the all-caps rule)
    • contains any lowercase Latin letter (e.g. iPhone Voice, Mary)
    • is otherwise not all-uppercase Latin (digits + parens + spaces are fine, but bare digits aren't valid)
  • Action — must !-prefix when the line:
    • is entirely uppercase Latin and would auto-detect as a Character (caps + alpha + non-empty next line)
    • is entirely uppercase Latin and ends in TO: (would auto-detect as a Transition)
    • begins with INT, EXT, EST, INT./EXT, INT/EXT, or I/E (would auto-detect as a Scene Heading)
  • Scene Heading — must .-prefix when the heading text doesn't begin with a recognised slug prefix (INT, EXT, EST, INT./EXT, INT/EXT, I/E). Common case: Malayalam scene slugs.
  • Transition — must >-prefix when the transition text isn't all-caps Latin ending in TO:. Includes any Malayalam transition.

Implement once as a force_prefix_for(node, text) helper used by every emitter.

Element emission

Source Emitted as
meta.extra map Title-page keys at top of file, sorted (BTreeMap key order). Original key spelling preserved.
scene_cards[i].description = description synopsis line(s) directly after scene i's heading. Multi-line descriptions → multiple = lines.
scene_cards[i].notes lines prefixed [[#section depth=N]] ... # ... / ## ... / etc. emitted before scene i's heading, stripping the marker.
scene_cards[i].notes lines without a section marker [[ ... ]] inline note inserted at the end of scene i's last action block (positional approximation — original anchor was lost on import)

Boneyard is never emitted (dropped on import).
Dual dialogue is never emitted (collapsed on import).
Custom scene numbers are not emitted (Scriptty auto-numbers).
Emphasis markers are not emitted (stripped on import — v1 data loss).

Whitespace gotchas

  • Two-space-trailing convention in dialogue: when emitting a multi-paragraph dialogue block, separate paragraphs with a (two-space) line, not a fully blank line. Otherwise the next reader will end the dialogue block.
  • Use \n line endings (LF), not CRLF.
  • Don't trim trailing whitespace blindly — let the emitter own which lines need the two-space convention.

Considerations / heads-up for the PR

  • The section-depth marker syntax ([[#section depth=N]] ...) is internal — choose something unlikely to collide with real user notes. Document the convention in SCREENPLAY_FORMAT.md.
  • Synopses currently exist as a scene_cards.description field that users also edit by hand — this issue means user-authored descriptions also get emitted as Fountain synopses. That's the right behavior (Fountain readers will show them in outline mode), but worth calling out: scene card descriptions become semi-public on Fountain export. Mention prominently in the Export modal copy.
  • If a user adds a scene_cards[i].notes entry by hand it'll be emitted as an inline note at scene end. Existing export behavior changes for non-imported files too — call this out in the PR description.

Test plan

  • Round-trip: same fixtures from Fountain parser (Rust): .fountain → ProseMirror JSON + meta + scene_cards #186. import → export → import → export must reach a fixed point after one full round.
  • Snapshot tests on emitted Fountain for canonical inputs.
  • Targeted forcing tests:
    • Malayalam character cue (രമേശ്) → @രമേശ് on export
    • Mixed-case character (iPhone Voice) → @iPhone Voice
    • Action line BANG!!BANG!
    • Action line HE WALKS UP TO:!HE WALKS UP TO:
    • Action line INT.MISLEADING!INT.MISLEADING
    • Malayalam scene heading അകത്ത്. വീട് - രാത്രി.അകത്ത്. വീട് - രാത്രി
    • Malayalam transition → > prefix

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions