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,690 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SkeinSession = void 0;
37
+ exports.normalizeCommand = normalizeCommand;
38
+ const os = __importStar(require("os"));
39
+ const events_1 = require("events");
40
+ const process_1 = require("./process");
41
+ const tree_1 = require("./tree");
42
+ const dynamic_1 = require("./dynamic");
43
+ const compile_error_1 = require("./compile-error");
44
+ const project_1 = require("./project");
45
+ const progress_1 = require("./progress");
46
+ const frotz_build_1 = require("./frotz-build");
47
+ const DYNAMIC_COMMAND = '@dynamic';
48
+ const DGDEBUG_TARGET_FILTER = 'dgdebug';
49
+ const REPLAY_ALL_CONCURRENCY = Math.max(2, Math.min(8, os.cpus().length));
50
+ const KEYSTROKE_KEY_CODES = {
51
+ enter: '\n',
52
+ space: ' ',
53
+ backspace: '\b'
54
+ };
55
+ function resolveKeystroke(command) {
56
+ return KEYSTROKE_KEY_CODES[command] ?? command;
57
+ }
58
+ async function mapWithConcurrency(items, limit, worker) {
59
+ const results = new Array(items.length);
60
+ let nextIndex = 0;
61
+ async function runNext() {
62
+ for (;;) {
63
+ const index = nextIndex++;
64
+ if (index >= items.length) {
65
+ return;
66
+ }
67
+ results[index] = await worker(items[index]);
68
+ }
69
+ }
70
+ const workerCount = Math.max(1, Math.min(limit, items.length));
71
+ await Promise.all(Array.from({ length: workerCount }, runNext));
72
+ return results;
73
+ }
74
+ function normalizeCommand(text) {
75
+ return text.trim().replace(/\s+/g, ' ').toLowerCase();
76
+ }
77
+ class SkeinSession {
78
+ constructor(config, progressHost = progress_1.noopProgressHost) {
79
+ this.process = null;
80
+ this.isRunning = false;
81
+ this.showDynamicState = false;
82
+ this.markerFilter = null;
83
+ this.changeEmitter = new events_1.EventEmitter();
84
+ this.processPositionId = 0;
85
+ this.sourcesChanged = false;
86
+ this.graphMenuId = null;
87
+ this.transcriptMenuId = null;
88
+ this.lastJumpId = { new: null, error: null };
89
+ this.undoStack = [];
90
+ this.redoStack = [];
91
+ this.hasLoadedHistory = false;
92
+ this.id = this.generateId();
93
+ this.config = config;
94
+ this.tree = tree_1.SkeinTree.newTree(config.engine, config.seed);
95
+ this.dynamicProcessor = new dynamic_1.DynamicProcessor();
96
+ this.progressHost = progressHost;
97
+ }
98
+ generateId() {
99
+ return `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
100
+ }
101
+ static createNew(config, progressHost) {
102
+ return new SkeinSession(config, progressHost);
103
+ }
104
+ static createLoaded(tree, config, progressHost) {
105
+ const session = new SkeinSession(config, progressHost);
106
+ session.tree = tree;
107
+ session.hasLoadedHistory = true;
108
+ return session;
109
+ }
110
+ async start() {
111
+ if (this.isRunning) {
112
+ throw new Error('Session already running');
113
+ }
114
+ try {
115
+ this.tree = await this.launchProcessAndCaptureBanner(this.tree, !this.hasLoadedHistory);
116
+ this.isRunning = true;
117
+ this.processPositionId = 0;
118
+ console.log(`Session ${this.id} started successfully`);
119
+ this.changeEmitter.emit('change');
120
+ }
121
+ catch (error) {
122
+ console.error('Failed to start session:', error);
123
+ throw error;
124
+ }
125
+ }
126
+ async launchProcessAndCaptureBanner(tree, blessRoot) {
127
+ this.process = new process_1.SkeinProcess(await this.buildProcessConfig());
128
+ this.sourcesChanged = false;
129
+ await this.process.start();
130
+ const banner = await this.process.readResponse();
131
+ if (!this.process.isProcessRunning()) {
132
+ throw new compile_error_1.DialogCompileError(banner.response);
133
+ }
134
+ let updated = tree.updateKnotResponse(0, { text: banner.response, inputType: banner.promptType });
135
+ if (blessRoot) {
136
+ updated = updated.blessKnot(0);
137
+ }
138
+ const dynamicState = await this.captureDynamicState(this.process, banner.promptType);
139
+ if (dynamicState) {
140
+ updated = updated.updateDynamicState(0, dynamicState);
141
+ }
142
+ return updated;
143
+ }
144
+ async buildProcessConfig(debugFlags) {
145
+ const { engine, seed, projectRoot, bundledBinDir, patchSourcePath } = this.config;
146
+ const project = (0, project_1.readProject)(projectRoot);
147
+ if (engine === 'dgdebug') {
148
+ const sourceFiles = (0, project_1.expandSources)(project, { debug: true, target: DGDEBUG_TARGET_FILTER });
149
+ return { engine, seed, sourceFiles, binDir: project.binDir, bundledBinDir, debugFlags };
150
+ }
151
+ if (!patchSourcePath) {
152
+ throw new Error(`${engine} requires patchSourcePath to be set on the session config`);
153
+ }
154
+ const gamePath = await (0, frotz_build_1.buildFrotzGame)({ project, engine, binDir: project.binDir, bundledBinDir, patchSourcePath });
155
+ return { engine, seed, gamePath, binDir: project.binDir, bundledBinDir, debugFlags };
156
+ }
157
+ getProjectRoot() {
158
+ return this.config.projectRoot;
159
+ }
160
+ markSourcesChanged() {
161
+ this.sourcesChanged = true;
162
+ }
163
+ async runCommand(command) {
164
+ if (!this.isRunning || !this.process) {
165
+ throw new Error('Session not running');
166
+ }
167
+ const parentId = this.tree.getActiveKnotId();
168
+ if (parentId === null) {
169
+ throw new Error('No active knot to run the command from');
170
+ }
171
+ if (parentId !== this.processPositionId || this.sourcesChanged) {
172
+ await this.replayTo(parentId);
173
+ }
174
+ try {
175
+ const process = this.process;
176
+ const keystroke = this.tree.promptTypeAt(parentId) === 'key';
177
+ if (keystroke) {
178
+ process.sendCommand(resolveKeystroke(command), true);
179
+ }
180
+ else {
181
+ process.sendCommand(command);
182
+ }
183
+ const response = await process.readResponse();
184
+ const newResponse = { text: response.response, inputType: response.promptType };
185
+ this.pushUndoSnapshot();
186
+ const existingChildId = this.tree.findChildId(parentId, command);
187
+ let activeKnotId;
188
+ if (existingChildId !== null) {
189
+ this.tree = this.tree.updateKnotResponse(existingChildId, newResponse);
190
+ activeKnotId = existingChildId;
191
+ }
192
+ else {
193
+ this.tree = this.tree.addChild(parentId, command, newResponse);
194
+ activeKnotId = this.tree.getDerivedKnot(parentId).selectedChild;
195
+ }
196
+ this.tree = this.tree.selectKnot(activeKnotId);
197
+ this.processPositionId = activeKnotId;
198
+ const dynamicState = await this.captureDynamicState(process, response.promptType);
199
+ if (dynamicState) {
200
+ this.tree = this.tree.updateDynamicState(activeKnotId, dynamicState);
201
+ }
202
+ console.log(`Command "${command}" executed successfully`);
203
+ this.changeEmitter.emit('change');
204
+ }
205
+ catch (error) {
206
+ console.error('Failed to execute command:', error);
207
+ throw error;
208
+ }
209
+ }
210
+ async replayPath(process, tree, targetId, token) {
211
+ const path = tree.commandPath(targetId);
212
+ const responses = new Map();
213
+ const dynamicStates = new Map();
214
+ let reachedId = 0;
215
+ let currentPromptType = tree.promptTypeAt(0);
216
+ for (const { id, command } of path) {
217
+ if (token?.isCancellationRequested) {
218
+ break;
219
+ }
220
+ const keystroke = currentPromptType === 'key';
221
+ if (keystroke) {
222
+ process.sendCommand(resolveKeystroke(command), true);
223
+ }
224
+ else {
225
+ process.sendCommand(command);
226
+ }
227
+ const response = await process.readResponse();
228
+ currentPromptType = response.promptType;
229
+ responses.set(id, { text: response.response, inputType: response.promptType });
230
+ const dynamicState = await this.captureDynamicState(process, response.promptType);
231
+ if (dynamicState) {
232
+ dynamicStates.set(id, dynamicState);
233
+ }
234
+ reachedId = id;
235
+ }
236
+ return { responses, dynamicStates, reachedId };
237
+ }
238
+ async captureDynamicState(process, promptType) {
239
+ if (this.config.engine !== 'dgdebug' || promptType === 'key') {
240
+ return null;
241
+ }
242
+ process.sendCommand(DYNAMIC_COMMAND);
243
+ const response = await process.readResponse();
244
+ return this.dynamicProcessor.parse(response.response);
245
+ }
246
+ async replayProcessTo(targetId) {
247
+ if (!this.process) {
248
+ throw new Error('Session not running');
249
+ }
250
+ await this.process.terminate();
251
+ this.tree = await this.launchProcessAndCaptureBanner(this.tree, false);
252
+ const { responses, dynamicStates, reachedId } = await this.replayPath(this.process, this.tree, targetId);
253
+ let tree = this.tree;
254
+ for (const [id, response] of responses) {
255
+ tree = tree.updateKnotResponse(id, response);
256
+ }
257
+ for (const [id, state] of dynamicStates) {
258
+ tree = tree.updateDynamicState(id, state);
259
+ }
260
+ this.tree = tree;
261
+ this.processPositionId = reachedId;
262
+ return { tree, reachedId };
263
+ }
264
+ async replayTo(targetId) {
265
+ const { tree, reachedId } = await this.replayProcessTo(targetId);
266
+ this.tree = tree.selectKnot(reachedId);
267
+ this.closeMenus();
268
+ this.changeEmitter.emit('change');
269
+ }
270
+ async replayAll() {
271
+ if (!this.process) {
272
+ throw new Error('Session not running');
273
+ }
274
+ const baseTree = this.tree;
275
+ const originalLeafId = baseTree.getSelectedLeafId();
276
+ const leafIds = baseTree.getLeafIds();
277
+ const totalLeaves = leafIds.length;
278
+ await this.progressHost.withProgress({ title: 'Replaying all paths...', cancellable: true }, async (progress, token) => {
279
+ let leavesReplayed = 0;
280
+ const replayLeaf = async (leafId) => {
281
+ if (token.isCancellationRequested) {
282
+ return undefined;
283
+ }
284
+ const process = new process_1.SkeinProcess(await this.buildProcessConfig());
285
+ await process.start();
286
+ const banner = await process.readResponse();
287
+ if (!process.isProcessRunning()) {
288
+ throw new compile_error_1.DialogCompileError(banner.response);
289
+ }
290
+ const bannerDynamicState = await this.captureDynamicState(process, banner.promptType);
291
+ const { responses, dynamicStates, reachedId } = await this.replayPath(process, baseTree, leafId, token);
292
+ responses.set(0, { text: banner.response, inputType: banner.promptType });
293
+ if (bannerDynamicState) {
294
+ dynamicStates.set(0, bannerDynamicState);
295
+ }
296
+ leavesReplayed++;
297
+ progress.report({ message: `${leavesReplayed} of ${totalLeaves}`, increment: 100 / totalLeaves });
298
+ return { leafId, process, responses, dynamicStates, reachedId };
299
+ };
300
+ const results = (await mapWithConcurrency(leafIds, REPLAY_ALL_CONCURRENCY, replayLeaf))
301
+ .filter((result) => result !== undefined);
302
+ const originalResult = results.find((result) => result.leafId === originalLeafId);
303
+ await Promise.all(results.filter((result) => result !== originalResult).map((result) => result.process.terminate()));
304
+ const orderedResults = originalResult
305
+ ? [...results.filter((result) => result !== originalResult), originalResult]
306
+ : results;
307
+ let tree = baseTree;
308
+ for (const result of orderedResults) {
309
+ for (const [id, response] of result.responses) {
310
+ tree = tree.updateKnotResponse(id, response);
311
+ }
312
+ for (const [id, state] of result.dynamicStates) {
313
+ tree = tree.updateDynamicState(id, state);
314
+ }
315
+ }
316
+ if (originalResult) {
317
+ await this.process.terminate();
318
+ this.process = originalResult.process;
319
+ this.processPositionId = originalResult.reachedId;
320
+ this.sourcesChanged = false;
321
+ }
322
+ this.tree = tree;
323
+ this.closeMenus();
324
+ this.changeEmitter.emit('change');
325
+ });
326
+ }
327
+ async replayToKnot(id) {
328
+ await this.replayTo(id);
329
+ }
330
+ async traceKnot(knotId) {
331
+ if (!this.isRunning) {
332
+ throw new Error('Session not running');
333
+ }
334
+ if (this.config.engine !== 'dgdebug') {
335
+ return null;
336
+ }
337
+ const knot = this.tree.getKnot(knotId);
338
+ if (!knot || knot.parentId === null) {
339
+ return null;
340
+ }
341
+ const { command, parentId } = knot;
342
+ if (this.tree.promptTypeAt(parentId) === 'key') {
343
+ return null;
344
+ }
345
+ await this.replayProcessTo(parentId);
346
+ const process = this.process;
347
+ process.sendCommand('(trace on)');
348
+ await process.readResponse();
349
+ process.sendCommand(command);
350
+ const traceResult = await process.readResponse();
351
+ process.sendCommand('(trace off)');
352
+ await process.readResponse();
353
+ this.processPositionId = knotId;
354
+ const dynamicState = await this.captureDynamicState(process, traceResult.promptType);
355
+ if (dynamicState) {
356
+ this.tree = this.tree.updateDynamicState(knotId, dynamicState);
357
+ }
358
+ this.closeMenus();
359
+ this.changeEmitter.emit('change');
360
+ return traceResult.response;
361
+ }
362
+ async traceStartup() {
363
+ if (this.config.engine !== 'dgdebug') {
364
+ return null;
365
+ }
366
+ const tracedProcess = new process_1.SkeinProcess(await this.buildProcessConfig(['--trace']));
367
+ await tracedProcess.start();
368
+ const tracedBanner = await tracedProcess.readResponse();
369
+ if (!tracedProcess.isProcessRunning()) {
370
+ throw new compile_error_1.DialogCompileError(tracedBanner.response);
371
+ }
372
+ await tracedProcess.terminate();
373
+ if (this.process) {
374
+ await this.process.terminate();
375
+ }
376
+ this.tree = await this.launchProcessAndCaptureBanner(this.tree, false);
377
+ this.processPositionId = 0;
378
+ this.closeMenus();
379
+ this.changeEmitter.emit('change');
380
+ return tracedBanner.response;
381
+ }
382
+ setActiveKnot(id) {
383
+ if (!this.tree.getKnot(id)) {
384
+ throw new Error(`Knot ${id} not found`);
385
+ }
386
+ this.tree = this.tree.selectKnot(id);
387
+ this.closeMenus();
388
+ this.changeEmitter.emit('change');
389
+ }
390
+ navigateSpine(direction) {
391
+ const activeId = this.tree.getActiveKnotId();
392
+ if (activeId === null) {
393
+ return;
394
+ }
395
+ const targetId = this.spineNavigationTarget(activeId, direction);
396
+ if (targetId === null || targetId === activeId) {
397
+ return;
398
+ }
399
+ this.setActiveKnot(targetId);
400
+ }
401
+ spineNavigationTarget(activeId, direction) {
402
+ switch (direction) {
403
+ case 'first':
404
+ return 0;
405
+ case 'last':
406
+ return this.tree.getSelectedLeafId();
407
+ case 'up':
408
+ return this.tree.getKnot(activeId)?.parentId ?? null;
409
+ case 'down':
410
+ return this.tree.getDerivedKnot(activeId)?.selectedChild ?? null;
411
+ case 'left':
412
+ case 'right': {
413
+ const knot = this.tree.getDerivedKnot(activeId);
414
+ if (!knot || knot.parentId === null) {
415
+ return null;
416
+ }
417
+ const siblings = this.tree.sortedChildren(knot.parentId);
418
+ const index = siblings.findIndex((sibling) => sibling.id === activeId);
419
+ const targetIndex = index + (direction === 'left' ? -1 : 1);
420
+ return targetIndex >= 0 && targetIndex < siblings.length ? siblings[targetIndex].id : null;
421
+ }
422
+ }
423
+ }
424
+ seekStatus(status) {
425
+ const matching = this.tree.knotIdsWithStatus(status);
426
+ if (matching.length === 0) {
427
+ return;
428
+ }
429
+ const lastId = this.lastJumpId[status];
430
+ const index = lastId !== null ? matching.indexOf(lastId) : -1;
431
+ const nextId = matching[index >= 0 ? (index + 1) % matching.length : 0];
432
+ this.lastJumpId[status] = nextId;
433
+ this.setActiveKnot(nextId);
434
+ }
435
+ newChild(id) {
436
+ if (!this.tree.getKnot(id)) {
437
+ throw new Error(`Knot ${id} not found`);
438
+ }
439
+ this.tree = this.tree.selectForNewChild(id);
440
+ this.closeMenus();
441
+ this.changeEmitter.emit('change');
442
+ }
443
+ openGraphMenu(id) {
444
+ if (!this.tree.getKnot(id)) {
445
+ throw new Error(`Knot ${id} not found`);
446
+ }
447
+ this.transcriptMenuId = null;
448
+ this.graphMenuId = this.graphMenuId === id ? null : id;
449
+ this.changeEmitter.emit('change');
450
+ }
451
+ openTranscriptMenu(id) {
452
+ if (!this.tree.getKnot(id)) {
453
+ throw new Error(`Knot ${id} not found`);
454
+ }
455
+ this.graphMenuId = null;
456
+ this.transcriptMenuId = this.transcriptMenuId === id ? null : id;
457
+ this.changeEmitter.emit('change');
458
+ }
459
+ toggleTreeNode(id) {
460
+ this.tree = this.tree.toggleCollapsed(id);
461
+ this.changeEmitter.emit('change');
462
+ }
463
+ getGraphMenuId() {
464
+ return this.graphMenuId;
465
+ }
466
+ getTranscriptMenuId() {
467
+ return this.transcriptMenuId;
468
+ }
469
+ closeMenus() {
470
+ this.graphMenuId = null;
471
+ this.transcriptMenuId = null;
472
+ }
473
+ closeAllMenus() {
474
+ if (this.graphMenuId === null && this.transcriptMenuId === null) {
475
+ return;
476
+ }
477
+ this.closeMenus();
478
+ this.changeEmitter.emit('change');
479
+ }
480
+ blessKnot(id) {
481
+ this.pushUndoSnapshot();
482
+ this.tree = this.tree.blessKnot(id);
483
+ this.closeMenus();
484
+ this.changeEmitter.emit('change');
485
+ }
486
+ blessChanges(id) {
487
+ this.pushUndoSnapshot();
488
+ this.tree = this.tree.blessTranscript(id);
489
+ this.closeMenus();
490
+ this.changeEmitter.emit('change');
491
+ }
492
+ toggleLock(id) {
493
+ if (id === 0) {
494
+ throw new Error('The root knot cannot be locked');
495
+ }
496
+ const knot = this.tree.getKnot(id);
497
+ if (!knot) {
498
+ throw new Error(`Knot ${id} not found`);
499
+ }
500
+ this.pushUndoSnapshot();
501
+ this.tree = this.tree.setLockStatus(id, !knot.locked);
502
+ this.closeMenus();
503
+ this.changeEmitter.emit('change');
504
+ }
505
+ setLabel(id, label) {
506
+ if (id === 0) {
507
+ throw new Error('The root knot\'s label cannot be changed');
508
+ }
509
+ const updated = this.tree.setLabel(id, label);
510
+ this.pushUndoSnapshot();
511
+ this.tree = updated;
512
+ this.closeMenus();
513
+ this.changeEmitter.emit('change');
514
+ }
515
+ setMarker(id, marker) {
516
+ if (!this.tree.getKnot(id)) {
517
+ throw new Error(`Knot ${id} not found`);
518
+ }
519
+ this.pushUndoSnapshot();
520
+ this.tree = this.tree.setMarker(id, marker);
521
+ this.closeMenus();
522
+ this.changeEmitter.emit('change');
523
+ }
524
+ async setCommand(id, command) {
525
+ if (id === 0) {
526
+ throw new Error('The root knot\'s command cannot be changed');
527
+ }
528
+ if (!this.isRunning) {
529
+ throw new Error('Session not running');
530
+ }
531
+ const normalized = normalizeCommand(command);
532
+ if (normalized === '') {
533
+ throw new Error('Command cannot be blank');
534
+ }
535
+ const knot = this.tree.getKnot(id);
536
+ if (!knot) {
537
+ throw new Error(`Knot ${id} not found`);
538
+ }
539
+ if (normalized === knot.command) {
540
+ return;
541
+ }
542
+ const renamed = this.tree.renameCommand(id, normalized);
543
+ this.pushUndoSnapshot();
544
+ this.tree = renamed;
545
+ await this.replayProcessTo(id);
546
+ this.closeMenus();
547
+ this.changeEmitter.emit('change');
548
+ }
549
+ async insertParent(id, command) {
550
+ if (!this.isRunning) {
551
+ throw new Error('Session not running');
552
+ }
553
+ const knot = this.tree.getKnot(id);
554
+ if (!knot || knot.parentId === null) {
555
+ throw new Error(`Knot ${id} not found, or is the root (which has no parent to insert above)`);
556
+ }
557
+ const normalized = normalizeCommand(command);
558
+ if (normalized === '') {
559
+ throw new Error('Command cannot be blank');
560
+ }
561
+ if (this.tree.promptTypeAt(knot.parentId) === 'key') {
562
+ throw new Error('Cannot insert a parent here: reached via a keystroke prompt.');
563
+ }
564
+ const newId = this.tree.nextId();
565
+ this.pushUndoSnapshot();
566
+ this.tree = this.tree.insertParent(id, normalized, { text: '', inputType: 'line' });
567
+ await this.replayProcessTo(id);
568
+ this.tree = this.tree.selectKnot(newId);
569
+ this.closeMenus();
570
+ this.changeEmitter.emit('change');
571
+ }
572
+ deleteKnot(id) {
573
+ if (id === 0) {
574
+ throw new Error('The root knot cannot be deleted');
575
+ }
576
+ const knot = this.tree.getKnot(id);
577
+ if (!knot) {
578
+ throw new Error(`Knot ${id} not found`);
579
+ }
580
+ let updated = this.tree;
581
+ if (this.isKnotOrDescendantActive(id)) {
582
+ updated = updated.setActiveKnotId(knot.parentId);
583
+ }
584
+ updated = updated.deleteKnot(id);
585
+ this.pushUndoSnapshot();
586
+ this.tree = updated;
587
+ this.closeMenus();
588
+ this.changeEmitter.emit('change');
589
+ }
590
+ spliceKnot(id) {
591
+ if (id === 0) {
592
+ throw new Error('The root knot cannot be spliced out');
593
+ }
594
+ const knot = this.tree.getKnot(id);
595
+ if (!knot) {
596
+ throw new Error(`Knot ${id} not found`);
597
+ }
598
+ this.pushUndoSnapshot();
599
+ if (this.tree.getActiveKnotId() === id) {
600
+ this.tree = this.tree.setActiveKnotId(knot.parentId);
601
+ }
602
+ this.tree = this.tree.spliceKnot(id);
603
+ this.closeMenus();
604
+ this.changeEmitter.emit('change');
605
+ }
606
+ pushUndoSnapshot() {
607
+ this.undoStack.push(this.tree);
608
+ this.redoStack = [];
609
+ }
610
+ undo() {
611
+ if (this.undoStack.length === 0) {
612
+ return;
613
+ }
614
+ const previous = this.undoStack.pop();
615
+ this.redoStack.push(this.tree);
616
+ this.tree = previous;
617
+ this.closeMenus();
618
+ this.changeEmitter.emit('change');
619
+ }
620
+ redo() {
621
+ if (this.redoStack.length === 0) {
622
+ return;
623
+ }
624
+ const next = this.redoStack.pop();
625
+ this.undoStack.push(this.tree);
626
+ this.tree = next;
627
+ this.closeMenus();
628
+ this.changeEmitter.emit('change');
629
+ }
630
+ isKnotOrDescendantActive(id) {
631
+ let currentId = this.tree.getActiveKnotId();
632
+ while (currentId !== null) {
633
+ if (currentId === id) {
634
+ return true;
635
+ }
636
+ currentId = this.tree.getKnot(currentId)?.parentId ?? null;
637
+ }
638
+ return false;
639
+ }
640
+ onChange(listener) {
641
+ this.changeEmitter.on('change', listener);
642
+ }
643
+ offChange(listener) {
644
+ this.changeEmitter.off('change', listener);
645
+ }
646
+ toggleShowDynamicState() {
647
+ this.showDynamicState = !this.showDynamicState;
648
+ this.changeEmitter.emit('change');
649
+ }
650
+ getShowDynamicState() {
651
+ return this.showDynamicState;
652
+ }
653
+ setMarkerFilter(marker) {
654
+ this.markerFilter = this.markerFilter === marker ? null : marker;
655
+ this.changeEmitter.emit('change');
656
+ }
657
+ getMarkerFilter() {
658
+ return this.markerFilter;
659
+ }
660
+ getState() {
661
+ return {
662
+ id: this.id,
663
+ config: this.config,
664
+ tree: this.tree,
665
+ process: this.process,
666
+ isRunning: this.isRunning
667
+ };
668
+ }
669
+ async stop() {
670
+ if (this.isRunning && this.process) {
671
+ await this.process.terminate();
672
+ this.isRunning = false;
673
+ console.log(`Session ${this.id} stopped`);
674
+ }
675
+ }
676
+ getTree() {
677
+ return this.tree;
678
+ }
679
+ getProcessPositionId() {
680
+ return this.processPositionId;
681
+ }
682
+ getId() {
683
+ return this.id;
684
+ }
685
+ isRunningSession() {
686
+ return this.isRunning;
687
+ }
688
+ }
689
+ exports.SkeinSession = SkeinSession;
690
+ //# sourceMappingURL=session.js.map