dialog-ide 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +201 -0
  3. package/README.md +211 -0
  4. package/THIRD_PARTY_LICENSES.md +157 -0
  5. package/bin/darwin-arm64/aambundle +0 -0
  6. package/bin/darwin-arm64/dgdebug +0 -0
  7. package/bin/darwin-arm64/dialogc +0 -0
  8. package/bin/dialog-lib/stddebug.dg +112 -0
  9. package/bin/dialog-lib/stdlib.dg +6730 -0
  10. package/bin/dialog-lib/unit.dg +187 -0
  11. package/dist/cli/commands/run-skein.d.ts +17 -0
  12. package/dist/cli/commands/run-skein.d.ts.map +1 -0
  13. package/dist/cli/commands/run-skein.js +105 -0
  14. package/dist/cli/commands/run-skein.js.map +1 -0
  15. package/dist/cli/commands/sources.d.ts +12 -0
  16. package/dist/cli/commands/sources.d.ts.map +1 -0
  17. package/dist/cli/commands/sources.js +42 -0
  18. package/dist/cli/commands/sources.js.map +1 -0
  19. package/dist/cli/commands/test.d.ts +9 -0
  20. package/dist/cli/commands/test.d.ts.map +1 -0
  21. package/dist/cli/commands/test.js +41 -0
  22. package/dist/cli/commands/test.js.map +1 -0
  23. package/dist/cli/context.d.ts +9 -0
  24. package/dist/cli/context.d.ts.map +1 -0
  25. package/dist/cli/context.js +78 -0
  26. package/dist/cli/context.js.map +1 -0
  27. package/dist/cli.d.ts +3 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +32 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/dialog-cover-resize.d.ts +2 -0
  32. package/dist/dialog-cover-resize.d.ts.map +1 -0
  33. package/dist/dialog-cover-resize.js +46 -0
  34. package/dist/dialog-cover-resize.js.map +1 -0
  35. package/dist/dialog-export.d.ts +23 -0
  36. package/dist/dialog-export.d.ts.map +1 -0
  37. package/dist/dialog-export.js +133 -0
  38. package/dist/dialog-export.js.map +1 -0
  39. package/dist/dialog-feelies.d.ts +4 -0
  40. package/dist/dialog-feelies.d.ts.map +1 -0
  41. package/dist/dialog-feelies.js +62 -0
  42. package/dist/dialog-feelies.js.map +1 -0
  43. package/dist/dialog-outline.d.ts +19 -0
  44. package/dist/dialog-outline.d.ts.map +1 -0
  45. package/dist/dialog-outline.js +122 -0
  46. package/dist/dialog-outline.js.map +1 -0
  47. package/dist/dialog-project-init.d.ts +5 -0
  48. package/dist/dialog-project-init.d.ts.map +1 -0
  49. package/dist/dialog-project-init.js +107 -0
  50. package/dist/dialog-project-init.js.map +1 -0
  51. package/dist/dialog-source-coverage.d.ts +9 -0
  52. package/dist/dialog-source-coverage.d.ts.map +1 -0
  53. package/dist/dialog-source-coverage.js +61 -0
  54. package/dist/dialog-source-coverage.js.map +1 -0
  55. package/dist/dialog-source-decoration-provider.d.ts +11 -0
  56. package/dist/dialog-source-decoration-provider.d.ts.map +1 -0
  57. package/dist/dialog-source-decoration-provider.js +79 -0
  58. package/dist/dialog-source-decoration-provider.js.map +1 -0
  59. package/dist/dialog-symbol-index.d.ts +19 -0
  60. package/dist/dialog-symbol-index.d.ts.map +1 -0
  61. package/dist/dialog-symbol-index.js +107 -0
  62. package/dist/dialog-symbol-index.js.map +1 -0
  63. package/dist/dialog-symbol-provider.d.ts +5 -0
  64. package/dist/dialog-symbol-provider.d.ts.map +1 -0
  65. package/dist/dialog-symbol-provider.js +56 -0
  66. package/dist/dialog-symbol-provider.js.map +1 -0
  67. package/dist/dialog-web-export.d.ts +25 -0
  68. package/dist/dialog-web-export.d.ts.map +1 -0
  69. package/dist/dialog-web-export.js +283 -0
  70. package/dist/dialog-web-export.js.map +1 -0
  71. package/dist/dialog-workspace-symbol-provider.d.ts +16 -0
  72. package/dist/dialog-workspace-symbol-provider.d.ts.map +1 -0
  73. package/dist/dialog-workspace-symbol-provider.js +107 -0
  74. package/dist/dialog-workspace-symbol-provider.js.map +1 -0
  75. package/dist/dialog-workspace-watcher.d.ts +22 -0
  76. package/dist/dialog-workspace-watcher.d.ts.map +1 -0
  77. package/dist/dialog-workspace-watcher.js +102 -0
  78. package/dist/dialog-workspace-watcher.js.map +1 -0
  79. package/dist/dialoged/skein/compile-error.d.ts +10 -0
  80. package/dist/dialoged/skein/compile-error.d.ts.map +1 -0
  81. package/dist/dialoged/skein/compile-error.js +28 -0
  82. package/dist/dialoged/skein/compile-error.js.map +1 -0
  83. package/dist/dialoged/skein/dynamic.d.ts +14 -0
  84. package/dist/dialoged/skein/dynamic.d.ts.map +1 -0
  85. package/dist/dialoged/skein/dynamic.js +176 -0
  86. package/dist/dialoged/skein/dynamic.js.map +1 -0
  87. package/dist/dialoged/skein/frotz-build.d.ts +10 -0
  88. package/dist/dialoged/skein/frotz-build.d.ts.map +1 -0
  89. package/dist/dialoged/skein/frotz-build.js +70 -0
  90. package/dist/dialoged/skein/frotz-build.js.map +1 -0
  91. package/dist/dialoged/skein/index.d.ts +15 -0
  92. package/dist/dialoged/skein/index.d.ts.map +1 -0
  93. package/dist/dialoged/skein/index.js +31 -0
  94. package/dist/dialoged/skein/index.js.map +1 -0
  95. package/dist/dialoged/skein/io.d.ts +15 -0
  96. package/dist/dialoged/skein/io.d.ts.map +1 -0
  97. package/dist/dialoged/skein/io.js +53 -0
  98. package/dist/dialoged/skein/io.js.map +1 -0
  99. package/dist/dialoged/skein/persistence.d.ts +13 -0
  100. package/dist/dialoged/skein/persistence.d.ts.map +1 -0
  101. package/dist/dialoged/skein/persistence.js +228 -0
  102. package/dist/dialoged/skein/persistence.js.map +1 -0
  103. package/dist/dialoged/skein/process.d.ts +38 -0
  104. package/dist/dialoged/skein/process.d.ts.map +1 -0
  105. package/dist/dialoged/skein/process.js +189 -0
  106. package/dist/dialoged/skein/process.js.map +1 -0
  107. package/dist/dialoged/skein/progress.d.ts +17 -0
  108. package/dist/dialoged/skein/progress.d.ts.map +1 -0
  109. package/dist/dialoged/skein/progress.js +9 -0
  110. package/dist/dialoged/skein/progress.js.map +1 -0
  111. package/dist/dialoged/skein/project.d.ts +44 -0
  112. package/dist/dialoged/skein/project.d.ts.map +1 -0
  113. package/dist/dialoged/skein/project.js +223 -0
  114. package/dist/dialoged/skein/project.js.map +1 -0
  115. package/dist/dialoged/skein/search.d.ts +15 -0
  116. package/dist/dialoged/skein/search.d.ts.map +1 -0
  117. package/dist/dialoged/skein/search.js +99 -0
  118. package/dist/dialoged/skein/search.js.map +1 -0
  119. package/dist/dialoged/skein/service.d.ts +91 -0
  120. package/dist/dialoged/skein/service.d.ts.map +1 -0
  121. package/dist/dialoged/skein/service.js +1097 -0
  122. package/dist/dialoged/skein/service.js.map +1 -0
  123. package/dist/dialoged/skein/session.d.ts +96 -0
  124. package/dist/dialoged/skein/session.d.ts.map +1 -0
  125. package/dist/dialoged/skein/session.js +690 -0
  126. package/dist/dialoged/skein/session.js.map +1 -0
  127. package/dist/dialoged/skein/syntax.d.ts +5 -0
  128. package/dist/dialoged/skein/syntax.d.ts.map +1 -0
  129. package/dist/dialoged/skein/syntax.js +150 -0
  130. package/dist/dialoged/skein/syntax.js.map +1 -0
  131. package/dist/dialoged/skein/trace.d.ts +31 -0
  132. package/dist/dialoged/skein/trace.d.ts.map +1 -0
  133. package/dist/dialoged/skein/trace.js +172 -0
  134. package/dist/dialoged/skein/trace.js.map +1 -0
  135. package/dist/dialoged/skein/tree.d.ts +114 -0
  136. package/dist/dialoged/skein/tree.d.ts.map +1 -0
  137. package/dist/dialoged/skein/tree.js +701 -0
  138. package/dist/dialoged/skein/tree.js.map +1 -0
  139. package/dist/dialoged/skein/ui/ansi.d.ts +4 -0
  140. package/dist/dialoged/skein/ui/ansi.d.ts.map +1 -0
  141. package/dist/dialoged/skein/ui/ansi.js +215 -0
  142. package/dist/dialoged/skein/ui/ansi.js.map +1 -0
  143. package/dist/dialoged/skein/ui/diff.d.ts +7 -0
  144. package/dist/dialoged/skein/ui/diff.d.ts.map +1 -0
  145. package/dist/dialoged/skein/ui/diff.js +17 -0
  146. package/dist/dialoged/skein/ui/diff.js.map +1 -0
  147. package/dist/dialoged/skein/ui/knot-menu.d.ts +5 -0
  148. package/dist/dialoged/skein/ui/knot-menu.d.ts.map +1 -0
  149. package/dist/dialoged/skein/ui/knot-menu.js +89 -0
  150. package/dist/dialoged/skein/ui/knot-menu.js.map +1 -0
  151. package/dist/dialoged/skein/ui/marker-colors.d.ts +5 -0
  152. package/dist/dialoged/skein/ui/marker-colors.d.ts.map +1 -0
  153. package/dist/dialoged/skein/ui/marker-colors.js +18 -0
  154. package/dist/dialoged/skein/ui/marker-colors.js.map +1 -0
  155. package/dist/dialoged/skein/ui/render.d.ts +13 -0
  156. package/dist/dialoged/skein/ui/render.d.ts.map +1 -0
  157. package/dist/dialoged/skein/ui/render.js +332 -0
  158. package/dist/dialoged/skein/ui/render.js.map +1 -0
  159. package/dist/dialoged/skein/ui/traceRender.d.ts +11 -0
  160. package/dist/dialoged/skein/ui/traceRender.d.ts.map +1 -0
  161. package/dist/dialoged/skein/ui/traceRender.js +139 -0
  162. package/dist/dialoged/skein/ui/traceRender.js.map +1 -0
  163. package/dist/dialoged/skein/ui/tree-pane.d.ts +3 -0
  164. package/dist/dialoged/skein/ui/tree-pane.d.ts.map +1 -0
  165. package/dist/dialoged/skein/ui/tree-pane.js +135 -0
  166. package/dist/dialoged/skein/ui/tree-pane.js.map +1 -0
  167. package/dist/dialoged/skein/ui/webExportPage.d.ts +29 -0
  168. package/dist/dialoged/skein/ui/webExportPage.d.ts.map +1 -0
  169. package/dist/dialoged/skein/ui/webExportPage.js +72 -0
  170. package/dist/dialoged/skein/ui/webExportPage.js.map +1 -0
  171. package/dist/extension.d.ts +4 -0
  172. package/dist/extension.d.ts.map +1 -0
  173. package/dist/extension.js +885 -0
  174. package/dist/extension.js.map +1 -0
  175. package/dist/session-runner.d.ts +23 -0
  176. package/dist/session-runner.d.ts.map +1 -0
  177. package/dist/session-runner.js +92 -0
  178. package/dist/session-runner.js.map +1 -0
  179. package/language-configuration.json +43 -0
  180. package/media/icon.png +0 -0
  181. package/media/icons/arrow-down-circle-solid.svg +3 -0
  182. package/media/icons/arrow-down-solid.svg +3 -0
  183. package/media/icons/arrow-path-solid.svg +3 -0
  184. package/media/icons/arrow-right-solid.svg +3 -0
  185. package/media/icons/arrow-up-on-square-stack-solid.svg +5 -0
  186. package/media/icons/arrow-up-solid.svg +3 -0
  187. package/media/icons/arrow-uturn-left.svg +3 -0
  188. package/media/icons/arrow-uturn-right.svg +3 -0
  189. package/media/icons/bookmark-solid.svg +3 -0
  190. package/media/icons/bug-ant-solid.svg +3 -0
  191. package/media/icons/check-badge-solid.svg +3 -0
  192. package/media/icons/check-circle-solid.svg +3 -0
  193. package/media/icons/chevron-double-down-solid.svg +4 -0
  194. package/media/icons/chevron-double-up-solid.svg +4 -0
  195. package/media/icons/command-line-solid.svg +3 -0
  196. package/media/icons/cpu-chip-solid.svg +4 -0
  197. package/media/icons/dg-file-dark.svg +6 -0
  198. package/media/icons/dg-file-light.svg +6 -0
  199. package/media/icons/ellipsis-vertical-solid.svg +3 -0
  200. package/media/icons/exclamation-triangle.svg +3 -0
  201. package/media/icons/folder-arrow-down-solid.svg +3 -0
  202. package/media/icons/globe-alt.svg +3 -0
  203. package/media/icons/lock-closed-solid.svg +3 -0
  204. package/media/icons/magnifying-glass-solid.svg +3 -0
  205. package/media/icons/pencil-solid.svg +4 -0
  206. package/media/icons/play-solid.svg +3 -0
  207. package/media/icons/plus-circle-solid.svg +3 -0
  208. package/media/icons/scissors-solid.svg +5 -0
  209. package/media/icons/tag-solid.svg +3 -0
  210. package/media/icons/trash-solid.svg +3 -0
  211. package/media/icons/x-circle-solid.svg +3 -0
  212. package/media/js/datastar.js +9 -0
  213. package/media/js/main.js +1109 -0
  214. package/media/js/skein-loader.js +10 -0
  215. package/media/js/trace-loader.js +4 -0
  216. package/media/js/trace.js +147 -0
  217. package/media/style.css +5783 -0
  218. package/package.json +231 -0
  219. package/resources/bundle/default-cover.png +0 -0
  220. package/resources/bundle/introduction-to-if.pdf +0 -0
  221. package/resources/bundle/play-if-card.pdf +1990 -1
  222. package/resources/bundle/play.css +432 -0
  223. package/resources/bundle/style.css +450 -0
  224. package/resources/dfrotz-skein-patch.dg +4 -0
  225. package/syntaxes/dialog.tmLanguage.json +490 -0
