Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
361 commits
Select commit Hold shift + click to select a range
698a15d
feat(dialog-editor): add/remove bundle branch controls (side-effect b…
burner1024 Jun 30, 2026
9779d53
fix(dialog-editor): anchor a new if branch at surviving branches; sof…
burner1024 Jun 30, 2026
2902a30
fix(dialog-editor): wire entry call for a node renamed and newly made…
burner1024 Jun 30, 2026
71a4c3b
feat(dialog-editor): fail loud on overlapping or malformed splice ops
burner1024 Jun 30, 2026
dcee49d
test(dialog-editor): cover reachability and layout boundary cases
burner1024 Jun 30, 2026
2fd955b
refactor(dialog-editor): drop dead targetLabel, unused layout directi…
burner1024 Jun 30, 2026
04d9880
refactor(dialog-editor): hoist msgIdOf helper, correct stale badge co…
burner1024 Jun 30, 2026
a73715f
feat(dialog-editor): represent if/else branches in the tree view; web…
burner1024 Jun 30, 2026
25187af
fix(dialog-editor): rename/delete every call site, not just the first
burner1024 Jun 30, 2026
38f68b1
fix(dialog-editor): keep a tall node inspector clear of the graph min…
burner1024 Jun 30, 2026
312cf03
fix(dialog-editor): stop graph overlays colliding; guard it in the ha…
burner1024 Jul 1, 2026
7849626
feat(dialog-editor): drop '(preview)' from the Dialog Editor command …
burner1024 Jul 1, 2026
301b8c4
test(dialog-editor): write harness screenshot to repo tmp/, not the s…
burner1024 Jul 1, 2026
08703ca
fix(dialog-editor): delete a node's forward declaration too, not just…
burner1024 Jul 1, 2026
f647b17
fix(dialog-editor): dock the inspector/source/issues into a rail besi…
burner1024 Jul 1, 2026
ddebf17
feat(dialog-editor): save on Ctrl/Cmd+S in the webview
burner1024 Jul 1, 2026
8cb4874
feat(dialog-editor): confirm delete's EXIT redirect; let a just-set e…
burner1024 Jul 1, 2026
2874508
test(binary-editor): write harness screenshots to repo tmp/, not the …
burner1024 Jul 1, 2026
af59cd1
fix(dialog-editor): route keyboard delete through the guarded delete …
burner1024 Jul 1, 2026
4121486
fix(dialog-editor): keyboard-navigable, screen-reader-labelled conver…
burner1024 Jul 1, 2026
8db55ff
refactor(dialog-editor): extract computeDialogSourceEdit from panel s…
burner1024 Jul 1, 2026
0373a71
test(dialog-editor): cover the SSL id-allocation branch of computeDia…
burner1024 Jul 1, 2026
98e6cc2
feat(dialog-editor): add EchoGuard for the provider self-edit feedbac…
burner1024 Jul 1, 2026
1185670
feat(dialog-editor): contribute the dialog custom editor (option prio…
burner1024 Jul 1, 2026
c5cec92
feat(dialog-editor): back the dialog editor with a CustomTextEditorPr…
burner1024 Jul 1, 2026
bdcd6ea
fix(dialog-editor): fail loud on edit-parse error, roll back self-edi…
burner1024 Jul 1, 2026
7cd2ceb
feat(dialog-editor): emit edits to the host; drop the webview Save bu…
burner1024 Jul 1, 2026
d278409
refactor(dialog-editor): drop the transitional save message branch in…
burner1024 Jul 1, 2026
ebfbfa9
refactor(dialog-editor): home DialogMessages in shared; note transien…
burner1024 Jul 1, 2026
35ba870
feat(dialog-editor): open the editor full-width, not beside the source
burner1024 Jul 1, 2026
b6c6e57
feat(dialog-editor): drop the redundant sink badge
burner1024 Jul 1, 2026
e0ef29e
feat(dialog-editor): show just the node id on SSL cards, not 'NPC - N…
burner1024 Jul 1, 2026
19e0c3b
feat(dialog-editor): color-code option reactions and mark low-INT rep…
burner1024 Jul 1, 2026
027570b
feat(dialog-editor): replace the entry checkbox with a read-only star…
burner1024 Jul 1, 2026
93bbfc2
feat(dialog-editor): mark nodes whose text is shared with another node
burner1024 Jul 1, 2026
0e8ba5e
feat(dialog-editor): drop the meaningless 'NPC' inspector title for SSL
burner1024 Jul 1, 2026
64669ef
chore(dialog-editor): regenerate harness app.html for the card/inspec…
burner1024 Jul 1, 2026
58bb672
test(dialog-editor): drop the virtual-sink test left by the sink-badg…
burner1024 Jul 1, 2026
e9ee17c
feat(dialog-editor): edit an SSL option's reaction and low-INT variant
burner1024 Jul 1, 2026
b0a995e
chore(dialog-editor): regenerate harness app.html for the reaction/lo…
burner1024 Jul 1, 2026
24eb1b3
feat(dialog-editor): show the node id in the tree view, not a generic…
burner1024 Jul 1, 2026
595ef38
fix(dialog-editor): re-frame the graph after switching back from the …
burner1024 Jul 1, 2026
8c33693
chore(dialog-editor): regenerate harness app.html for the tree/viewpo…
burner1024 Jul 1, 2026
64dc5f1
feat(dialog-editor): color-code option reactions and low-INT in the t…
burner1024 Jul 2, 2026
c566316
chore(dialog-editor): regenerate harness app.html for the tree reacti…
burner1024 Jul 2, 2026
42807ee
fix(dialog-editor): don't corrupt or lock a new SSL option edited aft…
burner1024 Jul 2, 2026
a8b6bba
chore(dialog-editor): regenerate harness app.html for the reconcile fix
burner1024 Jul 2, 2026
16f83b0
fix(dialog-editor): make a newly-added SSL node editable and stop the…
burner1024 Jul 2, 2026
7fac108
fix(dialog-editor): unify toolbar, fix context-menu placement, relabe…
burner1024 Jul 2, 2026
f0a9908
feat(dialog-editor): unify display vocabulary to State / Option / NPC…
burner1024 Jul 2, 2026
21b25ed
feat(dialog-editor): color NPC lines blue and neutral options grey, c…
burner1024 Jul 2, 2026
a535fec
feat(dialog-editor): show the speaker on every surface (D character, …
burner1024 Jul 2, 2026
4e42591
fix(dialog-editor): stop false "Duplicate state label" for CHAIN-deri…
burner1024 Jul 2, 2026
24ac5dc
refactor(dialog-editor): in the tree, dim the state id and drop the r…
burner1024 Jul 2, 2026
3626dfd
feat(dialog-editor): add an option via a trailing "+" at the end of t…
burner1024 Jul 2, 2026
d92a9f6
chore(dialog-editor): update the render harness for the tree default …
burner1024 Jul 2, 2026
cc1c2b2
fix(dialog-editor): resolve a state within the active root so Set tar…
burner1024 Jul 2, 2026
a2cd5b2
feat(dialog-editor): show "Referenced by" (find-callers) for the sele…
burner1024 Jul 3, 2026
34dc809
fix(dialog-editor): dedupe duplicate state ids so shared CHAIN labels…
burner1024 Jul 3, 2026
3bb0939
chore(dialog-editor): regenerate harness app.html for the duplicate-i…
burner1024 Jul 3, 2026
37d4187
fix(dialog-editor): serialize webview edits so concurrent applyEdits …
burner1024 Jul 3, 2026
834337d
feat(dialog-editor): warn when @N message text can't be resolved
burner1024 Jul 3, 2026
02602b9
chore(dialog-editor): regenerate harness app.html for the unresolved-…
burner1024 Jul 3, 2026
afdd6a4
docs(settings): document translation resolution (.bgforge.yml, @tra, …
burner1024 Jul 3, 2026
21d02c3
fix(dialog-editor): rewrite out-of-band and cross-dialogue refs on st…
burner1024 Jul 3, 2026
ee6c597
fix(dialog-editor): scope GOTO rename/delete to the state's own dialogue
burner1024 Jul 3, 2026
4a4655f
chore(dialog-editor): regenerate harness app.html for GOTO rename/del…
burner1024 Jul 3, 2026
cc4cdc0
fix(dialog-editor): scope rename uniqueness to the state's own dialogue
burner1024 Jul 3, 2026
612bb10
chore(dialog-editor): regenerate harness app.html for per-dialogue re…
burner1024 Jul 3, 2026
f46c9da
feat(dialog-editor): inline add/remove and select/edit options in the…
burner1024 Jul 3, 2026
e163e85
chore(dialog-editor): regenerate harness app.html for inline option a…
burner1024 Jul 3, 2026
3985013
feat(dialog-editor): edit option text inline in the tree
burner1024 Jul 4, 2026
26c3fe1
chore(dialog-editor): regenerate harness app.html for inline option-t…
burner1024 Jul 4, 2026
1c18629
feat(dialog-editor): add/remove nodes inline in the tree with +/-
burner1024 Jul 4, 2026
a1db5bd
chore(dialog-editor): regenerate harness app.html for inline node add…
burner1024 Jul 4, 2026
5d65fb0
feat(dialog-editor): author Fallout SSL dialogs from scratch
burner1024 Jul 4, 2026
293ef19
feat(dialog-editor): show Node998/Node999 as Combat/Exit targets (SSL)
burner1024 Jul 4, 2026
3084606
feat(dialog-editor): persist a from-scratch dialog's translation text
burner1024 Jul 4, 2026
4a5dca7
feat(dialog-editor): retarget a terminal option to a node (Combat)
burner1024 Jul 4, 2026
042c1eb
feat(dialog-editor): inline-edit NPC lines in the tree, arrow-key nav…
burner1024 Jul 5, 2026
53717b7
feat(dialog-editor): focus the Inspector on a selected option
burner1024 Jul 5, 2026
f887456
feat(dialog-editor): drop N/B/G reaction chips, clip-aware tooltips, …
burner1024 Jul 5, 2026
aedc002
feat(dialog-editor): tree UX - keyboard actions, go-to-source, cleane…
burner1024 Jul 5, 2026
32e2684
feat(dialog-editor): add beta feedback notice to the toolbar
burner1024 Jul 5, 2026
bec1425
chore(dialog-editor): regenerate harness app.html for toolbar beta no…
burner1024 Jul 5, 2026
cee7cbd
feat(dialog-editor): add a tree keyboard-shortcut legend to the toolbar
burner1024 Jul 5, 2026
47dbd4e
feat(dialog-editor): show the option condition [if] left of the say-text
burner1024 Jul 5, 2026
49cea49
chore(dialog-editor): regenerate harness app.html for toolbar legend …
burner1024 Jul 5, 2026
3c30562
feat(dialog-editor): render nested SSL conditions as a recursive block
burner1024 Jul 5, 2026
ed27db8
chore(dialog-editor): regenerate harness app.html for the recursive b…
burner1024 Jul 5, 2026
475e936
feat(dialog-editor): flag approximate nodes with a loud "approx" badge
burner1024 Jul 5, 2026
8c7561e
chore(dialog-editor): regenerate harness app.html for the approximate…
burner1024 Jul 5, 2026
ecad6e2
feat(dialog-editor): show group gates as compact [if]/[else] chips
burner1024 Jul 5, 2026
bbab20f
chore(dialog-editor): regenerate harness app.html for compact group g…
burner1024 Jul 5, 2026
1f02e28
fix(dialog-editor): use SSL `not (...)` for negated conditions; show …
burner1024 Jul 5, 2026
f125873
chore(dialog-editor): regenerate harness app.html for inverted-else /…
burner1024 Jul 5, 2026
503ae06
feat(dialog-editor): stack the toolbar into three rows (feedback / bu…
burner1024 Jul 5, 2026
8dc7d41
chore(dialog-editor): regenerate harness app.html for the three-row t…
burner1024 Jul 5, 2026
147a677
feat(dialog-editor): use [if]/[else] in the inspector branch labels too
burner1024 Jul 5, 2026
e46c9a2
chore(dialog-editor): regenerate harness app.html for inspector [if]/…
burner1024 Jul 5, 2026
cd0a670
feat(dialog-editor): put the [if] gate on each option's row, not a he…
burner1024 Jul 5, 2026
fcd0a50
chore(dialog-editor): regenerate harness app.html for per-option [if]…
burner1024 Jul 5, 2026
3105d30
fix(dialog-editor): make bundle/structured option rows selectable
burner1024 Jul 5, 2026
49509cb
chore(dialog-editor): regenerate harness app.html for selectable bran…
burner1024 Jul 5, 2026
52e5a6b
fix(dialog-editor): show a structured node's top-level opening line o…
burner1024 Jul 5, 2026
d94c8af
fix(dialog-editor): address UI-review findings on the conditional-nod…
burner1024 Jul 6, 2026
9236aa3
chore(dialog-editor): regenerate harness app.html for UI-review fixes
burner1024 Jul 6, 2026
ca81527
feat(dialog-editor): scope option conditions to state, gate editing o…
burner1024 Jul 6, 2026
ed68996
feat(dialog-editor): node ids + rename, conditional-line selectabilit…
burner1024 Jul 6, 2026
739bacc
feat(dialog-editor): frame if/else nodes as a fork instead of "(no li…
burner1024 Jul 6, 2026
ec277c1
feat(dialog-editor): label else-branch lines [else], not [if]
burner1024 Jul 6, 2026
d9d0012
feat(dialog-editor): highlight a whole if/else branch on click in the…
burner1024 Jul 6, 2026
36447b0
feat(dialog-editor): branch-aware Inspector for structured nodes
burner1024 Jul 6, 2026
ff2f8b3
fix(dialog-editor): inline-edit branch option text; fix Inspector tex…
burner1024 Jul 6, 2026
e5b8664
test(dialog-editor): guard branch-option edit parity; extract shared …
burner1024 Jul 6, 2026
75507f9
feat(dialog-editor): find-bar for the tree view
burner1024 Jul 7, 2026
14f30f9
fix(dialog-editor): dim the node id on editable nodes too
burner1024 Jul 7, 2026
3e2cd44
fix(dialog-editor): auto-select added options; always label an option…
burner1024 Jul 7, 2026
2e731a3
refactor(dialog-editor): unify selection and add/edit through single …
burner1024 Jul 7, 2026
4e4b6a7
refactor(dialog-editor): adopt the re-parse on self-edit so the tree …
burner1024 Jul 7, 2026
21f460d
fix(dialog-editor): F4 (go to source) on a WeiDU D option
burner1024 Jul 7, 2026
45e824b
feat(dialog-editor): F4 (go to source) from the graph view
burner1024 Jul 7, 2026
04d789e
fix(dialog-editor): treat an existing WeiDU D item as non-pending
burner1024 Jul 7, 2026
33ab992
fix(dialog): record every WeiDU D multisay text faithfully in the parser
burner1024 Jul 8, 2026
d9c8f65
refactor(dialog): single sourceLang discriminant with derived renderF…
burner1024 Jul 8, 2026
bac80bd
test(dialog): add authored TSSL dialog fixtures (flat, conditional, n…
burner1024 Jul 8, 2026
057ea62
feat(dialog): TSSL source parser (ts-morph, ranges into .tssl, tiered)
burner1024 Jul 8, 2026
867bec5
feat(dialog): TD source parser (ts-morph, ranges into .td, states/tra…
burner1024 Jul 8, 2026
e31e0bf
feat(dialog): open .td/.tssl in the dialog editor as a tiered read-on…
burner1024 Jul 8, 2026
b6510df
fix(dialog): complete sourceLang migration in Svelte webview; add sve…
burner1024 Jul 8, 2026
36033a2
fix(dialog): resolve svelte-check type errors in the dialog-editor we…
burner1024 Jul 8, 2026
f80b0be
build: gate svelte-check in the pipeline (tsc skips .svelte); fix bin…
burner1024 Jul 8, 2026
1cb2c0a
feat(dialog): TSSL option retarget - splice the new target into .tssl…
burner1024 Jul 8, 2026
9225e1a
feat(dialog): enable TSSL option retarget in the UI via fieldEditable…
burner1024 Jul 8, 2026
be6b914
feat(dialog): TSSL condition editing - record if-condition spans, spl…
burner1024 Jul 8, 2026
ee3cc7f
feat(dialog): TD field editing - retarget (targetRange splice) + text…
burner1024 Jul 8, 2026
b172389
feat(dialog): TSSL structural - remove option (splice statement / who…
burner1024 Jul 8, 2026
7771389
fix(dialog): open the dialog editor for .td/.tssl (languageId is "typ…
burner1024 Jul 8, 2026
5f6396f
fix(dialog): enable option retarget for faithful td/tssl (setTarget g…
burner1024 Jul 8, 2026
40f26a7
fix(dialog): editability copy names the real source for td/tssl, not …
burner1024 Jul 8, 2026
eb02506
fix(dialog): editability copy says "the source file" generically, dro…
burner1024 Jul 8, 2026
d2ab344
feat(dialog): TSSL add-option - serialize new NOption at the survivor…
burner1024 Jul 8, 2026
857cb20
feat(dialog): TSSL remove-node - splice the function + flip inbound o…
burner1024 Jul 8, 2026
b0faac7
feat(dialog): TSSL add-node - serialize a new function before talk_p_…
burner1024 Jul 8, 2026
00b607c
feat(dialog): TSSL rename-node - rewrite the function name + entry calls
burner1024 Jul 8, 2026
e4dcad2
feat(dialog): enable structural editing for faithful TSSL nodes
burner1024 Jul 8, 2026
82751dd
feat(dialog): TD/D-family tra id allocator; wire TD into source-edit
burner1024 Jul 8, 2026
b8e0429
feat(dialog): allocate tra ids for new D-family states; D new-state i…
burner1024 Jul 8, 2026
8ba9f57
feat(dialog): record TD transition/name spans and state-list wiring i…
burner1024 Jul 8, 2026
ae96dde
feat(dialog): TD remove-option - splice out the reply+goTo statement …
burner1024 Jul 8, 2026
b88bcee
feat(dialog): TD add-option - serialize new reply+goTo at the survivo…
burner1024 Jul 8, 2026
a675419
feat(dialog): TD remove-node - splice the function, prune wiring, fli…
burner1024 Jul 8, 2026
8b0eedb
feat(dialog): TD add-node - serialize a new function and wire it into…
burner1024 Jul 8, 2026
39a74b9
feat(dialog): TD rename-node - rewrite the function name, state-list …
burner1024 Jul 8, 2026
0fd0e2e
test(dialog): TD structural editing end-to-end round-trip via compute…
burner1024 Jul 8, 2026
b46bb0d
docs(dialog): note TD also sets DialogState.nameRange
burner1024 Jul 8, 2026
205f31f
feat(dialog): enable structural editing for TD nodes in the UI
burner1024 Jul 8, 2026
dde969c
fix(dialog): state-label reads "jump target" not "read-only" when str…
burner1024 Jul 8, 2026
874bc4b
test(tbaf): make sample typecheck hermetic with --skipLibCheck
burner1024 Jul 8, 2026
4534dad
fix(dialog): empty Inspector fields get a clear placeholder, not a bl…
burner1024 Jul 8, 2026
2070c8b
feat(dialog-editor): splice TSSL option reaction (N/G/B) edits back t…
burner1024 Jul 8, 2026
3036d48
feat(dialog-editor): splice TSSL option low-INT toggle back to source
burner1024 Jul 8, 2026
12e8b3e
feat(dialog-editor): reorder TSSL node options via survivor slot refill
burner1024 Jul 8, 2026
918e769
feat(dialog-editor): reorder TD transitions by lossless source-span move
burner1024 Jul 8, 2026
35e43ea
feat(dialog-editor): delete a gated TD state with its empty enclosing…
burner1024 Jul 8, 2026
d4f1b60
feat(dialog-editor): flip a chain-form TD transition to exit() via re…
burner1024 Jul 8, 2026
56e8092
feat(dialog): un-gate option reorder / reaction / low-INT now that th…
burner1024 Jul 8, 2026
37509cb
refactor(dialog-editor): address review findings - exhaustive dispatc…
burner1024 Jul 9, 2026
b004faf
feat(dialog-editor): TSSL display parity with the native SSL parser
burner1024 Jul 9, 2026
fece346
refactor(dialog-editor): consolidate duplicated writer helpers into d…
burner1024 Jul 9, 2026
a075a69
feat(dialog-editor): verify a saved dialog matches the intended edit
burner1024 Jul 9, 2026
d69ad14
test(dialog-editor): assert card connectability on fieldEditable, not…
burner1024 Jul 9, 2026
f73be8b
fix(dialog-editor): remove the false-positiving live save verification
burner1024 Jul 9, 2026
e908aa2
refactor(dialog-editor): drop the dormant verify oracle + pendingInse…
burner1024 Jul 9, 2026
41bd899
feat(dialog): TSSL source parser + writer reach full SSL parity
burner1024 Jul 9, 2026
d5ea2c4
refactor(dialog): route model flattening through shared allStates/all…
burner1024 Jul 9, 2026
3703d56
fix(dialog): capture the full multisay on the D CHAIN/INTERJECT path
burner1024 Jul 9, 2026
4b36400
fix(dialog): the tree honors per-state field-editability, matching th…
burner1024 Jul 9, 2026
90a882d
chore(dialog): review fruits - rationale comments, exhaustiveness gua…
burner1024 Jul 9, 2026
98efaef
refactor(dialog): retire the legacy dialog-tree preview, superseded b…
burner1024 Jul 9, 2026
b452ab5
test(dialog): direct edge-case coverage for dialog-edit-common
burner1024 Jul 9, 2026
52b6810
test(dialog): integration-test the panel session wiring (queue + disp…
burner1024 Jul 9, 2026
929f63f
fix(dialog): preserve multisay SAY alternates on WeiDU D write-back
burner1024 Jul 9, 2026
240237b
fix(dialog): don't leak 'committed' onto a duplicated state; dedupe f…
burner1024 Jul 9, 2026
3e23bca
refactor(dialog): one shared tree traversal (childStates), fixing mis…
burner1024 Jul 9, 2026
dd77313
refactor(dialog): dedupe the panel LSP parse request
burner1024 Jul 9, 2026
cc5fcc9
fix(dialog): log the parser degrade-to-empty; document two token/rege…
burner1024 Jul 9, 2026
2368bbc
refactor(dialog): rename applyDialogEdits -> applyDDialogEdits for si…
burner1024 Jul 9, 2026
5050a2b
test(dialog): assert cross-language parity + write-back faithfulness
burner1024 Jul 9, 2026
cc0b0c4
refactor(dialog): unify the SSL/TSSL write-back through one flat-opti…
burner1024 Jul 9, 2026
175ed1b
test(dialog): real RP corpus write-back round-trip (integration)
burner1024 Jul 9, 2026
7094a99
test(dialog): assert D<->TD write-back parity across the edit matrix
burner1024 Jul 9, 2026
9be3273
fix(dialog): family-correct wording in the unresolved-refs banner
burner1024 Jul 9, 2026
de2096e
fix(dialog): collapse an emptied shared `if` on conditional-option re…
burner1024 Jul 9, 2026
ace2a16
fix(dialog): wrap a TSSL option when a condition is added (was silent…
burner1024 Jul 9, 2026
0204e91
refactor(dialog): unify SSL/TSSL whole-model write-back through one e…
burner1024 Jul 9, 2026
c4a03a4
fix(dialog): mark TD states with body conditionals unfaithful; conjoi…
burner1024 Jul 9, 2026
97cc0c2
refactor(dialog): one editability predicate; gate unfaithful D-family…
burner1024 Jul 9, 2026
59c2cd3
fix(dialog): keep derived state in sync on every mutation path
burner1024 Jul 9, 2026
516066c
fix(dialog): tree collapse sticks; parse failure surfaces; guard surv…
burner1024 Jul 9, 2026
53a948f
fix(dialog): guard invalid-output and overlapping-splice edge cases i…
burner1024 Jul 9, 2026
6286849
test(dialog): assert SSL<->TSSL parity for bundle (if/else) branch ops
burner1024 Jul 9, 2026
cbde245
fix(dialog): a structured node's option uses its read-only view, not …
burner1024 Jul 9, 2026
05383d9
chore(dialog-editor): stop tracking the generated harness bundle app.…
burner1024 Jul 9, 2026
288248e
fix(dialog): degrade TD/TSSL source parses to the empty model on synt…
burner1024 Jul 9, 2026
838a3a1
feat(dialog): surface the side-effect honesty badge on TSSL nodes (SS…
burner1024 Jul 9, 2026
4c422cf
refactor(dialog-editor): extract the re-parse routing into a unit-tes…
burner1024 Jul 9, 2026
81e4581
refactor(dialog): unexport file-local SSL edit helpers; reuse the err…
burner1024 Jul 9, 2026
141e0ae
fix(dialog-editor): uniform delete gating, Escape on the confirm moda…
burner1024 Jul 9, 2026
f919b94
test(dialog): drop the aggregate-summary corpus test
burner1024 Jul 9, 2026
f89b9f6
fix(dialog): anchor D write-back on the fresh parse, matching states …
burner1024 Jul 10, 2026
5c26a78
fix(dialog-editor): arm the emit echo-suppression only when a reconci…
burner1024 Jul 10, 2026
d29ae0c
fix(dialog): append newly-minted @N entries to .tra on save, like the…
burner1024 Jul 10, 2026
b96de7f
fix(format): exclude the generated harness real-model.ts from oxfmt
burner1024 Jul 10, 2026
7e6f456
test(dialog-editor): drive the webview/host edit protocol under autom…
burner1024 Jul 10, 2026
3d7b89c
test(dialog): pin D-family locality and multi-invocation write-back c…
burner1024 Jul 10, 2026
28b5d9d
fix(dialog): anchor every write-back splice on the fresh parse, at to…
burner1024 Jul 10, 2026
70a41ff
docs(dialog-editor): record why the graph projection is imperative wh…
burner1024 Jul 10, 2026
d7fed51
refactor(dialog-editor): adopt every reparse; an editing overlay repl…
burner1024 Jul 10, 2026
d9a904f
test(dialog-editor): close the client coverage gap the host-core extr…
burner1024 Jul 10, 2026
d51a12b
docs(dialog-editor): update harness protocol wording to the adopt-plu…
burner1024 Jul 10, 2026
0edbb6a
docs(dialog-editor): record why the optimistic model is kept over a t…
burner1024 Jul 10, 2026
bccefc6
fix(dialog-editor): keep a new node's empty NPC line editable via one…
burner1024 Jul 10, 2026
50e84ae
fix(dialog-editor): duplicate a node copy-on-write so editing the cop…
burner1024 Jul 10, 2026
a7b2b9d
fix(dialog-editor): project empty NodeNNN procedures so +State can't …
burner1024 Jul 10, 2026
8409c86
fix(dialog-editor): lock an unresolved-@tra D line so its edit can't …
burner1024 Jul 10, 2026
f53ec66
test(dialog-editor): add end-to-end view-vs-disk integrity round-trip…
burner1024 Jul 10, 2026
3f7f6b9
fix(dialog-editor): stop the inspector baking a newline into a single…
burner1024 Jul 10, 2026
5e1450e
fix(dialog-editor): make Del act on the selected option, matching the…
burner1024 Jul 10, 2026
31cdd7e
fix(dialog-editor): splice out a deleted terminal (exit) option inste…
burner1024 Jul 10, 2026
d56945f
docs(dialog-editor): correct the isPendingChoice note after the D tex…
burner1024 Jul 10, 2026
0d890db
fix(dialog-editor): correct two misleading guard messages surfaced by…
burner1024 Jul 10, 2026
43097d4
feat(dialog-editor): show and edit every line of a multisay monologue…
burner1024 Jul 10, 2026
b2d20e1
feat(dialog-editor): mark unsaved-draft items so the view shows what …
burner1024 Jul 10, 2026
c634437
feat(dialog-editor): name new and duplicated D states StateNNN
burner1024 Jul 10, 2026
29c54aa
fix(dialog-editor): splice a new TD node into a trailing-comma append…
burner1024 Jul 10, 2026
b8996f5
fix(dialog-editor): correct D/SSL/TSSL write-back on real-corpus shapes
burner1024 Jul 10, 2026
783954a
feat(dialog-editor): separate the beta notice from the toolbar with a…
burner1024 Jul 10, 2026
f26437b
fix(binary-editor): poll for the deep-jump landing instead of a fixed…
burner1024 Jul 11, 2026
3403d0b
test: run the webview render/drive harnesses in CI as a regression suite
burner1024 Jul 11, 2026
4c87022
test(harness): replace fixed sleeps with condition polls in the webvi…
burner1024 Jul 11, 2026
78074db
fix(binary): recompute an empty CRE section offset a shrinking edit p…
burner1024 Jul 11, 2026
ed31098
feat(weidu-tp2): parse REQUIRE_FILE/FORBID_FILE/FORBID_PREDICATE comp…
burner1024 Jul 11, 2026
5d064d0
test(external): exclude BG1NPC template BAF and malformed TPA from th…
burner1024 Jul 11, 2026
58f18e7
feat(weidu-tp2): parse EXTEND_TOP/BOTTOM USING tra and REMOVE_CRE_ITEMS
burner1024 Jul 11, 2026
3fd2043
feat(weidu-d): parse the CHAIN3 chain-variant keyword
burner1024 Jul 11, 2026
da43cc4
feat(weidu-d): parse %var%-interpolated names and three more BG1NPC D…
burner1024 Jul 11, 2026
5f9ab04
test(external): exclude the malformed BG1NPC dialog x#kivantazok_ee.d
burner1024 Jul 11, 2026
ce23663
test(external): park BG1NPC x#faint.d for a pre-existing formatter id…
burner1024 Jul 11, 2026
852dff8
fix(weidu-d): keep formatter idempotent when a ~string~ abuts a keyword
burner1024 Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/harness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: "Harness"

# Render/drive the binary-editor and dialog-editor webview harnesses in headless Chromium as a regression
# suite (scripts/test-harness.sh). Each driver mounts the real webview bundle and asserts through PASS/FAIL
# gates - catching render/mount/CSP/layout regressions the node/jsdom vitest suites cannot see.
#
# Kept in its OWN job rather than the main "Build" gate so the Chromium download (~150MB) and the browser
# runs add no wall-clock to every other check. See the harness READMEs and docs/architecture.md.

on:
pull_request:
push:
branches:
- "**"

# Cancel superseded runs on the same ref (branch pushes and PR updates).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
harness:
name: harness
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
run_install: false

- name: Install Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

# The dialog-editor edit drivers parse real .d through the weidu-d tree-sitter WASM (via the fake host),
# which is a gitignored build artifact.
- name: Build tree-sitter grammar
run: pnpm build:grammar

# playwright is a pinned devDep, but pnpm's build-script gate skips its browser postinstall, so pull the
# one browser the harnesses launch here. --with-deps installs the OS libraries Chromium needs on the runner.
- name: Install Chromium for Playwright
run: pnpm exec playwright install --with-deps chromium

# Several binary-editor drivers read real .itm/.cre/.map files from the gitignored-but-reproducible
# external/ trees (the dialog drivers and the deep-jump driver use committed testFixture/ and need none).
- name: Reset external fixture repos
run: ./scripts/reset-external.sh

- name: Run render/drive harnesses
run: ./scripts/test-harness.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ bgforge-mls-kate*/
.reports/
.vscode-test/
.dev/
.playwright-mcp/

client/node_modules
client/out
Expand Down
4 changes: 3 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"themes/vs-seti-icon-theme.json",
"themes/bgforge-monokai.json",
"themes/bgforge-icon-theme.json",
"binary-editor/test/harness/*.html"
"binary-editor/test/harness/*.html",
"client/src/dialog-editor/test/harness/app.html",
"client/src/dialog-editor/test/harness/real-model.ts"
]
}
21 changes: 17 additions & 4 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,21 +266,33 @@
// `document.getElementById` is clearer and faster than
// `.querySelector('#id')`; the rule's premise doesn't hold.
// Webview scripts are the only DOM consumers in the codebase.
"files": ["client/src/**/*-webview*.ts"],
"files": ["client/src/**/*-webview*.ts", "client/src/dialog-editor/webview/**"],
"rules": {
"unicorn/prefer-query-selector": "off"
}
},
{
// Svelte 5 runes require `let`: `let x = $state(...)` and
// `let { ... } = $props()` are reassigned by the compiler's reactivity,
// which oxlint cannot see - it reports them as never-reassigned. `sort-vars`
// likewise fights the rune/prop declaration order. Scope: the dialog editor's
// Svelte webview components.
"files": ["client/src/dialog-editor/webview/**/*.svelte"],
"rules": {
"prefer-const": "off",
"sort-vars": "off"
}
},
{
// VS Code's `webview.postMessage()` (extension side),
// `acquireVsCodeApi().postMessage()` (webview side), and
// `worker_threads` `parentPort.postMessage()` are not the browser's
// `window.postMessage`; none take a `targetOrigin`.
"files": [
"client/src/editors/**",
"client/src/dialog-tree/**",
"client/src/webview-utils.ts",
"client/src/binary-editor/**"
"client/src/binary-editor/**",
"client/src/dialog-editor/**"
],
"rules": {
"unicorn/require-post-message-target-origin": "off"
Expand Down Expand Up @@ -366,6 +378,7 @@
"**/out/**",
"**/*.d.ts",
"server/src/user-messages.ts",
"binary-editor/test/harness/**"
"binary-editor/test/harness/**",
"client/src/dialog-editor/test/harness/**"
]
}
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Dependabot version-update PRs are intentionally disabled - there is no `.github/
- **User-facing messages:** Never call `connection.window.showInformationMessage/showWarningMessage/showErrorMessage` directly in server code. Use `showInfo()`, `showWarning()`, `showError()`, or `showErrorWithActions()` from `user-messages.ts` - they auto-decode `file://` URIs to readable paths. An oxlint rule enforces this.
- **Temporary artifacts:** Put transient test/build files under the repo-level `tmp/` directory (or `os.tmpdir()` when system temp is required). Do not create ad hoc temp directories under source trees like `server/test/`, `binary/test/`, or `scripts/**`.
- **Webview CSP - styles need `cspSource`, not a bare nonce.** A webview's `style-src` must include `{{cspSource}}`. VS Code's wrapped webview silently drops a `style-src 'nonce-...'` stylesheet that lacks `cspSource` (raw Chromium honors it, so headless/standalone renders pass while the real panel renders fully unstyled). Load CSS as `webview.asWebviewUri()` `<link>` elements with `style-src {{cspSource}}` (keep the nonce for `script-src` only); add each CSS dir to `localResourceRoots`. See `docs/architecture.md` (Webview CSP); guarded by `client/test/webview-csp.test.ts`.
- **Binary-editor webview changes can be rendered - don't fall back to a sketch without checking.** A headless Playwright harness in `binary-editor/test/harness/` loads the real `App.svelte` bundle in Chromium and writes per-format PNG screenshots (e.g. `render-pro-eff.mts` -> `shot-pro.png` + `shot-eff.png`, `render-itm.mts`, `render-spl.mts`, `render-cre.mts`, `render-map.mts`, `render-primitives.mts`). For any visual/CSS/layout change to the binary editor, render and inspect the screenshot rather than reasoning about the cascade blind. Run order: `cd binary && pnpm build` (only if `binary/src` changed), then `pnpm exec tsx binary-editor/test/harness/build.mts` (rebuilds `app.html` after any webview/Svelte/`styles.css` edit), then a driver `pnpm exec tsx binary-editor/test/harness/render-pro-eff.mts`. Prereqs (environment, not repo deps): Playwright + a Chromium browser on `PATH`. It is e2e-tier and intentionally excluded from `pnpm test`/`pnpm test:all`. See `binary-editor/test/harness/README.md` for the harness, and **`docs/binary-editor-ui-guidelines.md`** for the project's UI conventions and the review brief - it indexes the co-located writer-facing rules (`client/src/binary-editor/webview/AGENTS.md` for the render layer, `binary/src/AGENTS.md` for the layout schema), which auto-load when you edit those dirs. Review rendered screenshots against the guidelines.
- **Binary-editor webview changes can be rendered - don't fall back to a sketch without checking.** A headless Playwright harness in `binary-editor/test/harness/` loads the real `App.svelte` bundle in Chromium and writes per-format PNG screenshots (e.g. `render-pro-eff.mts` -> `shot-pro.png` + `shot-eff.png`, `render-itm.mts`, `render-spl.mts`, `render-cre.mts`, `render-map.mts`, `render-primitives.mts`). For any visual/CSS/layout change to the binary editor, render and inspect the screenshot rather than reasoning about the cascade blind. Run order: `cd binary && pnpm build` (only if `binary/src` changed), then `pnpm exec tsx binary-editor/test/harness/build.mts` (rebuilds `app.html` after any webview/Svelte/`styles.css` edit), then a driver `pnpm exec tsx binary-editor/test/harness/render-pro-eff.mts`. `playwright` is a pinned devDep; the Chromium browser it drives is the environment prereq (`pnpm exec playwright install chromium`). The drivers assert through PASS/FAIL gates and now run in CI as a regression suite via the separate `Harness` workflow (`scripts/test-harness.sh` - both editors' harnesses); they stay excluded from `pnpm test`/`pnpm test:all`, which run browserless. See `binary-editor/test/harness/README.md` for the harness, and **`docs/binary-editor-ui-guidelines.md`** for the project's UI conventions and the review brief - it indexes the co-located writer-facing rules (`client/src/binary-editor/webview/AGENTS.md` for the render layer, `binary/src/AGENTS.md` for the layout schema), which auto-load when you edit those dirs. Review rendered screenshots against the guidelines.
- **To run the whole extension in a real VS Code instance, use `pnpm dev:web`.** It starts [code-server](https://github.com/coder/code-server) (VS Code in a browser) with this repo loaded as an unpacked extension, so the LSP server and binary custom editor actually run - the screenshot harness above only renders the webview in isolation. Default serves plain HTTP on `0.0.0.0:8080` (override with `CODE_SERVER_PORT`/`CODE_SERVER_HOST`); it bootstraps a pinned code-server into the gitignored `.dev/` on first run, then builds via `build:dev`. The command runs a long-lived server in the foreground - background it (or run it as a separate task) if you need the shell, bind to the port your environment exposes, and confirm it is up before reporting a URL. The binary editor is a webview, so the browser must reach it over a secure context (`http://localhost` via a port-forward, or a trusted cert) or the editor renders blank. Full details and configuration: **`scripts/dev-web.md`**.
- **Use `pnpm` exclusively. Never use `npx`.** This is a hard requirement. Every time you reach for `npx`, `npm`, or any npm-series command, stop and use `pnpm exec` instead. Example: `pnpm exec playwright` not `npx playwright`. This applies to all contexts including one-off commands, scripts, subagents, and delegated tasks. If pnpm is not available in a context, install it first or use the workspace package.json scripts.
- **Milestone close-out commands (scope to what you touched).** Binary changes -> `pnpm exec vitest run --config binary/vitest.config.ts` (run from the repo root, not `cd binary` - several tests resolve `external/` and `client/testFixture/` fixtures relative to cwd); webview/Svelte changes -> the client tests plus the binary-editor render harness. Reserve `pnpm build:all` + `pnpm test:all` (~9 min) for changes that span subsystems or touch shared build infra, grammars, transpilers, or the server.
Expand Down Expand Up @@ -120,6 +120,8 @@ cd grammars/weidu-tp2 && pnpm test # or any grammars/*/

```

**Testing against real external files.** The `external/` mod trees are gitignored but REPRODUCIBLE - `pnpm test:external` (via `scripts/reset-external.sh` + `scripts/external-repos-lib.sh`) clones/checks them out at pinned refs. So real-corpus coverage belongs in a committed test, not a throwaway: tests that exercise real external files live under `server/test/integration/**` (run by `pnpm test:integration`, config `server/vitest.integration.config.ts`), using `test/integration/test-helpers.ts` (`FALLOUT_FIXTURES` = `external/fallout`, `IE_FIXTURES` = `external/infinity-engine`, `loadFixture`/`loadFixtures`). Gate a corpus sweep with `describe.skipIf(files.length === 0)` so it skips cleanly when the corpus is not checked out. Before placing any real-file test, read a sibling there (`integration/weidu-d.test.ts`, `fallout-ssl/rename.test.ts`) for the fixture/init conventions. Do NOT commit copies of gitignored `external/` files as fixtures, and do NOT hand-run a one-off script where this suite is the home.

## Publishing & Release

Releases are tag-driven via GitHub Actions. `docs/releasing.md` is the canonical reference: the tag scheme (which tag form triggers which workflow), and the per-stream release procedures for the extension, the three libraries, and the reusable Action - including the root/`server` version-identity invariant. The server and VSIX bundle their `@bgforge/*` libraries rather than depending on them at runtime, so the extension and the libraries release in any order. Consult it before cutting any release. See `docs/architecture.md` for packaging mechanics.
Expand All @@ -132,6 +134,7 @@ Releases are tag-driven via GitHub Actions. `docs/releasing.md` is the canonical
- `ts-morph` is pinned to `^27.0.2` across the server runtime and all transpiler subpackages so the bundled TypeScript matches the project's own `typescript ^5.9.3` pin. ts-morph bundles its TypeScript dependency rather than treating it as a peer, so the ts-morph version effectively chooses the TS compiler that runs against transpiler ASTs regardless of the workspace `typescript` pin. ts-morph 28 bundles TS 6.0 - TS 6 is on a 6.0.x line with no 6.1 published, fails the standard "matured target major" upgrade rule, and ts-morph 28 is therefore deferred until TS 6.1 ships. ts-morph 27.0.x is technically also on a 0-minor line, but the TS 5.9 alignment is the deciding factor. Bump in lockstep with the project's `typescript` pin: when `typescript` moves to 6.x, ts-morph moves to whichever ts-morph major bundles that TS line.
- `@types/node` tracks the latest LTS Node major, currently `^24.x`. The extension targets LTS Node only (minimum supported is 20), so do not bump `@types/node` to odd-numbered "Current" majors (e.g. 25.x) - that would expose type definitions for APIs not present at the supported runtime floor. Move it forward only when a new even-numbered Node release reaches LTS.
- `ini` (runtime dep of `@bgforge/format`) is held at `^6.x`; `7.0.0` is a major with potential parse/stringify behavior changes that need a changelog review before adoption.
- `playwright` (devDep) is pinned to an EXACT version (no caret) because the webview harnesses launch a browser from Playwright's version-keyed cache: the devbox and the `Harness` CI job download that browser via `playwright install`, and a caret drift to a version whose browser revision is not cached would break the harness run until a re-download. Bump the pin and re-run `playwright install` together. Its browser postinstall is skipped by pnpm's build-script gate (not in `onlyBuiltDependencies`), so a plain `pnpm install` never pulls ~150MB of browser - the harness paths install Chromium explicitly.
- `sslc-emscripten-noderawfs` (the built-in SSL compiler WASM, `server/package.json`) is an HTTPS GitHub-release tarball, and its `pnpm-lock.yaml` entry carries a hand-maintained `integrity` field. pnpm 11.4+ fails closed (`ERR_PNPM_MISSING_TARBALL_INTEGRITY`) on any tarball lockfile entry that lacks integrity, and URL-tarball resolvers only learn the hash on download - so when the package is reused from the store, the field is never emitted. Practical rule: **evolve the lockfile incrementally** (`pnpm install` / `pnpm update`); pnpm 11.5.0+ preserves the existing integrity across those, so `pnpm update` keeps working. Do **not** `rm pnpm-lock.yaml` to regenerate from scratch - that drops the field and breaks `pnpm update` until it is re-added. The release asset is immutable, so the hash is stable; if a full regen is ever unavoidable, restore the line `resolution: {integrity: sha512-oQEOCRLmCodC1MlcIVAqGyTb3aWLpJ7c9tQm4CF8hrf9feZoxcIvSfwKpkcq0FykigqVGdpFpkbwQBS8yqo28Q==, tarball: <url>}`. The same dep is also listed under `minimumReleaseAgeExclude` in `pnpm-workspace.yaml` (a non-registry tarball has no publish timestamp for the 24h maturity window to check).

## Architecture
Expand Down
11 changes: 4 additions & 7 deletions binary-editor/test/harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,14 @@ It is type-checked via `test/harness/tsconfig.json`, which includes the DOM lib

## Prerequisites

- **Playwright + a Chromium browser** must be available on `PATH` in whatever environment runs the harness.
Install Playwright and its browsers globally:
- **Playwright** is a pinned devDependency (`pnpm install` provides it), so `pnpm exec tsx <driver>` resolves
`import { chromium } from "playwright"` with no global install. Its browser postinstall is skipped by pnpm's
build-script gate, so install the one browser the drivers launch:

```
npm install -g playwright
playwright install chromium
pnpm exec playwright install chromium
```

Playwright is intentionally NOT listed in any `package.json` in this repo. It is an environment
prerequisite, not a repo dependency.

- **Node 20+** (matched to the project's minimum supported runtime).

- **`tsx`** - available via the repo's dev dependencies (`pnpm exec tsx ...`).
Expand Down
14 changes: 14 additions & 0 deletions binary-editor/test/harness/out-dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { mkdirSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

// Repo-level tmp/ for harness screenshots, keeping the source tree clean (project convention).
// This module lives in binary-editor/test/harness/, so the repo root is three levels up.
const here = path.dirname(fileURLToPath(import.meta.url));
export const SHOT_DIR = path.resolve(here, "../../../tmp");
mkdirSync(SHOT_DIR, { recursive: true });

/** Absolute path for a harness screenshot, under the repo tmp/ dir. */
export function shotPath(name: string): string {
return path.join(SHOT_DIR, name);
}
20 changes: 17 additions & 3 deletions binary-editor/test/harness/render-clip-sweep.mts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ async function runFormat(browser: Browser, label: string, uri: string, bytes: Ui
});
await page.goto("file://" + path.join(here, "app.html"));
await page.waitForSelector(".layout-root", { timeout: 5000 });
await page.waitForTimeout(200);

const found: ClipViolation[] = [];
// Check whatever a freshly-opened detail/list selection renders too: select the first list row if one is
Expand All @@ -109,7 +108,11 @@ async function runFormat(browser: Browser, label: string, uri: string, bytes: Ui
const firstRow = page.locator(".master .vlist .vrow").first();
if (await firstRow.count()) {
await firstRow.click().catch(() => undefined);
await page.waitForTimeout(150);
await page
.waitForFunction(() => document.querySelector(".detail .layout-root .field") !== null, undefined, {
timeout: 5000,
})
.catch(() => undefined);
found.push(...(await collectClipViolations(page, ctx + " (detail)")));
}
};
Expand All @@ -123,7 +126,18 @@ async function runFormat(browser: Browser, label: string, uri: string, bytes: Ui
const tab = tabs.nth(i);
const name = ((await tab.textContent()) ?? `tab${i}`).replace(/\s*\(\d.*\)\s*$/, "").trim();
await tab.click();
await page.waitForTimeout(200);
await page
.waitForFunction(
(expectedLabel) => {
const active = document.querySelector(
'.bb-tabs.primary button[role="tab"][aria-selected="true"]',
);
return !!active && (active.textContent ?? "").includes(expectedLabel);
},
name,
{ timeout: 5000 },
)
.catch(() => undefined);
await sweepCurrentView(`${label} > ${name}`);
}
}
Expand Down
Loading