Navigation
These commands and selectors move the reader through the story.
Novel keeps track of where the reader is with a call stack: a list of LexiaRefs that records the reader’s position and how they got there.
Commands
Section titled “Commands”startNovel
Section titled “startNovel”Starts the novel from the beginning.
startNovel(): Promise<void>Goes to the startup.startLabel from your project settings, and draws the opening scene.
Example
const { startNovel } = getCommands();await startNovel();navigate
Section titled “navigate”Moves the reader to a new place in the story.
navigate(ctx?: NavigateContext): Promise<void>
interface NavigateContext { label?: string; lexiaRef?: LexiaRef; link?: LinkLexia; type?: "call" | "jump" | "return";}| Field | Description |
|---|---|
label | Goes to a section or identity label. |
lexiaRef | Goes to that lexia. |
link | Follows the link. |
type | How the call stack changes. See below. |
With no destination, navigate moves on from the reader’s current position, following any silent links in the beat.
| Type | Call stack |
|---|---|
"jump" | Replaces the top of the stack, so there’s no way back. This is the default. |
"call" | Adds to the stack, so the story can return to where it branched. |
"return" | Removes the top of the stack, and returns the reader to where the last call was made. |
To keep some of your plugin’s state in step with the call stack, see withCallStack.
Example
const { navigate } = getCommands();
await navigate(); // advanceawait navigate({ link }); // the reader chose this linkawait navigate({ label: "the-docks" }); // go to a labelawait navigate({ label: "flashback", type: "call" }); // callawait navigate({ type: "return" }); // come backabortNavigation
Section titled “abortNavigation”Cancels a navigation that failed partway through, and discards any uncommitted state.
abortNavigation(ctx: AbortNavigationContext): Promise<void>
interface AbortNavigationContext { error: unknown;}navigate calls this when any stage of a beat throws an error. It discards state back to the last commit with discardState, then throws the error again.
Decorate it to handle errors more gently than a crash. Let next run so the state is still discarded, and catch the error it throws.
Example
decorateCommand("abortNavigation", async (context, next) => { try { await next(context); } catch (error) { showApology(error); }});updateCallStack
Section titled “updateCallStack”Changes the call stack without loading or drawing anything.
updateCallStack(ctx: UpdateCallStackContext): Promise<void>
interface UpdateCallStackContext { lexiaRef?: LexiaRef; type?: "call" | "jump" | "return";}A "jump" or "call" needs a lexiaRef. A "return" doesn’t take one, because its destination is already on the stack.
Example
const { updateCallStack } = getCommands();
await updateCallStack({ lexiaRef, type: "call" });await updateCallStack({ type: "return" });stepForward
Section titled “stepForward”Moves the reader forward one step through their history, and redraws the scene.
stepForward(): Promise<void>Wraps the SDK’s stepForward with a redraw.
Example
const { stepForward } = getCommands();await stepForward();stepBack
Section titled “stepBack”Moves the reader back one step through their history, and redraws the scene.
stepBack(): Promise<void>Wraps the SDK’s stepBack with a redraw.
Example
const { stepBack } = getCommands();await stepBack();resolveLabel
Section titled “resolveLabel”Returns the LexiaRef a screenplay label points to.
resolveLabel(ctx: ResolveLabelContext): Promise<ResolveLabelResult>
interface ResolveLabelContext { label: string;}interface ResolveLabelResult { lexiaRef: LexiaRef;}Throws an error if the label isn’t in the label manifest.
Example
const { resolveLabel } = getCommands();const { lexiaRef } = await resolveLabel({ label: "chapter-two" });resolveNextBeat
Section titled “resolveNextBeat”Returns the beat that comes after the current one.
resolveNextBeat( ctx: ResolveNextBeatContext,): Promise<null | ResolveNextBeatResult>
interface ResolveNextBeatContext { lexiaRef: LexiaRef; link?: LinkLexia;}interface ResolveNextBeatResult { lexiaRef: LexiaRef; type?: "call" | "jump";}- If the beat has a silent link, it follows that link.
- If the beat has any other links, it returns
null, because the reader has to choose one. - Otherwise, it moves on to the next beat in the screenplay.
Example
const { resolveNextBeat } = getCommands();const next = await resolveNextBeat({ lexiaRef });Selectors
Section titled “Selectors”getCallStackIndex
Section titled “getCallStackIndex”Returns the index of the top of the call stack.
getCallStackIndex(ctx: GetCallStackContext): { index: number }The SDK’s withCallStack mixin uses this selector, and throws if no plugin provides it.
Example
const { getCallStackIndex } = getSelectors();const { index } = getCallStackIndex();