Table of Contents

Navigating the Playbook

Note

Status: implemented. In the Playbook tab, a table cell that names a node or speaker takes the reader to it in the JSON, and a bare index in the JSON is a link to the node or speaker it names.

Table of contents

Goal and scope

The playbook is full of pointers — "entry": 0, an edge's "target": 5, a line's "speaker": 1 — and the tables beside it are full of references to the same places. Following any of them by hand means scrolling and counting. Both directions land in the same editor, on the element's opening brace, centered.

Out of scope: opening another tab (the Dialogue Graph or Semantic Model) on the target, and highlighting a table row from the JSON.

Ubiquitous language

Term Meaning
Reference An integer in the playbook that names a node or a speaker. The schema types it nodeReference or speakerReference.
Definition Where a reference points: the opening line of a node (found by its "id") or of a speaker object (found by its index). A node's own "id" is a definition, never a reference.
Reference mark The dd-playbook-ref decoration over a reference's digits: a dotted underline and a pointer.
Go to Definition Following a reference, by a click on its mark or F12 on its line.

From a table into the JSON

Most cells are facts. The ones that name a place carry a PlaybookTarget and become a jump:

Table Cell Goes to
Playbook Entry node The node a playthrough starts on
Speakers Name That speaker's object
Anchors Node The node the anchor lands on
Nodes #, each number in Leads to, and each summary piece that names a node That node

An anchor's own #the-market cell copies instead: it is what a writer pastes into a script, not a place in this document (see Table Cell Conventions). The Nodes table's targets are described in Playbook Nodes Table.

From an index to its definition

playbookReferences() (playbook-references.ts) marks every reference in the viewport. A click on a mark, or F12 with the cursor on a reference line, reveals its definition. The Playbook tab's help panel documents both under Following an index.

flowchart LR
    SCHEMA["playbook-0.schema.json"] --> RT["referenceTypeAt<br/>(playbook-schema)"]
    TEXT["rendered playbook text"] --> RK["referenceKindAt"]
    RT --> RK
    RK --> EXT["playbookReferences()"]
    EXT --> MARK["dd-playbook-ref marks"]
    EXT --> NAV["click / F12 → definitionLineFor → revealLine"]
Function Responsibility
referenceTypeAt(path, kinds) Whether a schema path ends at a nodeReference, a speakerReference, or neither. Shares the schema walk the hover uses.
referenceKindAt(state, line) Classifies a rendered line as "node", "speaker", or null, excluding a node's own /id.
referenceRanges(state, from, to) The digit spans to mark in a range, with kind and value.
definitionLineFor(state, line) The line a reference points to, or null. A click and F12 both use it.
nodeLine, elementLine, revealLine (playbook-jump.ts) Find an element and reveal it centered; shared with the table jumps.

Finding the place

The playbook is JsonSerializer output with WriteIndented, so its shape is exactly regular (Playbook Tab D11). Finding an element is a scan: locate the named array, then walk the lines that open a block one level inside it, stepping over nested objects by depth.

A node's id is not its position. conformance/readable/node-out-of-position carries a playbook whose ids run 0, 5, so the search reads each element's "id". A speaker has no id, so it is found by index — bound when the table is built, because a sorted table no longer holds the speaker where the array put it. A node is found by what it says it is, a speaker by where it was.

Key design decisions

D1 — The cell carries the target; the tab performs the jump

The shared table component draws cells for tabs that have no document. It marks the cell with its target, and the Playbook tab, which owns the editor, listens.

D2 — The schema decides what is a reference

The schema already types entry, every anchors/*, every edge target, and a line's speaker as references. The classifier reads the $ref at the end of a line's path instead of listing those paths, so it cannot drift from the format. A plain number — format.version, an edge's order, a weight's percentage — gets no mark.

D3 — Marks cover the viewport and read the text

A playbook can run to thousands of lines, so the plugin scans only view.visibleRanges. Classification reads the text and the schema, never syntaxTree, so it answers the same at any scroll depth.

D4 — A plain click follows

The editor is read-only, so a click on a digit has nothing else to mean; a Ctrl/Cmd gate would only hide the feature. F12 is bound ahead of the defaults for the keyboard.

D5 — Land on the opening brace, centered

The brace shows the whole element; a line revealed at the bottom edge would leave its object off screen.

D6 — An unresolvable target does nothing

A reference whose target is absent stays marked, and following it leaves the reader where they are. Sending them somewhere plausible and wrong is worse.

D7 — A dotted underline, not the copy cue

Copying and going somewhere are different promises, so they look different.

Error and boundary cases

Case Behavior
The script did not compile No editor, so no cell is a jump and no mark is drawn.
A target absent from the document Nothing happens.
A sorted or filtered table Still correct, because the target was bound when the row was built.
An empty cell Never a jump.
"entry": 0 Marked and followable like any other reference.
F12 off a reference line Falls through to the editor's other bindings.
A definition inside a folded block Scrolled to, but the fold stays closed.

Testability

Level Covers
Vitest — playbook-jump Against sparse ids: node 5 is the second element, id 1 resolves to nothing, a speaker is found by index.
Vitest — playbook-view Which cells carry which target.
Vitest — playbook-schema, playbook-references Reference classification per path, /id excluded, digit spans, definitionLineFor, and the extension's marks, click, and F12 in jsdom.
Playwright — playbook.spec.ts A table jump and a reference click land on the right line and on screen; F12 from a speaker reference; marks absent on "id" and version; the help names F12; axe passes.

Open questions

  • A hover card over a reference. A card such as → node 5 · line · Guide: … shown before following. The Nodes table's summary segments are the text it would show, so it needs only the card itself.
  • Unfolding on arrival. A definition inside a folded block is scrolled to but not unfolded.
  • A context-menu item. Go to Definition beside the click and F12.