Import and export screenplay text
Use CSV for native script records or import the supported Final Draft subset. Keep the source file and review omissions before replacing a script.
A script and a panel caption are separate. Does editing a caption rewrite the script?
Read the dialogue in the source card, the staged caption and the revised caption. The source text stays unchanged. Staging links script records to panels. A later caption edit changes the panel's text, not its original script record.
Exchange script records as CSV
exportScriptCSV(script) exports a ScriptInput or saved ProductionScript, including stable
entry IDs and ordered panel links. importScriptCSV(csv, {id, title}) returns detached
ScriptInput data for replaceScript or a durable script.replace plan:
const current = project.studio.script;
if (!current) throw new Error('Project has no script');
const csv = exportScriptCSV(current);
const incoming = importScriptCSV(csv, { id: current.id, title: current.title });
const plan = project.plan('Import revised script', [{
op: 'script.replace', script: incoming, expectedRevision: current.revision,
}]);The exact header is id,kind,text,speaker,panelIds. Kinds are scene/action/dialogue.
panelIds is a JSON array inside a CSV cell; an empty cell means no links. Empty speaker
means absent, and only dialogue may name a speaker. Commas, doubled quotes and multiline
quoted text are supported; CRLF, LF and CR record separators and an initial UTF-8 BOM are
accepted. Text is not trimmed. Unknown columns, malformed quoting, invalid field types,
duplicate IDs and invalid speakers reject. Existing caption CSV and script CSV share the
same lexical parser but have separate headers and domain validation.
CSV is limited to 2 MiB/1000 entries, with the resulting script still capped at 1 MiB. Script ID/title are explicit import options; revision is deliberately outside the file and must come from the inspected project. Panel links are preserved in exported records rather than inferred from names or text. Missing/deleted panel links and global ID collisions reject when applying to a project. Removing a CSV row removes that entry on full replacement; review the existing field-level change report and preserve the original CSV before committing edits. No panel artwork or timing is regenerated by import. CSV export retains text literally and is a data interchange format, not a spreadsheet formula sanitizer or Final Draft adapter.
Import a Final Draft screenplay subset
inspectScriptFDX(xml) reads FDX version 3 Script documents and returns a source SHA-256,
supported paragraphs and loss records. importScriptFDX(xml, options) converts explicitly
bound records into detached ScriptInput data. Both operations are synchronous and perform no
filesystem/network access or project mutation.
import { inspectScriptFDX, importScriptFDX } from 'codeboard-studio';
const xml = await readFile('screenplay.fdx', 'utf8');
const inspected = inspectScriptFDX(xml);
console.log(inspected.paragraphs, inspected.losses);
// Prepare these bindings from the actual inspection, retaining existing IDs/links on revision.
// Here the source has Action, Character, Dialogue at direct Paragraph positions 0, 1, 2.
const imported = importScriptFDX(xml, {
id: 'script:film', title: 'Film', sourceSha256: inspected.sourceSha256,
bindings: [
{ paragraph: 0, id: 'entry:arrival', panelIds: [] },
{ paragraph: 2, id: 'entry:greeting', panelIds: [] },
],
});
const plan = project.plan('Import screenplay', [{
op: 'script.replace', script: imported.script,
expectedRevision: project.scriptSummary()?.revision ?? 0,
}]);The supported mapping is Scene Heading → scene, Action → action and Dialogue → dialogue. Character establishes the speaker for following dialogue paragraphs; it produces no separate entry. Action, scene headings and other unsupported content boundaries clear that speaker. Parenthetical paragraphs retain the current speaker but their text is omitted and reported. Unconsumed character cues are also reported. Ordered Text runs concatenate without trimming; formatting attributes are reported as losses, even when they look like defaults.
Transitions, parentheticals, custom paragraph types, dual-dialogue containers, nested text markup, scene properties, title pages, headers, revision metadata, layout and other unknown subtrees are not represented in the script model. The report identifies an omitted subtree once, rather than enumerating all of its descendants. Comments and processing instructions other than the XML declaration are also reported. Plain script import does not recreate Final Draft pagination or styling. See Final Draft's description of script elements. The XML layout was checked against an independent implementation's FDX sample; this is implementation evidence, not a vendor conformance specification.
Import rejects any losses by default. lossPolicy: 'report' explicitly permits every listed
omission and returns the loss report beside the script. Inspect the complete report before
using this policy. Preserve the original file; there is no FDX exporter or lossless round trip.
The source hash covers the exact UTF-8 encoding of the supplied string, including whitespace.
A mismatch rejects stale mappings. Every supported paragraph needs exactly one binding;
missing, duplicate or extra bindings reject. Paragraph indices count direct Paragraph nodes,
including unsupported paragraph types and Character cues. They are positions in that exact
source, never persistent IDs: a revised file must be inspected and its bindings reconciled.
The adapter never matches by text, speaker or position across revisions. Existing entry IDs
and panel links must be carried explicitly in the revised bindings.
Full replacement removes entries absent from the import. Review the existing field-level
replacement report and use saved-project plan/commit receipts for durable changes. Global ID
collisions and missing panel links still reject at the project boundary. Import creates no
artwork, panels, timing or audio; use planScriptBoard for explicit staging afterward.
Resource limits: 2 MiB source and inspection, 20,000 XML elements, parser nesting limit 32, 4,000 direct paragraphs, 1,000 resulting entries, 1,000 loss records and a 256 KiB loss report. The resulting script must satisfy the shared 1 MiB/field/ID/link limits. DOCTYPE and entity declarations reject; no external references are loaded. Malformed XML, unsupported document version/type and templates reject regardless of loss policy. Review the inspection and loss report for each supplied FDX file; support is limited to the subset described here.
Codeboard by Nonom Friedman
Explore Nonom Library