Skip to main content

Data Model

The same three types describe a show in Rust (crates/show/src/types.rs) and in TypeScript (src/show.ts).

Show​

type Show = {
title: string;
description: string;
sections: Section[];
};

Section​

type Section = {
id: string;
title: string;
sections: Section[];
cues: Cue[];
};

A section is a leaf (has cues, no children) or a container (has children, no cues), never both. The editing actions maintain this invariant, and perform mode relies on it.

Cue​

type Cue = {
id: string;
text: string;
};

Flat items (derived)​

Perform mode flattens the tree into a chronological list:

type FlatCue = { type: "cue"; cue: Cue; sectionPath: string };
type FlatDivider = { type: "divider"; sectionPath: string };
type FlatItem = FlatCue | FlatDivider;

flattenCues walks the tree in order. A section with cues emits a divider (labeled with the full ancestor path, e.g. Act I - Scene i) followed by its cues; child sections are expanded in place. This is a pure derivation, so the stored show stays hierarchical.

The file envelope​

On disk, a show is wrapped for forward compatibility:

type ShowFile = {
format: "nque";
version: 0;
show: Show;
};

See Show file format.

IDs​

Every section and cue carries a unique id (a v4 UUID in Rust, a random id in the frontend). The schema requires ids to be non-empty. They act as stable keys for edits and for locating nodes anywhere in the tree.

Traversal​

Tree operations are implemented as recursive immutable helpers (updateSections, insertInto, removeFrom, findSection, …). Each returns a new show without mutating the original; isDirty is toggled by the caller.