@@ -0,0 +1,701 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SkeinTree = exports.ALL_MARKERS = exports.KnotLockedError = exports.LabelConflictError = void 0;
4
+ exports.isEffectivelyLocked = isEffectivelyLocked;
5
+ exports.isValidMarker = isValidMarker;
6
+ const immutable_1 = require("immutable");
7
+ class LabelConflictError extends Error {
8
+ constructor(label) {
9
+ super(`Label "${label}" is already used by another knot.`);
10
+ this.name = 'LabelConflictError';
11
+ }
12
+ }
13
+ exports.LabelConflictError = LabelConflictError;
14
+ class KnotLockedError extends Error {
15
+ constructor() {
16
+ super('Cannot delete: this knot or one of its descendants is locked or labeled.');
17
+ this.name = 'KnotLockedError';
18
+ }
19
+ }
20
+ exports.KnotLockedError = KnotLockedError;
21
+ function isEffectivelyLocked(knot) {
22
+ return knot.locked || knot.label !== null;
23
+ }
24
+ function responsesMatch(blessed, fresh) {
25
+ return blessed !== null && blessed.text === fresh.text && blessed.inputType === fresh.inputType;
26
+ }
27
+ function responseAfterUpdate(existing, fresh) {
28
+ return responsesMatch(existing.response, fresh) ? null : fresh;
29
+ }
30
+ exports.ALL_MARKERS = [1, 2, 3, 4];
31
+ function isValidMarker(value) {
32
+ return value === 1 || value === 2 || value === 3 || value === 4;
33
+ }
34
+ class SkeinTree {
35
+ constructor(engine, seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates) {
36
+ this.engine = engine;
37
+ this.seed = seed;
38
+ this.knots = knots;
39
+ this.knotStates = knotStates;
40
+ this.activeKnotId = activeKnotId;
41
+ this.collapsedKnotIds = collapsedKnotIds;
42
+ this.dynamicStates = dynamicStates;
43
+ }
44
+ static newTree(engine, seed) {
45
+ const initialKnot = {
46
+ id: 0,
47
+ command: 'START',
48
+ response: {
49
+ text: 'Welcome to the game. > ',
50
+ inputType: 'line'
51
+ },
52
+ unblessedResponse: null,
53
+ parentId: null,
54
+ label: 'START',
55
+ locked: false,
56
+ marker: null
57
+ };
58
+ const initialState = {
59
+ state: 'valid',
60
+ treeState: 'valid',
61
+ selectedChild: null,
62
+ children: []
63
+ };
64
+ return new SkeinTree(engine, seed, (0, immutable_1.Map)().set(0, initialKnot), (0, immutable_1.Map)().set(0, initialState), 0, (0, immutable_1.Set)(), (0, immutable_1.Map)());
65
+ }
66
+ addChild(parentId, command, response) {
67
+ const newId = this.generateNextId();
68
+ const parentKnot = this.knots.get(parentId);
69
+ if (!parentKnot) {
70
+ throw new Error(`Parent knot ${parentId} not found`);
71
+ }
72
+ const newKnot = {
73
+ id: newId,
74
+ command,
75
+ response: null,
76
+ unblessedResponse: response,
77
+ parentId,
78
+ label: null,
79
+ locked: false,
80
+ marker: null
81
+ };
82
+ const newState = {
83
+ state: 'new',
84
+ treeState: 'new',
85
+ selectedChild: null,
86
+ children: []
87
+ };
88
+ const parentState = this.knotStates.get(parentId);
89
+ const updatedParentState = {
90
+ ...parentState,
91
+ children: [...parentState.children, newId],
92
+ selectedChild: newId
93
+ };
94
+ const knots = this.knots.set(newId, newKnot);
95
+ let knotStates = this.knotStates
96
+ .set(newId, newState)
97
+ .set(parentId, updatedParentState);
98
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, parentId);
99
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
100
+ }
101
+ updateKnotCommandAndResponse(id, command, response) {
102
+ const knot = this.knots.get(id);
103
+ if (!knot) {
104
+ throw new Error(`Knot ${id} not found`);
105
+ }
106
+ const updatedKnot = {
107
+ ...knot,
108
+ command,
109
+ unblessedResponse: responseAfterUpdate(knot, response)
110
+ };
111
+ const knots = this.knots.set(id, updatedKnot);
112
+ const knotStates = SkeinTree.propagateOwnStateChange(knots, this.knotStates, id, updatedKnot);
113
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
114
+ }
115
+ updateKnotResponse(id, response) {
116
+ const knot = this.knots.get(id);
117
+ if (!knot) {
118
+ throw new Error(`Knot ${id} not found`);
119
+ }
120
+ const updatedKnot = {
121
+ ...knot,
122
+ unblessedResponse: responseAfterUpdate(knot, response)
123
+ };
124
+ const knots = this.knots.set(id, updatedKnot);
125
+ const knotStates = SkeinTree.propagateOwnStateChange(knots, this.knotStates, id, updatedKnot);
126
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
127
+ }
128
+ blessKnot(id) {
129
+ const knot = this.knots.get(id);
130
+ if (!knot) {
131
+ throw new Error(`Knot ${id} not found`);
132
+ }
133
+ if (!knot.unblessedResponse) {
134
+ return this;
135
+ }
136
+ const updatedKnot = {
137
+ ...knot,
138
+ response: knot.unblessedResponse,
139
+ unblessedResponse: null
140
+ };
141
+ const knots = this.knots.set(id, updatedKnot);
142
+ const knotStates = SkeinTree.propagateOwnStateChange(knots, this.knotStates, id, updatedKnot);
143
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
144
+ }
145
+ blessTranscript(id) {
146
+ if (!this.knots.get(id)) {
147
+ throw new Error(`Knot ${id} not found`);
148
+ }
149
+ let tree = this;
150
+ for (const pathId of this.pathFromRoot(id)) {
151
+ const knot = tree.knots.get(pathId);
152
+ if (SkeinTree.computeKnotState(knot) !== 'valid') {
153
+ tree = tree.blessKnot(pathId);
154
+ }
155
+ }
156
+ return tree;
157
+ }
158
+ deleteKnot(id) {
159
+ const knot = this.knots.get(id);
160
+ if (!knot) {
161
+ throw new Error(`Knot ${id} not found`);
162
+ }
163
+ const subtreeIds = this.collectSubtreeIds(id);
164
+ if (subtreeIds.some((subtreeId) => isEffectivelyLocked(this.knots.get(subtreeId)))) {
165
+ throw new KnotLockedError();
166
+ }
167
+ let knots = this.knots;
168
+ let knotStates = this.knotStates;
169
+ let collapsedKnotIds = this.collapsedKnotIds;
170
+ let dynamicStates = this.dynamicStates;
171
+ for (const descendantId of subtreeIds) {
172
+ knots = knots.delete(descendantId);
173
+ knotStates = knotStates.delete(descendantId);
174
+ collapsedKnotIds = collapsedKnotIds.remove(descendantId);
175
+ dynamicStates = dynamicStates.delete(descendantId);
176
+ }
177
+ if (knot.parentId !== null) {
178
+ knotStates = SkeinTree.removeChildFromParent(knotStates, knot.parentId, id);
179
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, knot.parentId);
180
+ }
181
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, collapsedKnotIds, dynamicStates);
182
+ }
183
+ spliceKnot(id) {
184
+ const knot = this.knots.get(id);
185
+ if (!knot) {
186
+ throw new Error(`Knot ${id} not found`);
187
+ }
188
+ const knotState = this.knotStates.get(id);
189
+ const childIds = knotState.children;
190
+ let knots = this.knots.delete(id);
191
+ let knotStates = this.knotStates.delete(id);
192
+ for (const childId of childIds) {
193
+ const child = knots.get(childId);
194
+ knots = knots.set(childId, { ...child, parentId: knot.parentId });
195
+ }
196
+ if (knot.parentId !== null) {
197
+ const parentState = knotStates.get(knot.parentId);
198
+ const index = parentState.children.indexOf(id);
199
+ const children = index === -1
200
+ ? [...parentState.children, ...childIds]
201
+ : [...parentState.children.slice(0, index), ...childIds, ...parentState.children.slice(index + 1)];
202
+ knotStates = knotStates.set(knot.parentId, {
203
+ ...parentState,
204
+ children,
205
+ selectedChild: parentState.selectedChild === id
206
+ ? (childIds.length > 0 ? childIds[0] : null)
207
+ : parentState.selectedChild
208
+ });
209
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, knot.parentId);
210
+ }
211
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, this.activeKnotId, this.collapsedKnotIds.remove(id), this.dynamicStates.delete(id));
212
+ }
213
+ nextId() {
214
+ return this.generateNextId();
215
+ }
216
+ insertParent(id, command, response) {
217
+ const knot = this.knots.get(id);
218
+ if (!knot) {
219
+ throw new Error(`Knot ${id} not found`);
220
+ }
221
+ const newId = this.generateNextId();
222
+ const oldParentId = knot.parentId;
223
+ const newParentKnot = {
224
+ id: newId,
225
+ command,
226
+ response: null,
227
+ unblessedResponse: response,
228
+ parentId: oldParentId,
229
+ label: null,
230
+ locked: false,
231
+ marker: null
232
+ };
233
+ const newParentState = {
234
+ state: 'new',
235
+ treeState: 'new',
236
+ selectedChild: id,
237
+ children: [id]
238
+ };
239
+ const updatedKnot = { ...knot, parentId: newId };
240
+ let knots = this.knots.set(newId, newParentKnot).set(id, updatedKnot);
241
+ let knotStates = this.knotStates.set(newId, newParentState);
242
+ let collapsedKnotIds = this.collapsedKnotIds;
243
+ let dynamicStates = this.dynamicStates;
244
+ let activeKnotId = this.activeKnotId;
245
+ if (oldParentId !== null) {
246
+ const grandparentState = knotStates.get(oldParentId);
247
+ const index = grandparentState.children.indexOf(id);
248
+ const children = index === -1
249
+ ? grandparentState.children
250
+ : [...grandparentState.children.slice(0, index), newId, ...grandparentState.children.slice(index + 1)];
251
+ knotStates = knotStates.set(oldParentId, {
252
+ ...grandparentState,
253
+ children,
254
+ selectedChild: grandparentState.selectedChild === id ? newId : grandparentState.selectedChild
255
+ });
256
+ const otherId = children.find((cid) => cid !== newId && knots.get(cid).command === command) ?? null;
257
+ if (otherId !== null) {
258
+ const merged = new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates)
259
+ .mergeSiblingInto(newId, otherId);
260
+ knots = merged.knots;
261
+ knotStates = merged.knotStates;
262
+ collapsedKnotIds = merged.collapsedKnotIds;
263
+ dynamicStates = merged.dynamicStates;
264
+ activeKnotId = merged.activeKnotId;
265
+ knotStates = SkeinTree.absorbSiblingInParent(knotStates, oldParentId, newId, otherId);
266
+ }
267
+ }
268
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, newId);
269
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates);
270
+ }
271
+ setLabel(id, label) {
272
+ const knot = this.knots.get(id);
273
+ if (!knot) {
274
+ throw new Error(`Knot ${id} not found`);
275
+ }
276
+ if (label !== null) {
277
+ const existing = this.findByLabel(label);
278
+ if (existing && existing.id !== id) {
279
+ throw new LabelConflictError(label);
280
+ }
281
+ }
282
+ const updatedKnot = {
283
+ ...knot,
284
+ label
285
+ };
286
+ return new SkeinTree(this.engine, this.seed, this.knots.set(id, updatedKnot), this.knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
287
+ }
288
+ findByLabel(label) {
289
+ for (const knot of this.knots.valueSeq()) {
290
+ if (knot.label === label) {
291
+ return knot;
292
+ }
293
+ }
294
+ return null;
295
+ }
296
+ setMarker(id, marker) {
297
+ const knot = this.knots.get(id);
298
+ if (!knot) {
299
+ throw new Error(`Knot ${id} not found`);
300
+ }
301
+ return new SkeinTree(this.engine, this.seed, this.knots.set(id, { ...knot, marker }), this.knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
302
+ }
303
+ renameCommand(id, command) {
304
+ const knot = this.knots.get(id);
305
+ if (!knot) {
306
+ throw new Error(`Knot ${id} not found`);
307
+ }
308
+ if (command === knot.command) {
309
+ return this;
310
+ }
311
+ let knots = this.knots.set(id, { ...knot, command });
312
+ let knotStates = this.knotStates;
313
+ let collapsedKnotIds = this.collapsedKnotIds;
314
+ let dynamicStates = this.dynamicStates;
315
+ let activeKnotId = this.activeKnotId;
316
+ if (knot.parentId !== null) {
317
+ const parentState = knotStates.get(knot.parentId);
318
+ const otherId = parentState.children.find((cid) => cid !== id && knots.get(cid).command === command) ?? null;
319
+ if (otherId !== null) {
320
+ const merged = new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates)
321
+ .mergeSiblingInto(id, otherId);
322
+ knots = merged.knots;
323
+ knotStates = merged.knotStates;
324
+ collapsedKnotIds = merged.collapsedKnotIds;
325
+ dynamicStates = merged.dynamicStates;
326
+ activeKnotId = merged.activeKnotId;
327
+ knotStates = SkeinTree.absorbSiblingInParent(knotStates, knot.parentId, id, otherId);
328
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, knot.parentId);
329
+ }
330
+ }
331
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates);
332
+ }
333
+ mergeSiblingInto(keepId, otherId) {
334
+ const keepKnot = this.knots.get(keepId);
335
+ const otherKnot = this.knots.get(otherId);
336
+ const otherState = this.knotStates.get(otherId);
337
+ let knots = this.knots.set(keepId, {
338
+ ...keepKnot,
339
+ label: keepKnot.label ?? otherKnot.label,
340
+ locked: keepKnot.locked || otherKnot.locked,
341
+ marker: keepKnot.marker ?? otherKnot.marker
342
+ });
343
+ let knotStates = this.knotStates;
344
+ let collapsedKnotIds = this.collapsedKnotIds;
345
+ let dynamicStates = this.dynamicStates;
346
+ let activeKnotId = this.activeKnotId;
347
+ for (const otherChildId of otherState.children) {
348
+ const otherChildCommand = knots.get(otherChildId).command;
349
+ const keepState = knotStates.get(keepId);
350
+ const matchedKeepChildId = keepState.children.find((cid) => knots.get(cid).command === otherChildCommand) ?? null;
351
+ if (matchedKeepChildId !== null) {
352
+ const merged = new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates)
353
+ .mergeSiblingInto(matchedKeepChildId, otherChildId);
354
+ knots = merged.knots;
355
+ knotStates = merged.knotStates;
356
+ collapsedKnotIds = merged.collapsedKnotIds;
357
+ dynamicStates = merged.dynamicStates;
358
+ activeKnotId = merged.activeKnotId;
359
+ }
360
+ else {
361
+ knots = knots.set(otherChildId, { ...knots.get(otherChildId), parentId: keepId });
362
+ const ks = knotStates.get(keepId);
363
+ knotStates = knotStates.set(keepId, {
364
+ ...ks,
365
+ children: [...ks.children, otherChildId],
366
+ selectedChild: ks.selectedChild ?? otherChildId
367
+ });
368
+ }
369
+ }
370
+ knots = knots.delete(otherId);
371
+ knotStates = knotStates.delete(otherId);
372
+ collapsedKnotIds = collapsedKnotIds.remove(otherId);
373
+ dynamicStates = dynamicStates.delete(otherId);
374
+ if (activeKnotId === otherId) {
375
+ activeKnotId = keepId;
376
+ }
377
+ const keepState = knotStates.get(keepId);
378
+ const treeState = keepState.children.reduce((acc, cid) => SkeinTree.maxStatus(acc, knotStates.get(cid).treeState), keepState.state);
379
+ knotStates = knotStates.set(keepId, { ...keepState, treeState });
380
+ return new SkeinTree(this.engine, this.seed, knots, knotStates, activeKnotId, collapsedKnotIds, dynamicStates);
381
+ }
382
+ static absorbSiblingInParent(knotStates, parentId, keepId, otherId) {
383
+ const parentState = knotStates.get(parentId);
384
+ const children = parentState.children.filter((cid) => cid !== otherId);
385
+ return knotStates.set(parentId, {
386
+ ...parentState,
387
+ children,
388
+ selectedChild: parentState.selectedChild === otherId ? keepId : parentState.selectedChild
389
+ });
390
+ }
391
+ setLockStatus(id, locked) {
392
+ const knot = this.knots.get(id);
393
+ if (!knot) {
394
+ throw new Error(`Knot ${id} not found`);
395
+ }
396
+ const updatedKnot = {
397
+ ...knot,
398
+ locked
399
+ };
400
+ return new SkeinTree(this.engine, this.seed, this.knots.set(id, updatedKnot), this.knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
401
+ }
402
+ getKnot(id) {
403
+ return this.knots.get(id) || null;
404
+ }
405
+ promptTypeAt(id) {
406
+ if (id === null) {
407
+ return 'line';
408
+ }
409
+ const knot = this.knots.get(id);
410
+ const response = knot?.response ?? knot?.unblessedResponse;
411
+ return response?.inputType ?? 'line';
412
+ }
413
+ updateDynamicState(id, state) {
414
+ if (!this.knots.get(id)) {
415
+ throw new Error(`Knot ${id} not found`);
416
+ }
417
+ return new SkeinTree(this.engine, this.seed, this.knots, this.knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates.set(id, state));
418
+ }
419
+ getDynamicState(id) {
420
+ return this.dynamicStates.get(id) ?? null;
421
+ }
422
+ findChildId(parentId, command) {
423
+ const parentState = this.knotStates.get(parentId);
424
+ if (!parentState) {
425
+ return null;
426
+ }
427
+ for (const childId of parentState.children) {
428
+ if (this.knots.get(childId)?.command === command) {
429
+ return childId;
430
+ }
431
+ }
432
+ return null;
433
+ }
434
+ sortedChildren(parentId) {
435
+ const state = this.knotStates.get(parentId);
436
+ if (!state) {
437
+ return [];
438
+ }
439
+ return state.children.map((id) => this.getDerivedKnot(id)).sort((a, b) => a.command.localeCompare(b.command));
440
+ }
441
+ commandPath(id) {
442
+ if (!this.knots.get(id)) {
443
+ throw new Error(`Knot ${id} not found`);
444
+ }
445
+ return this.pathFromRoot(id)
446
+ .filter((pathId) => pathId !== 0)
447
+ .map((pathId) => ({ id: pathId, command: this.knots.get(pathId).command }));
448
+ }
449
+ getAllKnots() {
450
+ return this.knots.valueSeq().toArray();
451
+ }
452
+ getDerivedKnot(id) {
453
+ const knot = this.knots.get(id);
454
+ if (!knot) {
455
+ return null;
456
+ }
457
+ const state = this.knotStates.get(id);
458
+ return {
459
+ id: knot.id,
460
+ command: knot.command,
461
+ response: knot.response ? knot.response.text : '',
462
+ unblessedResponse: knot.unblessedResponse ? knot.unblessedResponse.text : null,
463
+ state: state ? state.state : SkeinTree.computeKnotState(knot),
464
+ treeState: state ? state.treeState : SkeinTree.computeKnotState(knot),
465
+ parentId: knot.parentId,
466
+ children: state ? state.children : [],
467
+ selectedChild: state ? state.selectedChild : null,
468
+ inputType: knot.response ? knot.response.inputType : 'line',
469
+ label: knot.label,
470
+ locked: knot.locked,
471
+ marker: knot.marker,
472
+ collapsed: this.collapsedKnotIds.has(id)
473
+ };
474
+ }
475
+ visibleKnotIdsForMarkerFilter(marker) {
476
+ const visible = new globalThis.Set();
477
+ const visit = (id) => {
478
+ const knot = this.knots.get(id);
479
+ const state = this.knotStates.get(id);
480
+ const anyChildMatches = state.children.map(visit).some(Boolean);
481
+ const selfMatches = knot.marker === marker;
482
+ if (anyChildMatches || selfMatches) {
483
+ visible.add(id);
484
+ return true;
485
+ }
486
+ return false;
487
+ };
488
+ visit(0);
489
+ return visible;
490
+ }
491
+ static computeKnotState(knot) {
492
+ if (knot.response) {
493
+ if (knot.unblessedResponse && knot.response.text !== knot.unblessedResponse.text) {
494
+ return 'error';
495
+ }
496
+ return 'valid';
497
+ }
498
+ return 'new';
499
+ }
500
+ static maxStatus(a, b) {
501
+ if (a === 'error' || b === 'error') {
502
+ return 'error';
503
+ }
504
+ if (a === 'new' || b === 'new') {
505
+ return 'new';
506
+ }
507
+ return 'valid';
508
+ }
509
+ static propagateOwnStateChange(knots, knotStates, id, updatedKnot) {
510
+ const state = knotStates.get(id);
511
+ const withUpdatedState = knotStates.set(id, { ...state, state: SkeinTree.computeKnotState(updatedKnot) });
512
+ return SkeinTree.propagateTreeState(knots, withUpdatedState, id);
513
+ }
514
+ static propagateTreeState(knots, knotStates, startId) {
515
+ let states = knotStates;
516
+ let currentId = startId;
517
+ while (currentId !== null) {
518
+ const current = states.get(currentId);
519
+ const computed = current.children.reduce((acc, childId) => SkeinTree.maxStatus(acc, states.get(childId).treeState), current.state);
520
+ states = states.set(currentId, { ...current, treeState: computed });
521
+ currentId = knots.get(currentId).parentId;
522
+ }
523
+ return states;
524
+ }
525
+ collectSubtreeIds(id) {
526
+ const ids = [id];
527
+ const state = this.knotStates.get(id);
528
+ if (state) {
529
+ for (const childId of state.children) {
530
+ ids.push(...this.collectSubtreeIds(childId));
531
+ }
532
+ }
533
+ return ids;
534
+ }
535
+ pathFromRoot(id) {
536
+ const path = [];
537
+ let current = this.knots.get(id);
538
+ while (current) {
539
+ path.unshift(current.id);
540
+ current = current.parentId !== null ? this.knots.get(current.parentId) : undefined;
541
+ }
542
+ return path;
543
+ }
544
+ static removeChildFromParent(knotStates, parentId, childId) {
545
+ const parentState = knotStates.get(parentId);
546
+ if (!parentState) {
547
+ return knotStates;
548
+ }
549
+ const children = parentState.children.filter((existingId) => existingId !== childId);
550
+ return knotStates.set(parentId, {
551
+ ...parentState,
552
+ children,
553
+ selectedChild: parentState.selectedChild === childId
554
+ ? (children.length > 0 ? children[0] : null)
555
+ : parentState.selectedChild
556
+ });
557
+ }
558
+ static fromKnots(engine, seed, wireKnots) {
559
+ const sorted = [...wireKnots].sort((a, b) => a.id - b.id);
560
+ const childrenByParent = new globalThis.Map();
561
+ for (const knot of sorted) {
562
+ if (knot.parentId !== null) {
563
+ const children = childrenByParent.get(knot.parentId) ?? [];
564
+ children.push(knot.id);
565
+ childrenByParent.set(knot.parentId, children);
566
+ }
567
+ }
568
+ let knots = (0, immutable_1.Map)();
569
+ let knotStates = (0, immutable_1.Map)();
570
+ for (const knot of sorted) {
571
+ const children = childrenByParent.get(knot.id) ?? [];
572
+ const knotState = SkeinTree.computeKnotState(knot);
573
+ knots = knots.set(knot.id, knot);
574
+ knotStates = knotStates.set(knot.id, {
575
+ state: knotState,
576
+ treeState: knotState,
577
+ selectedChild: children.length > 0 ? children[0] : null,
578
+ children
579
+ });
580
+ }
581
+ const leafIds = sorted.filter((knot) => !childrenByParent.has(knot.id)).map((knot) => knot.id);
582
+ for (const leafId of leafIds) {
583
+ knotStates = SkeinTree.propagateTreeState(knots, knotStates, leafId);
584
+ }
585
+ let activeKnotId = 0;
586
+ let cursor = 0;
587
+ while (cursor !== null) {
588
+ activeKnotId = cursor;
589
+ cursor = knotStates.get(cursor)?.selectedChild ?? null;
590
+ }
591
+ return new SkeinTree(engine, seed, knots, knotStates, activeKnotId, (0, immutable_1.Set)(), (0, immutable_1.Map)());
592
+ }
593
+ generateNextId() {
594
+ let maxId = -1;
595
+ this.knots.forEach((knot) => {
596
+ if (knot.id > maxId) {
597
+ maxId = knot.id;
598
+ }
599
+ });
600
+ return maxId + 1;
601
+ }
602
+ getEngine() {
603
+ return this.engine;
604
+ }
605
+ getSeed() {
606
+ return this.seed;
607
+ }
608
+ getActiveKnotId() {
609
+ return this.activeKnotId;
610
+ }
611
+ getSelectedLeafId() {
612
+ let currentId = 0;
613
+ for (;;) {
614
+ const state = this.knotStates.get(currentId);
615
+ if (!state || state.selectedChild === null) {
616
+ return currentId;
617
+ }
618
+ currentId = state.selectedChild;
619
+ }
620
+ }
621
+ knotIdsWithStatus(status) {
622
+ return this.knotStates
623
+ .filter((state) => state.state === status)
624
+ .keySeq()
625
+ .toArray()
626
+ .sort((a, b) => a - b);
627
+ }
628
+ getLeafIds() {
629
+ return this.knotStates
630
+ .filter((state) => state.children.length === 0)
631
+ .keySeq()
632
+ .toArray()
633
+ .sort((a, b) => a - b);
634
+ }
635
+ setActiveKnotId(id) {
636
+ return new SkeinTree(this.engine, this.seed, this.knots, this.knotStates, id, this.collapsedKnotIds, this.dynamicStates);
637
+ }
638
+ selectKnot(id) {
639
+ if (!this.knots.get(id)) {
640
+ throw new Error(`Knot ${id} not found`);
641
+ }
642
+ return this.selectPath(id).extendSelection(id).setActiveKnotId(id);
643
+ }
644
+ selectPath(id) {
645
+ const path = this.pathFromRoot(id);
646
+ let knotStates = this.knotStates;
647
+ for (let i = 0; i < path.length - 1; i++) {
648
+ const parentId = path[i];
649
+ const childId = path[i + 1];
650
+ const parentState = knotStates.get(parentId);
651
+ if (parentState.selectedChild !== childId) {
652
+ knotStates = knotStates.set(parentId, { ...parentState, selectedChild: childId });
653
+ }
654
+ }
655
+ return new SkeinTree(this.engine, this.seed, this.knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
656
+ }
657
+ extendSelection(id) {
658
+ let knotStates = this.knotStates;
659
+ let currentId = id;
660
+ for (;;) {
661
+ const state = knotStates.get(currentId);
662
+ if (!state || state.children.length !== 1) {
663
+ break;
664
+ }
665
+ const childId = state.children[0];
666
+ knotStates = knotStates.set(currentId, { ...state, selectedChild: childId });
667
+ currentId = childId;
668
+ }
669
+ return new SkeinTree(this.engine, this.seed, this.knots, knotStates, this.activeKnotId, this.collapsedKnotIds, this.dynamicStates);
670
+ }
671
+ selectForNewChild(id) {
672
+ if (!this.knots.get(id)) {
673
+ throw new Error(`Knot ${id} not found`);
674
+ }
675
+ const afterPath = this.selectPath(id);
676
+ const state = afterPath.knotStates.get(id);
677
+ const knotStates = state.selectedChild === null ? afterPath.knotStates : afterPath.knotStates.set(id, { ...state, selectedChild: null });
678
+ return new SkeinTree(this.engine, this.seed, this.knots, knotStates, id, this.collapsedKnotIds, this.dynamicStates);
679
+ }
680
+ toggleCollapsed(id) {
681
+ if (!this.knots.get(id)) {
682
+ throw new Error(`Knot ${id} not found`);
683
+ }
684
+ const collapsing = !this.collapsedKnotIds.has(id);
685
+ const collapsedKnotIds = collapsing ? this.collapsedKnotIds.add(id) : this.collapsedKnotIds.remove(id);
686
+ const activeKnotId = collapsing && this.activeKnotId !== null && this.isDescendantOf(this.activeKnotId, id) ? id : this.activeKnotId;
687
+ return new SkeinTree(this.engine, this.seed, this.knots, this.knotStates, activeKnotId, collapsedKnotIds, this.dynamicStates);
688
+ }
689
+ isDescendantOf(id, ancestorId) {
690
+ let current = this.knots.get(id)?.parentId ?? null;
691
+ while (current !== null) {
692
+ if (current === ancestorId) {
693
+ return true;
694
+ }
695
+ current = this.knots.get(current)?.parentId ?? null;
696
+ }
697
+ return false;
698
+ }
699
+ }
700
+ exports.SkeinTree = SkeinTree;
701
+ //# sourceMappingURL=tree.js.map