Update the journal-set doc entry, the CLI diagnostic table, and the
authoring skill, along with the in-app empty-state hint and the var
message comment.
Replace the tag/threshold/key/expr CSV body with YAML `vars:` and
`tags:` maps. `parseDeclare` validates the body with an exported Zod
schema and returns the same declaration and modifier shapes the
reactivity engine already consumes.
The breaking rename from `parseDeclareCsv` updates the registry, the
journal re-exports, and the `tttk check` role checks, where R003 now
reports schema errors with their path and R004 warns when a block
declares nothing.
EvalContext.lookup now receives the full $var name, matching getCombined,
setBase, and getMods, and parseFactor throws "Unknown variable" when the
lookup returns undefined. /set and /roll use the new resolveVariable() so a
typo errors instead of silently evaluating to 0.
Declarations and tag modifiers stay lenient (unknown -> 0): the documented
$hp = $con*5+$mod_hp pattern depends on external/undeclared variables
defaulting to 0.
Make the reactivity engine re-initialize when the loaded declarations
change instead of only at import. ensureReactivity() reads the live
completions signal, compares a signature, and rebuilds from the store
(with exact bases) only when needed; dispatchCommand calls it before each
dispatch. Remove the unused DispatchContext.declarations/tagModifiers
fields, which were mount-time snapshots and could not be trusted.
Build initReactivity's maps into locals and commit only after the
circular check passes, so a failed re-init leaves the previous engine
intact; clear inFlight on commit.
Drop the unreachable var "del" action from the schema, render, and
reducer.
Collect and report failed declarations, failed tag modifier expressions, and
circular dependencies instead of swallowing them. Diagnostics are deduped,
logged with key/expression context, cleared on re-init, and exposed through
useJournalCompletions and the VariableView warning banner. A bad declaration
no longer aborts the rest of a cascade.
Reject malformed numeric literals in the tokenizer so inputs like "1.2.3"
throw instead of being truncated by parseFloat.
The stream only carried combined values, so hydrating a session lost the raw base and rebuildReactivityFromStore re-applied tag modifiers on top of already-combined values (e.g. $hp 70 became 90).
Publish the raw base alongside the combined value on var messages, persist it in variableBases, and rebuild engine state from exact bases. Derive tag activations from base tagmaps so tagmap-valued modifier targets are not re-triggered on every rebuild.
Seed declared variables only after hydration and only for keys absent from the store, so persisted values are no longer clobbered on load and reloads stop appending duplicate var messages.
The spark message never displayed the dice, and its rollResult came from a
second, independent roll that could disagree with the row actually
selected.
rollSparkTable now reports the roll(s) it used, so the message can show
'header / notation -> roll' in the purple box and render each rolled value
as markdown (its own div) below it.
Inline spark tables (role=spark-table) live in the content registry, but the
rsbuild dev proxy forwarded only /__COMPLETIONS.json and
/__CONTENT_INDEX.json. The registry fetch fell back to the SPA index.html,
so /roll resolved the inline CSV as HTML and csv-parse failed with an
invalid opening quote.
Load the registry before publishing the loaded completions state so a roll
cannot fire while inline content is still unresolvable.
Add dev:web (rsbuild) and dev:cli (tttk serve ./content), with dev
running both via scripts/dev.mjs, which shuts both down when either
exits. Also add tttk:inspect for --inspect debugging.
Both servers previously defaulted to port 3000, so the CLI could not
bind while the web dev server was running and the proxy targeted
itself. Pin the web dev server to 5173; the CLI keeps 3000 to match
the proxy. Document the two-process setup and inspector debugging in
docs/development.md.
Directive examples sat bare in the doc bodies, so rendering them as
markdown executed the directives: live components, missing-file
fetches, and style side effects on the dialog. Wrap each example in
a markdown fence, matching the convention already used by md-table.
DocContent rendered the raw body in a whitespace-pre-wrap div, so
markdown syntax and fenced code blocks appeared literally. Parse
through the shared markdown pipeline and style with prose.
Wrap the content in an <article> so md-bg and md-border, which
resolve element.closest("article"), get a valid style target
instead of throwing.
Move the skill to .agents/skills/tttk-authoring and point gen:skill-reference at it. Activate it by symlinking ~/.agents/skills/tttk-authoring to the in-repo copy.
Extend the reference drift guard to cover the skill copy now that it is versioned, and document activation in docs/development.md.
Enrich known.ts with element/role details, inline specs, and dice notation, and render them from reference.ts. Add gen:skill-reference and ~ expansion in --out so the skill copy can be regenerated.
The skill's reference.md is now generated; its extra authoring content moved to layout.md, yaml-tag.md, csv.md, and markdown.md.
Add D002 (malformed attribute blocks), R004 (skipped declare rows), and V001-V005 (CSV data files). CSV checks run once per referenced file and also cover inline spark-table bodies.
Generate docs/reference.md from the known.ts catalog via npm run gen:reference, and add drift guards asserting the catalog matches the customElement call sites and the committed reference.
Add check to the CLI guide, README, QWEN, and project structure: options, exit codes, diagnostic codes, and the checks not yet implemented. Mark compile as an unimplemented stub and fix stale element syntax in examples.
Statically validate a directory of tttk documents and report structured diagnostics with file/line positions, exit codes, and --json output. Checks cover code-block roles, unresolved content references, tag blocks, element names, links, anchors, and assets. Runs over the same content registry as the app.
Drop the mermaid import from the markdown module. The extension only emits <pre class="mermaid">, so initialize mermaid in the app entry instead. Use explicit .js specifiers plus a bundler extensionAlias so both the NodeNext CLI build and the web build resolve the sources.
Replaces the render-first compile plan with a diagnostics-first design for a new tttk check command.
Covers the whole-tree registry pass, raw-source diagnostics with line numbers, the rule catalog, and exit codes, plus resolved decisions on the element catalog, mermaid refactor, icon search scope, and CSV checks.
- deriveLinks strips fenced code blocks so markdown examples don't
produce phantom link completions
- Add resolveContentEntry returning body + path + inline flag;
scanDocDirectives uses it instead of re-deriving resolution, and
parseDirectiveAttrs is replaced by the shared parseBlockAttrs
- Fix relative file refs with ./ prefix never resolving through
resolveContent (pathIndex lookup joined the ./ verbatim)
- Drop resolveBlockAs and the as= attribute; scanDoc now switches
directly on role and warns on unknown roles instead of silently
stripping
- md-table derives data-spark from its loaded CSV via
parseSparkTableCsv; delete injectSparkDirectives and the second
registry pass so pathIndex is exactly scanDoc output
- Align spark-table dice header regex with isSparkTableHeader
- Split loadCSV into parseCSVString (content) and loadCSVFromPath
(path); drop isCSV/looksLikeCsv heuristics
- Delete coerceSparkTables and markedTable label-header magic;
plain markdown tables now render as plain tables
- Add explicit markdown role=spark-table fence syntax that converts
pipe tables to CSV at scan time, with dice-header validation
- Map ESM-only github-slugger and csv-parse browser build to CJS in
jest config; add content-registry tests
Drop legacy yaml/tag info string; tag blocks now require
```yaml role=tag. Add fence-line tag=/id= attributes that
override YAML body values, and move parseBlockAttrs to
src/markdown/block-attrs.ts so scanner and render extensions
share one parser.
Expand align from l/c/r to a 3x3 grid with vertical prefixes:
tl/tc/tr/bl/bc/br. Vertical position renders via flex
justify-content; compact layers strings and YAML both parse the
new values, and the layer editor dropdown gains corner/edge
options.
Accept yaml role=tag alongside legacy yaml/tag so the block
body gets yaml syntax highlighting, matching the CLI role=
attribute convention. Copy button now emits the role=tag form.
Replace per-axis x1/y1/x2/y2 layer fields with a single
pos string (x1,y1-x2,y2) matching the compact layers format.
When a deck is configured via a yaml/tag codeblock (data-config),
the copy button now emits a yaml/tag block serialized from current
store state so template layers round-trip.
Allow md-deck layers to render markdown templates via {{var}}
substitution instead of only CSV props. Add config.ts to normalize
a JSON data-config attribute (size, grid, shape, layers) emitted by
the yaml code-block tag, taking precedence over legacy string props.
Export isSparkTableHeader from content-registry and use it in both CLI
and frontend table parsing. Add resolveContentRef to resolve content
references consistently. Write processed registry content back to the
file index so browser mode renders identically to CLI mode.
Extract buildRegistryFromIndex and normalizePathKey from the CLI serve
command and the browser completions module into the shared
content-registry module. This ensures both paths produce identical
pathIndex/docContent, including the spark-injection pass that was
previously only applied in the CLI.
Add a browser-safe ContentRegistry owning path and inline content
stores, then derive completions from it. Remove the old block-processor,
directive-scanner, and link-source modules in favor of scanDoc,
deriveCompletions, and injectSparkDirectives, and register an inline
resolver for the frontend file index.
- Move card tab selector to a left sidebar (CardList) with pagination
- Replace separate side toggle and layer panels with EditorTabs
- Move copy code button from layer panel to DeckHeader
- Simplify PropertiesEditorPanel by removing front/back toggle and
using a select for shape