@hydranium/cli 1.0.0-next.39 → 1.0.0-next.390

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 (339) hide show
  1. package/README.md +102 -204
  2. package/bin/hydranium-cli.js +13 -0
  3. package/heap-analysis/heap/core/load.mjs +2 -2
  4. package/heap-analysis/heap/langium/langium.classifier.mjs +22 -11
  5. package/lib/cli-version.js.map +1 -1
  6. package/lib/cli.js +7 -2
  7. package/lib/cli.js.map +1 -1
  8. package/lib/commands/analyze-heap-args.d.ts.map +1 -1
  9. package/lib/commands/analyze-heap-args.js +1 -2
  10. package/lib/commands/analyze-heap-args.js.map +1 -1
  11. package/lib/commands/analyze-heap.d.ts +3 -0
  12. package/lib/commands/analyze-heap.d.ts.map +1 -1
  13. package/lib/commands/analyze-heap.js +2 -1
  14. package/lib/commands/analyze-heap.js.map +1 -1
  15. package/lib/commands/ast-ground-truth-args.d.ts +3 -2
  16. package/lib/commands/ast-ground-truth-args.d.ts.map +1 -1
  17. package/lib/commands/ast-ground-truth-args.js +11 -9
  18. package/lib/commands/ast-ground-truth-args.js.map +1 -1
  19. package/lib/commands/ast-ground-truth-driver.js +4 -2
  20. package/lib/commands/ast-ground-truth-driver.js.map +1 -1
  21. package/lib/commands/ast-ground-truth.js +1 -1
  22. package/lib/commands/ast-ground-truth.js.map +1 -1
  23. package/lib/commands/client-session.d.ts +17 -0
  24. package/lib/commands/client-session.d.ts.map +1 -0
  25. package/lib/commands/client-session.js +27 -0
  26. package/lib/commands/client-session.js.map +1 -0
  27. package/lib/commands/create-args.d.ts +17 -0
  28. package/lib/commands/create-args.d.ts.map +1 -0
  29. package/lib/commands/create-args.js +53 -0
  30. package/lib/commands/create-args.js.map +1 -0
  31. package/lib/commands/create.d.ts +38 -0
  32. package/lib/commands/create.d.ts.map +1 -0
  33. package/lib/commands/create.js +43 -0
  34. package/lib/commands/create.js.map +1 -0
  35. package/lib/commands/delete-args.d.ts +17 -0
  36. package/lib/commands/delete-args.d.ts.map +1 -0
  37. package/lib/commands/delete-args.js +49 -0
  38. package/lib/commands/delete-args.js.map +1 -0
  39. package/lib/commands/delete.d.ts +30 -0
  40. package/lib/commands/delete.d.ts.map +1 -0
  41. package/lib/commands/delete.js +32 -0
  42. package/lib/commands/delete.js.map +1 -0
  43. package/lib/commands/generate-ast-builder.d.ts.map +1 -1
  44. package/lib/commands/generate-ast-builder.js +1 -10
  45. package/lib/commands/generate-ast-builder.js.map +1 -1
  46. package/lib/commands/generate-languages.d.ts +35 -0
  47. package/lib/commands/generate-languages.d.ts.map +1 -0
  48. package/lib/commands/generate-languages.js +105 -0
  49. package/lib/commands/generate-languages.js.map +1 -0
  50. package/lib/commands/generate-transfer-builder.d.ts +33 -0
  51. package/lib/commands/generate-transfer-builder.d.ts.map +1 -0
  52. package/lib/commands/generate-transfer-builder.js +86 -0
  53. package/lib/commands/generate-transfer-builder.js.map +1 -0
  54. package/lib/commands/generate-transfer-model-args.d.ts.map +1 -1
  55. package/lib/commands/generate-transfer-model-args.js +20 -5
  56. package/lib/commands/generate-transfer-model-args.js.map +1 -1
  57. package/lib/commands/generate-transfer-model-config.d.ts.map +1 -1
  58. package/lib/commands/generate-transfer-model-config.js +8 -3
  59. package/lib/commands/generate-transfer-model-config.js.map +1 -1
  60. package/lib/commands/generate-transfer-model.d.ts +40 -9
  61. package/lib/commands/generate-transfer-model.d.ts.map +1 -1
  62. package/lib/commands/generate-transfer-model.js +219 -67
  63. package/lib/commands/generate-transfer-model.js.map +1 -1
  64. package/lib/commands/generated-module-path.d.ts +16 -0
  65. package/lib/commands/generated-module-path.d.ts.map +1 -0
  66. package/lib/commands/generated-module-path.js +21 -0
  67. package/lib/commands/generated-module-path.js.map +1 -0
  68. package/lib/commands/harness-args.d.ts +44 -42
  69. package/lib/commands/harness-args.d.ts.map +1 -1
  70. package/lib/commands/harness-args.js +109 -27
  71. package/lib/commands/harness-args.js.map +1 -1
  72. package/lib/commands/headless-harness.d.ts +37 -20
  73. package/lib/commands/headless-harness.d.ts.map +1 -1
  74. package/lib/commands/headless-harness.js +71 -19
  75. package/lib/commands/headless-harness.js.map +1 -1
  76. package/lib/commands/init-args.d.ts +2 -0
  77. package/lib/commands/init-args.d.ts.map +1 -1
  78. package/lib/commands/init-args.js +74 -31
  79. package/lib/commands/init-args.js.map +1 -1
  80. package/lib/commands/init-hosts.d.ts +24 -0
  81. package/lib/commands/init-hosts.d.ts.map +1 -0
  82. package/lib/commands/init-hosts.js +37 -0
  83. package/lib/commands/init-hosts.js.map +1 -0
  84. package/lib/commands/init-prompt.d.ts +3 -3
  85. package/lib/commands/init-prompt.d.ts.map +1 -1
  86. package/lib/commands/init-prompt.js +4 -5
  87. package/lib/commands/init-prompt.js.map +1 -1
  88. package/lib/commands/init-templates.d.ts +26 -14
  89. package/lib/commands/init-templates.d.ts.map +1 -1
  90. package/lib/commands/init-templates.js +2989 -746
  91. package/lib/commands/init-templates.js.map +1 -1
  92. package/lib/commands/init-wizard.d.ts +3 -0
  93. package/lib/commands/init-wizard.d.ts.map +1 -1
  94. package/lib/commands/init-wizard.js +46 -31
  95. package/lib/commands/init-wizard.js.map +1 -1
  96. package/lib/commands/init-workspace.d.ts +13 -11
  97. package/lib/commands/init-workspace.d.ts.map +1 -1
  98. package/lib/commands/init-workspace.js +21 -7
  99. package/lib/commands/init-workspace.js.map +1 -1
  100. package/lib/commands/init.d.ts +58 -22
  101. package/lib/commands/init.d.ts.map +1 -1
  102. package/lib/commands/init.js +206 -48
  103. package/lib/commands/init.js.map +1 -1
  104. package/lib/commands/kebab.d.ts +15 -0
  105. package/lib/commands/kebab.d.ts.map +1 -0
  106. package/lib/commands/kebab.js +17 -0
  107. package/lib/commands/kebab.js.map +1 -0
  108. package/lib/commands/lint-grammar-args.d.ts +3 -2
  109. package/lib/commands/lint-grammar-args.d.ts.map +1 -1
  110. package/lib/commands/lint-grammar-args.js +13 -10
  111. package/lib/commands/lint-grammar-args.js.map +1 -1
  112. package/lib/commands/lint-grammar-driver.js +2 -1
  113. package/lib/commands/lint-grammar-driver.js.map +1 -1
  114. package/lib/commands/lint-grammar-report.js.map +1 -1
  115. package/lib/commands/lint-grammar.js +1 -1
  116. package/lib/commands/lint-grammar.js.map +1 -1
  117. package/lib/commands/measure-memory-args.d.ts +3 -2
  118. package/lib/commands/measure-memory-args.d.ts.map +1 -1
  119. package/lib/commands/measure-memory-args.js +22 -11
  120. package/lib/commands/measure-memory-args.js.map +1 -1
  121. package/lib/commands/measure-memory-driver.js +5 -3
  122. package/lib/commands/measure-memory-driver.js.map +1 -1
  123. package/lib/commands/measure-memory.js +1 -1
  124. package/lib/commands/measure-memory.js.map +1 -1
  125. package/lib/commands/model-docs-args.d.ts +4 -3
  126. package/lib/commands/model-docs-args.d.ts.map +1 -1
  127. package/lib/commands/model-docs-args.js +12 -10
  128. package/lib/commands/model-docs-args.js.map +1 -1
  129. package/lib/commands/model-docs-driver.js +2 -1
  130. package/lib/commands/model-docs-driver.js.map +1 -1
  131. package/lib/commands/model-docs.js +1 -1
  132. package/lib/commands/model-docs.js.map +1 -1
  133. package/lib/commands/projects-args.d.ts.map +1 -1
  134. package/lib/commands/projects-args.js +5 -5
  135. package/lib/commands/projects-args.js.map +1 -1
  136. package/lib/commands/projects.d.ts +3 -4
  137. package/lib/commands/projects.d.ts.map +1 -1
  138. package/lib/commands/projects.js +2 -2
  139. package/lib/commands/projects.js.map +1 -1
  140. package/lib/commands/query-args.d.ts.map +1 -1
  141. package/lib/commands/query-args.js +6 -5
  142. package/lib/commands/query-args.js.map +1 -1
  143. package/lib/commands/query.d.ts +6 -5
  144. package/lib/commands/query.d.ts.map +1 -1
  145. package/lib/commands/query.js +5 -4
  146. package/lib/commands/query.js.map +1 -1
  147. package/lib/commands/reflect-args.d.ts +3 -2
  148. package/lib/commands/reflect-args.d.ts.map +1 -1
  149. package/lib/commands/reflect-args.js +13 -10
  150. package/lib/commands/reflect-args.js.map +1 -1
  151. package/lib/commands/reflect-driver.js +2 -1
  152. package/lib/commands/reflect-driver.js.map +1 -1
  153. package/lib/commands/reflect-report.js.map +1 -1
  154. package/lib/commands/reflect.js +1 -1
  155. package/lib/commands/reflect.js.map +1 -1
  156. package/lib/commands/save-args.d.ts.map +1 -1
  157. package/lib/commands/save-args.js +7 -4
  158. package/lib/commands/save-args.js.map +1 -1
  159. package/lib/commands/save.d.ts +8 -4
  160. package/lib/commands/save.d.ts.map +1 -1
  161. package/lib/commands/save.js +12 -23
  162. package/lib/commands/save.js.map +1 -1
  163. package/lib/commands/validate-args.d.ts +3 -2
  164. package/lib/commands/validate-args.d.ts.map +1 -1
  165. package/lib/commands/validate-args.js +13 -10
  166. package/lib/commands/validate-args.js.map +1 -1
  167. package/lib/commands/validate-driver.js +2 -1
  168. package/lib/commands/validate-driver.js.map +1 -1
  169. package/lib/commands/validate-report.js.map +1 -1
  170. package/lib/commands/validate.js +1 -1
  171. package/lib/commands/validate.js.map +1 -1
  172. package/lib/commands/watch-args.d.ts.map +1 -1
  173. package/lib/commands/watch-args.js +6 -4
  174. package/lib/commands/watch-args.js.map +1 -1
  175. package/lib/commands/watch.d.ts +3 -4
  176. package/lib/commands/watch.d.ts.map +1 -1
  177. package/lib/commands/watch.js +5 -2
  178. package/lib/commands/watch.js.map +1 -1
  179. package/lib/driver-heap.d.ts +52 -0
  180. package/lib/driver-heap.d.ts.map +1 -0
  181. package/lib/driver-heap.js +51 -0
  182. package/lib/driver-heap.js.map +1 -0
  183. package/lib/index.d.ts +2 -0
  184. package/lib/index.d.ts.map +1 -1
  185. package/lib/index.js +2 -0
  186. package/lib/index.js.map +1 -1
  187. package/lib/log-level.d.ts +21 -11
  188. package/lib/log-level.d.ts.map +1 -1
  189. package/lib/log-level.js +25 -14
  190. package/lib/log-level.js.map +1 -1
  191. package/lib/spawn-data-server.d.ts +9 -4
  192. package/lib/spawn-data-server.d.ts.map +1 -1
  193. package/lib/spawn-data-server.js +5 -4
  194. package/lib/spawn-data-server.js.map +1 -1
  195. package/lib/stderr-connection-logger.d.ts +12 -0
  196. package/lib/stderr-connection-logger.d.ts.map +1 -0
  197. package/lib/stderr-connection-logger.js +16 -0
  198. package/lib/stderr-connection-logger.js.map +1 -0
  199. package/lib/testing/echo-server.js +9 -1
  200. package/lib/testing/echo-server.js.map +1 -1
  201. package/package.json +90 -39
  202. package/src/cli.ts +7 -2
  203. package/src/commands/analyze-heap-args.ts +1 -2
  204. package/src/commands/analyze-heap.ts +4 -1
  205. package/src/commands/ast-ground-truth-args.ts +15 -12
  206. package/src/commands/ast-ground-truth-driver.ts +4 -2
  207. package/src/commands/ast-ground-truth.ts +1 -1
  208. package/src/commands/client-session.ts +33 -0
  209. package/src/commands/create-args.ts +68 -0
  210. package/src/commands/create.ts +83 -0
  211. package/src/commands/delete-args.ts +64 -0
  212. package/src/commands/delete.ts +61 -0
  213. package/src/commands/generate-ast-builder.ts +1 -11
  214. package/src/commands/generate-languages.ts +132 -0
  215. package/src/commands/generate-transfer-builder.ts +101 -0
  216. package/src/commands/generate-transfer-model-args.ts +20 -5
  217. package/src/commands/generate-transfer-model-config.ts +8 -3
  218. package/src/commands/generate-transfer-model.ts +291 -65
  219. package/src/commands/generated-module-path.ts +22 -0
  220. package/src/commands/harness-args.ts +99 -31
  221. package/src/commands/headless-harness.ts +89 -28
  222. package/src/commands/init-args.ts +76 -31
  223. package/src/commands/init-hosts.ts +51 -0
  224. package/src/commands/init-prompt.ts +5 -6
  225. package/src/commands/init-templates.ts +3161 -759
  226. package/src/commands/init-wizard.ts +55 -31
  227. package/src/commands/init-workspace.ts +31 -16
  228. package/src/commands/init.ts +266 -63
  229. package/src/commands/kebab.ts +17 -0
  230. package/src/commands/lint-grammar-args.ts +17 -13
  231. package/src/commands/lint-grammar-driver.ts +2 -1
  232. package/src/commands/lint-grammar.ts +1 -1
  233. package/src/commands/measure-memory-args.ts +27 -14
  234. package/src/commands/measure-memory-driver.ts +5 -3
  235. package/src/commands/measure-memory.ts +1 -1
  236. package/src/commands/model-docs-args.ts +16 -13
  237. package/src/commands/model-docs-driver.ts +2 -1
  238. package/src/commands/model-docs.ts +1 -1
  239. package/src/commands/projects-args.ts +5 -4
  240. package/src/commands/projects.ts +4 -6
  241. package/src/commands/query-args.ts +6 -4
  242. package/src/commands/query.ts +9 -9
  243. package/src/commands/reflect-args.ts +17 -13
  244. package/src/commands/reflect-driver.ts +2 -1
  245. package/src/commands/reflect.ts +1 -1
  246. package/src/commands/save-args.ts +7 -3
  247. package/src/commands/save.ts +24 -27
  248. package/src/commands/validate-args.ts +17 -13
  249. package/src/commands/validate-driver.ts +2 -1
  250. package/src/commands/validate.ts +1 -1
  251. package/src/commands/watch-args.ts +6 -3
  252. package/src/commands/watch.ts +7 -6
  253. package/src/driver-heap.ts +65 -0
  254. package/src/index.ts +2 -0
  255. package/src/log-level.ts +42 -14
  256. package/src/spawn-data-server.ts +17 -5
  257. package/src/stderr-connection-logger.ts +18 -0
  258. package/src/testing/echo-server.ts +11 -2
  259. package/templates/client/gitignore +3 -0
  260. package/templates/client/src/__GRAMMAR_ID__-diagram-module.ts +82 -0
  261. package/templates/editorconfig +15 -0
  262. package/templates/gitignore +1 -0
  263. package/templates/host-browser/esbuild.mjs +82 -0
  264. package/templates/host-browser/gitignore +4 -0
  265. package/templates/host-browser/hydranium-mark.svg +40 -0
  266. package/templates/host-browser/index.html +287 -0
  267. package/templates/host-browser/playwright.config.mts +24 -0
  268. package/templates/host-browser/src/data-status.ts +15 -0
  269. package/templates/host-browser/src/diagram-save.ts +60 -0
  270. package/templates/host-browser/src/diagram.ts +121 -0
  271. package/templates/host-browser/src/editor.ts +102 -0
  272. package/templates/host-browser/src/log.ts +14 -0
  273. package/templates/host-browser/src/monaco-editor-core-worker.d.ts +7 -0
  274. package/templates/host-browser/src/monaco-editor-worker.ts +10 -0
  275. package/templates/host-browser/src/posix-path-shim.ts +23 -0
  276. package/templates/host-browser/src/shown-file.ts +61 -0
  277. package/templates/host-browser/src/workspace.ts +5 -0
  278. package/templates/host-browser/tsconfig.e2e.json +16 -0
  279. package/templates/host-browser/tsconfig.page.json +16 -0
  280. package/templates/host-browser/tsconfig.worker.json +16 -0
  281. package/templates/host-theia-browser/esbuild.mjs +24 -0
  282. package/templates/host-theia-browser/gitignore +9 -0
  283. package/templates/host-theia-browser/playwright.config.mts +42 -0
  284. package/templates/host-theia-browser/start.mjs +50 -0
  285. package/templates/host-theia-browser/tsconfig.e2e.json +16 -0
  286. package/templates/host-theia-electron/esbuild.mjs +27 -0
  287. package/templates/host-theia-electron/gitignore +8 -0
  288. package/templates/host-theia-electron/playwright.config.mts +12 -0
  289. package/templates/host-theia-electron/start.mjs +47 -0
  290. package/templates/host-theia-electron/tsconfig.e2e.json +16 -0
  291. package/templates/host-vscode/esbuild.mjs +38 -0
  292. package/templates/host-vscode/gitignore +7 -0
  293. package/templates/host-vscode/playwright.config.mts +14 -0
  294. package/templates/host-vscode/src/__GRAMMAR_ID__-diagram-editor.ts +19 -0
  295. package/templates/host-vscode/src/show-model-command.ts +24 -0
  296. package/templates/host-vscode/src/webview/__GRAMMAR_ID__-diagram.ts +18 -0
  297. package/templates/host-vscode/src/webview/diagram.css +40 -0
  298. package/templates/host-vscode/start.mjs +22 -0
  299. package/templates/host-vscode/test/install-vscode.mts +21 -0
  300. package/templates/host-vscode/test/vscode-version.mts +10 -0
  301. package/templates/host-vscode/tsconfig.e2e.json +17 -0
  302. package/templates/host-vscode/tsconfig.json +19 -0
  303. package/templates/host-vscode/tsconfig.webview.json +17 -0
  304. package/templates/oxlintrc.json +4 -0
  305. package/templates/protocol/gitignore +3 -0
  306. package/templates/protocol/src/__GRAMMAR_ID__-diagram-types.ts +47 -0
  307. package/templates/server/gitignore +6 -0
  308. package/templates/server/src/glsp/__GRAMMAR_ID__/create-node-operation-handler.ts +62 -0
  309. package/templates/server/src/glsp/__GRAMMAR_ID__/delete-operation-handler.ts +63 -0
  310. package/templates/server/src/glsp/__GRAMMAR_ID__/gmodel-factory.ts +144 -0
  311. package/templates/server/src/glsp/__GRAMMAR_ID__/state.ts +18 -0
  312. package/templates/server/src/glsp/__GRAMMAR_ID__/storage.ts +16 -0
  313. package/templates/server/src/glsp/__GRAMMAR_ID__/submission-handler.ts +24 -0
  314. package/templates/server/src/grammar/common.langium +12 -0
  315. package/templates/server/src/language-server/__GRAMMAR_ID__-serializer.ts +53 -0
  316. package/templates/server/src/language-server/__GRAMMAR_ID__-validation.ts +30 -0
  317. package/templates/server/src/language-server/ast.ts +23 -0
  318. package/templates/server/src/node/services.ts +16 -0
  319. package/templates/server/test/__GRAMMAR_ID__-diagram.test.ts +403 -0
  320. package/templates/server/test/tsconfig.json +4 -0
  321. package/templates/server/tsconfig.test.json +10 -0
  322. package/templates/server/vitest.config.ts +14 -0
  323. package/templates/theia/gitignore +3 -0
  324. package/templates/theia/src/browser/__GRAMMAR_ID__-diagram-frontend-module.ts +65 -0
  325. package/templates/theia/src/browser/__PROJECT_ID__-connection-preload-module.ts +15 -0
  326. package/templates/theia/src/browser/__PROJECT_ID__-data-connection.ts +15 -0
  327. package/templates/theia/src/browser/__PROJECT_ID__-data-port.ts +11 -0
  328. package/templates/theia/src/browser/__PROJECT_ID__-glsp-client-contribution.ts +13 -0
  329. package/templates/theia/src/browser/__PROJECT_ID__-show-model-command.ts +51 -0
  330. package/templates/theia/src/common/__PROJECT_ID__-glsp-contribution.ts +3 -0
  331. package/templates/theia/src/node/__PROJECT_ID__-connection-backend-module.ts +12 -0
  332. package/templates/theia/src/node/__PROJECT_ID__-data-backend-module.ts +15 -0
  333. package/templates/theia/src/node/__PROJECT_ID__-glsp-backend-module.ts +16 -0
  334. package/templates/theia/style/diagram.css +20 -0
  335. package/templates/theia/tsconfig.json +24 -0
  336. package/templates/vscode-servers/gitignore +4 -0
  337. package/templates/vscode-servers/tsconfig.json +19 -0
  338. package/templates/workspace/nodes.__EXTENSION__ +5 -0
  339. package/templates/workspace/references.__EXTENSION__ +2 -0
@@ -0,0 +1,40 @@
1
+ <svg width="140" height="140" viewBox="50 72 140 140" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Hydranium">
2
+ <defs>
3
+ <linearGradient id="hb" x1="40" y1="60" x2="200" y2="200" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0" stop-color="#2DD4BF"/>
5
+ <stop offset="1" stop-color="#6366F1"/>
6
+ </linearGradient>
7
+ </defs>
8
+
9
+ <!-- open book with curved pages -->
10
+ <g stroke="url(#hb)" stroke-width="7" stroke-linejoin="round" stroke-linecap="round">
11
+ <path d="M120 162 C 102 152 82 151 66 158 L 63 193 C 83 190 102 196 120 202 Z" fill="#fff"/>
12
+ <path d="M120 162 C 138 152 158 151 174 158 L 177 193 C 157 190 138 196 120 202 Z" fill="#fff"/>
13
+ <line x1="120" y1="162" x2="120" y2="202"/>
14
+ </g>
15
+ <!-- gray text lines on the pages -->
16
+ <g stroke="#94A3B8" stroke-width="4" stroke-linecap="round" fill="none">
17
+ <path d="M74 170 C 84 166 99 169 108 174"/>
18
+ <path d="M74 180 C 84 176 99 179 108 184"/>
19
+ <path d="M166 170 C 156 166 141 169 132 174"/>
20
+ <path d="M166 180 C 156 176 141 179 132 184"/>
21
+ </g>
22
+
23
+ <!-- necks emerging from the body -->
24
+ <g stroke="url(#hb)" stroke-width="11" stroke-linecap="round" fill="none">
25
+ <path d="M114 144 C 108 134 90 132 84 120 C 81 114 78 110 76 107"/>
26
+ <path d="M120 143 C 123 133 116 122 120 107"/>
27
+ <path d="M126 144 C 132 134 150 132 156 120 C 159 114 162 110 164 107"/>
28
+ </g>
29
+
30
+ <!-- the hydra's body: a tapered torso sitting in the book's opening -->
31
+ <path d="M120 130 C 131 132 140 146 140 158 C 140 171 132 178 120 178 C 108 178 100 171 100 158 C 100 146 109 132 120 130 Z" fill="url(#hb)"/>
32
+
33
+ <!-- three noticeably different heads -->
34
+ <circle cx="76" cy="104" r="14" fill="url(#hb)"/>
35
+ <circle cx="71" cy="100" r="2.8" fill="#fff"/>
36
+ <polygon points="136,92 128,105.9 112,105.9 104,92 112,78.1 128,78.1" fill="url(#hb)"/>
37
+ <circle cx="120" cy="88" r="2.8" fill="#fff"/>
38
+ <rect x="151" y="91" width="26" height="26" rx="6" fill="url(#hb)"/>
39
+ <circle cx="169" cy="100" r="2.8" fill="#fff"/>
40
+ </svg>
@@ -0,0 +1,287 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>__NAME__</title>
7
+ <!-- The stylesheets Monaco and GLSP import, which esbuild writes beside the page bundle. -->
8
+ <link rel="stylesheet" href="./out/page.css" />
9
+ <!-- The mark with white pages, which stays visible on a light and a dark tab strip alike. -->
10
+ <link rel="icon" href="./hydranium-mark.svg" />
11
+ <style>
12
+ :root {
13
+ --accent: #3b6ea8;
14
+ --border: #dde1e6;
15
+ --surface: #f6f7f9;
16
+ --text: #1f2933;
17
+ /* GLSP's marker colours, pure yellow and light blue by default, which a light page barely shows. */
18
+ --glsp-error-foreground: #c62828;
19
+ --glsp-warning-foreground: #b26a00;
20
+ --glsp-info-foreground: var(--accent);
21
+ }
22
+ html,
23
+ body {
24
+ height: 100%;
25
+ margin: 0;
26
+ }
27
+ body {
28
+ display: grid;
29
+ grid-template-rows: auto minmax(0, 1fr) auto;
30
+ /* The window's width, which a long log line otherwise widens past it, cutting off the diagram. */
31
+ grid-template-columns: minmax(0, 1fr);
32
+ font-family: system-ui, sans-serif;
33
+ font-size: 13px;
34
+ color: var(--text);
35
+ }
36
+ /* The page's toolbar: its name, then the file switch, Save and the status they report to. */
37
+ header {
38
+ display: flex;
39
+ gap: 12px;
40
+ align-items: center;
41
+ padding: 10px 16px;
42
+ background: var(--accent);
43
+ color: #ffffff;
44
+ }
45
+ h1 {
46
+ margin: 0;
47
+ font-size: 16px;
48
+ font-weight: 600;
49
+ }
50
+ #status {
51
+ padding: 2px 8px;
52
+ border-radius: 10px;
53
+ background: rgb(255 255 255 / 0.18);
54
+ font-size: 12px;
55
+ }
56
+ select,
57
+ button {
58
+ padding: 4px 10px;
59
+ border: 1px solid var(--border);
60
+ border-radius: 4px;
61
+ background: #ffffff;
62
+ color: var(--text);
63
+ font: inherit;
64
+ }
65
+ /* On the toolbar's accent: white, with the accent as its text. */
66
+ button {
67
+ border-color: #ffffff;
68
+ color: var(--accent);
69
+ font-weight: 600;
70
+ cursor: pointer;
71
+ }
72
+ button:hover {
73
+ background: #e8eef6;
74
+ }
75
+ /* One column per shown pane, its title, hint and body rows shared, so the titles and hints line up. */
76
+ main {
77
+ display: grid;
78
+ grid-auto-flow: column;
79
+ grid-auto-columns: minmax(0, 1fr);
80
+ grid-template-rows: auto auto minmax(0, 1fr);
81
+ min-height: 0;
82
+ }
83
+ main > section {
84
+ display: grid;
85
+ grid-row: 1 / -1;
86
+ grid-template-rows: subgrid;
87
+ min-width: 0;
88
+ min-height: 0;
89
+ }
90
+ main > section[hidden] {
91
+ display: none;
92
+ }
93
+ #diagram-pane {
94
+ border-left: 1px solid var(--border);
95
+ }
96
+ .pane-title {
97
+ margin: 0;
98
+ padding: 10px 12px 2px;
99
+ color: var(--accent);
100
+ font-size: 12px;
101
+ font-weight: 600;
102
+ letter-spacing: 0.04em;
103
+ text-transform: uppercase;
104
+ }
105
+ .hint {
106
+ margin: 0;
107
+ padding: 2px 12px 8px;
108
+ border-bottom: 1px solid var(--border);
109
+ color: #5f6b7a;
110
+ font-size: 12px;
111
+ }
112
+ /* Shrinkable, or Monaco's width from before the diagram pane showed holds it at the whole window. */
113
+ .pane-body {
114
+ display: flex;
115
+ flex-direction: column;
116
+ min-width: 0;
117
+ min-height: 0;
118
+ }
119
+ #editor {
120
+ flex: 1 1 auto;
121
+ min-height: 0;
122
+ }
123
+ /* The diagram needs a height, `position: relative` for its tool palette, and its `<svg>` sized to it. */
124
+ #diagram {
125
+ flex: 1 1 auto;
126
+ position: relative;
127
+ min-height: 0;
128
+ background: var(--surface) radial-gradient(#d5dae1 1px, transparent 1px) 0 0 / 16px 16px;
129
+ touch-action: none;
130
+ }
131
+ /* GLSP's tool palette, in the page's colours. */
132
+ .tool-palette {
133
+ top: 12px;
134
+ right: 44px;
135
+ overflow: hidden;
136
+ border: 1px solid var(--border);
137
+ border-radius: 6px;
138
+ box-shadow: 0 2px 8px rgb(0 0 0 / 0.12);
139
+ color: var(--text);
140
+ font-size: 13px;
141
+ }
142
+ .palette-header {
143
+ border: 0;
144
+ background: var(--accent);
145
+ color: #ffffff;
146
+ }
147
+ .header-tools i:hover {
148
+ background: rgb(255 255 255 / 0.2);
149
+ }
150
+ .header-tools .clicked {
151
+ border-color: transparent;
152
+ background: rgb(255 255 255 / 0.3);
153
+ }
154
+ .palette-body,
155
+ .tool-group,
156
+ .tool-button {
157
+ border: 0;
158
+ background: #ffffff;
159
+ }
160
+ .group-header,
161
+ .group-header:hover {
162
+ border-bottom: 1px solid var(--border);
163
+ background: var(--surface);
164
+ font-weight: 600;
165
+ }
166
+ .tool-button {
167
+ padding: 6px 12px;
168
+ }
169
+ .tool-button:hover {
170
+ background: var(--surface);
171
+ }
172
+ .tool-button.clicked {
173
+ background: #dbe6f3;
174
+ }
175
+ .minimize-palette-button {
176
+ top: 12px;
177
+ right: 14px;
178
+ color: var(--accent);
179
+ }
180
+ #diagram svg {
181
+ width: 100%;
182
+ height: 100%;
183
+ }
184
+ /* The copy sprotty measures shapes in: a zero-sized box whose overflow otherwise covers the page and takes its clicks. */
185
+ body > .sprotty-hidden {
186
+ pointer-events: none;
187
+ overflow: hidden;
188
+ }
189
+ /* GLSP's stylesheet leaves shapes at the SVG defaults: a black fill, no node outline, and no stroke on an edge. */
190
+ .sprotty-node {
191
+ fill: #ffffff;
192
+ stroke: var(--accent);
193
+ rx: 6px;
194
+ filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.12));
195
+ }
196
+ /* GLSP's own rule, which draws an unselected node with no outline. */
197
+ .sprotty-node:not(.selected) {
198
+ stroke-width: 1.5px;
199
+ }
200
+ .sprotty-node.selected {
201
+ stroke-width: 2.5px;
202
+ }
203
+ .sprotty-label {
204
+ fill: var(--text);
205
+ font-size: 13px;
206
+ }
207
+ .sprotty-edge {
208
+ fill: none;
209
+ stroke: var(--accent);
210
+ stroke-width: 1.5px;
211
+ }
212
+ .sprotty-edge-arrow {
213
+ fill: var(--accent);
214
+ }
215
+ /* A node of another file, which the diagram draws but does not own. */
216
+ .external .sprotty-node {
217
+ fill: var(--surface);
218
+ stroke-dasharray: 4 3;
219
+ filter: none;
220
+ }
221
+ .external .sprotty-label {
222
+ fill: #5f6b7a;
223
+ font-style: italic;
224
+ }
225
+ footer {
226
+ border-top: 1px solid var(--border);
227
+ }
228
+ #data-status {
229
+ margin: 0;
230
+ padding: 2px 12px;
231
+ }
232
+ #log {
233
+ height: 200px;
234
+ margin: 0;
235
+ padding: 6px 12px;
236
+ overflow-y: auto;
237
+ /* Wrapped to the window rather than scrolled sideways. */
238
+ white-space: pre-wrap;
239
+ overflow-wrap: anywhere;
240
+ color: #5f6b7a;
241
+ font-family: ui-monospace, monospace;
242
+ font-size: 12px;
243
+ }
244
+ </style>
245
+ </head>
246
+ <body>
247
+ <header>
248
+ <h1>__NAME__</h1>
249
+ <!-- Each control stays hidden until the part it belongs to is up: the switch once the worker has sent its files, Save once
250
+ the part that saves (the editor, or the diagram without one) is armed. -->
251
+ <select id="file" aria-label="File" hidden></select>
252
+ <button id="save" type="button" title="Save (Ctrl+S)" hidden>Save</button>
253
+ <span id="status" role="status">starting…</span>
254
+ </header>
255
+ <!-- Each pane stays hidden until the page part for its head fills it. -->
256
+ <main>
257
+ <section id="editor-pane" hidden>
258
+ <h2 class="pane-title">Monaco Editor</h2>
259
+ <p class="hint">
260
+ Edit the file picked above with completion, hover and diagnostics. Save (Ctrl+S) keeps every edit in this browser, over the
261
+ files in <code>../workspace</code>.
262
+ </p>
263
+ <div class="pane-body">
264
+ <div id="editor"></div>
265
+ </div>
266
+ </section>
267
+ <section id="diagram-pane" hidden>
268
+ <h2 class="pane-title">Diagram Editor</h2>
269
+ <p class="hint">
270
+ The diagram of the file picked above. An arrow runs from a node to the node it references; a dashed node is declared in
271
+ another file. Create a node from the palette; the file stores no positions, so each node is placed above the node it
272
+ references rather than where you drop it.
273
+ </p>
274
+ <div class="pane-body">
275
+ <div id="diagram"></div>
276
+ </div>
277
+ </section>
278
+ </main>
279
+ <footer>
280
+ <h2 class="pane-title">Log View</h2>
281
+ <p class="hint">__LOG_HINT__</p>
282
+ <p id="data-status" hidden></p>
283
+ <pre id="log" aria-label="Log"></pre>
284
+ </footer>
285
+ <script src="./out/page.js"></script>
286
+ </body>
287
+ </html>
@@ -0,0 +1,24 @@
1
+ // The smoke tier: Playwright serves the page with `npm start` and drives it in
2
+ // Chromium, which `npm run test:e2e:install` downloads once. `npm start`
3
+ // bundles the page itself but not the packages it imports, so build those
4
+ // first.
5
+
6
+ import { defineConfig, devices } from '@playwright/test';
7
+
8
+ /** The port `npm start` serves on; set `PORT` to run beside another server. */
9
+ const PORT = Number(process.env.PORT || 4937);
10
+
11
+ export default defineConfig({
12
+ testDir: './test',
13
+ // The first load parses the grammar and builds the workspace in the worker.
14
+ timeout: 60_000,
15
+ expect: { timeout: 20_000 },
16
+ use: { ...devices['Desktop Chrome'], baseURL: `http://localhost:${PORT}` },
17
+ webServer: {
18
+ command: 'npm start',
19
+ url: `http://localhost:${PORT}`,
20
+ // A server already on the port fails the run rather than being tested in this one's place.
21
+ reuseExistingServer: false,
22
+ timeout: 120_000
23
+ }
24
+ });
@@ -0,0 +1,15 @@
1
+ // The data part of the page: one document read through the data head, out of
2
+ // the same store the other heads serve.
3
+
4
+ import { DataConnectionWithEvents, type DataPort } from '@hydranium/protocol';
5
+ import type { __NAME__Element } from '__PROTOCOL_PACKAGE__';
6
+
7
+ /** Open `uri` through the data head and say what it holds on the status line below the panes. */
8
+ export async function showDataStatus(port: DataPort, uri: string): Promise<void> {
9
+ const session = new DataConnectionWithEvents<__NAME__Element>(port).createSession('__PROJECT_ID__-page');
10
+ const opened = await session.openDocument({ uri });
11
+ const line = document.getElementById('data-status') as HTMLElement;
12
+ const file = uri.slice(uri.lastIndexOf('/') + 1);
13
+ line.textContent = `data head: ${file} holds a ${opened.model?.root.$type ?? 'document with no model'}`;
14
+ line.hidden = false;
15
+ }
@@ -0,0 +1,60 @@
1
+ // Save for a page with no editor: the diagram's edits are saved through the
2
+ // GLSP head, which writes them to the store the worker keeps in IndexedDB.
3
+
4
+ import { EditorContextService, type IActionDispatcher, TYPES } from '@eclipse-glsp/client';
5
+ import type { GlspDiagramMount } from '@hydranium/glsp-client';
6
+ import { describeError, RequestSaveModelAction } from '@hydranium/protocol';
7
+ import { setStatus } from './log.js';
8
+
9
+ /** Save `diagram` through the GLSP head: resolves once the save has written, rejects when it failed. */
10
+ async function saveDiagram(diagram: GlspDiagramMount): Promise<void> {
11
+ await diagram.container.get<IActionDispatcher>(TYPES.IActionDispatcher).request(RequestSaveModelAction.create());
12
+ }
13
+
14
+ /**
15
+ * Save the diagram the page switches away from: Save and the reload guard reach
16
+ * only the diagram shown, so an edit left in another file would otherwise be
17
+ * lost unasked. Saved whether or not it reads as dirty, since an edit made just
18
+ * before the switch is marked dirty only once the server has answered it; the
19
+ * server runs the save after that edit, and a save of a clean diagram writes
20
+ * nothing.
21
+ */
22
+ export async function saveBeforeLeaving(diagram: GlspDiagramMount): Promise<void> {
23
+ await saveDiagram(diagram).catch((error: unknown) => {
24
+ setStatus(`save failed: ${describeError(error)}`);
25
+ throw error;
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Arm the page's Save and Ctrl+S for the diagram `shownDiagram` reads at the
31
+ * time, and ask before a reload drops an unsaved edit.
32
+ */
33
+ export function armDiagramSave(shownDiagram: () => GlspDiagramMount | undefined): void {
34
+ const save = (): void => {
35
+ const diagram = shownDiagram();
36
+ if (diagram === undefined) {
37
+ setStatus('no diagram to save');
38
+ return;
39
+ }
40
+ saveDiagram(diagram).then(
41
+ () => setStatus('saved the diagram'),
42
+ (error: unknown) => setStatus(`save failed: ${describeError(error)}`)
43
+ );
44
+ };
45
+ const saveButton = document.getElementById('save') as HTMLButtonElement;
46
+ saveButton.addEventListener('click', save);
47
+ // Ctrl+S (Cmd+S) saves too, in place of the browser's "Save page as".
48
+ window.addEventListener('keydown', event => {
49
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
50
+ event.preventDefault();
51
+ save();
52
+ }
53
+ });
54
+ window.addEventListener('beforeunload', event => {
55
+ if (shownDiagram()?.container.get(EditorContextService).isDirty) {
56
+ event.preventDefault();
57
+ }
58
+ });
59
+ saveButton.hidden = false;
60
+ }
@@ -0,0 +1,121 @@
1
+ // The GLSP part of the page: the __GRAMMAR__ diagram of the file the page
2
+ // shows, drawn by the client module every host of this project mounts.
3
+
4
+ import type { WorkerHeads } from '@hydranium/client-browser';
5
+ import { glspConnection, type GlspDiagramMount, HydraniumGlspClient, mountGlspDiagram } from '@hydranium/glsp-client';
6
+ import { describeError, type Logger } from '@hydranium/protocol';
7
+ import { initialize__GRAMMAR__DiagramContainer } from '__CLIENT_PACKAGE__';
8
+ import { __GRAMMAR_ID_CONST___DIAGRAM_TYPE, __GRAMMAR_ID_CONST___LANGUAGE } from '__PROTOCOL_PACKAGE__';
9
+ import { appendLog } from './log.js';
10
+ import type { ShownFile } from './shown-file.js';
11
+
12
+ /**
13
+ * Show the diagram of the file the page shows, and of each file it switches
14
+ * to, and hand back what reads the mount shown now, if it loaded. A file of
15
+ * another language hides the pane. `beforeLeave` runs on the shown mount
16
+ * before a switch disposes it, while the diagram takes no input; when it
17
+ * throws, that mount stays shown and the page switches back to its file.
18
+ */
19
+ export async function mountDiagram(
20
+ connection: WorkerHeads['glsp'],
21
+ shownFile: ShownFile,
22
+ logger: Logger,
23
+ beforeLeave?: (mount: GlspDiagramMount) => Promise<void>
24
+ ): Promise<() => GlspDiagramMount | undefined> {
25
+ const glspClient = new HydraniumGlspClient({ id: 'diagram', connectionProvider: glspConnection(connection) });
26
+ let mounted: GlspDiagramMount | undefined;
27
+ let mountedUri: string | undefined;
28
+ const show = async (uri: string): Promise<void> => {
29
+ // Switched away while queued: the show queued for the newer file replaces this one.
30
+ // Switched back before the switch away ran: its diagram is still the one shown.
31
+ if (uri !== shownFile.current() || uri === mountedUri) {
32
+ return;
33
+ }
34
+ // Only a mount that loaded: one that failed has no edits to lose, and its
35
+ // dispatcher may refuse every save, which would keep it shown for good.
36
+ if (mounted !== undefined && mountedUri !== undefined && beforeLeave !== undefined) {
37
+ // Inert, since an edit made while it runs would outlast its wait and be dropped with the mount.
38
+ const shown = document.getElementById('diagram') as HTMLElement;
39
+ shown.inert = true;
40
+ try {
41
+ await beforeLeave(mounted);
42
+ } catch (error: unknown) {
43
+ // The diagram stays on its file, so the switch goes back to it.
44
+ shownFile.show(mountedUri);
45
+ throw error;
46
+ } finally {
47
+ shown.inert = false;
48
+ }
49
+ // Switched again while it ran: the show queued for that file takes over.
50
+ if (uri !== shownFile.current()) {
51
+ return;
52
+ }
53
+ }
54
+ mounted?.dispose();
55
+ mounted = undefined;
56
+ mountedUri = undefined;
57
+ // A fresh element, since a disposed mount leaves what it drew.
58
+ const element = document.createElement('div');
59
+ element.id = 'diagram';
60
+ (document.getElementById('diagram') as HTMLElement).replaceWith(element);
61
+ const pane = document.getElementById('diagram-pane') as HTMLElement;
62
+ // Shown before the mount, which measures the element it draws into.
63
+ pane.hidden = !__GRAMMAR_ID_CONST___LANGUAGE.fileExtensions.some(extension => uri.endsWith(extension));
64
+ if (pane.hidden) {
65
+ return;
66
+ }
67
+ mounted = await mountGlspDiagram({
68
+ mount: element,
69
+ diagramType: __GRAMMAR_ID_CONST___DIAGRAM_TYPE,
70
+ sourceUri: uri,
71
+ glspClient,
72
+ initializeDiagramContainer: initialize__GRAMMAR__DiagramContainer,
73
+ logger
74
+ });
75
+ if (mounted.loadOutcome.status === 'failed') {
76
+ throw new Error(`the diagram of ${uri} did not load: ${describeError(mounted.loadOutcome.error)}`);
77
+ }
78
+ mountedUri = uri;
79
+ };
80
+
81
+ // One mount at a time, so one still loading is not disposed half built, and a
82
+ // failed one does not stop the next.
83
+ let switching: Promise<void> = Promise.resolve();
84
+ const follow = (uri: string): Promise<void> => {
85
+ const shown = switching.then(() => show(uri));
86
+ // Derived, so a dropped failure is reported, and before the catch below,
87
+ // so the caller resumes before the next switch starts.
88
+ const handed = shown.then(() => undefined);
89
+ switching = shown.catch(() => undefined);
90
+ return handed;
91
+ };
92
+ // Before the first mount, since the page can switch while it loads.
93
+ shownFile.onDidChange(uri => {
94
+ follow(uri).catch((error: unknown) => appendLog(describeError(error)));
95
+ });
96
+ // A first file that fails to load is logged as a later switch's is, so the
97
+ // page still arms what follows and the switch can reach a file that loads.
98
+ await follow(shownFile.current()).catch((error: unknown) => appendLog(describeError(error)));
99
+ // Asks before a reload or a closed tab drops an edit the server has not
100
+ // answered yet, which neither dirty state nor the editor shows until then.
101
+ window.addEventListener('beforeunload', event => {
102
+ if ((mounted?.editsInFlight ?? 0) > 0) {
103
+ event.preventDefault();
104
+ }
105
+ });
106
+ // Only a mount that loaded: one that failed has nothing to save.
107
+ return () => (mountedUri === undefined ? undefined : mounted);
108
+ }
109
+
110
+ /**
111
+ * Wait for the edits of `diagram` the server has not answered yet, before a
112
+ * save, which would miss them, or before a switch disposes it, as the server
113
+ * drops what an ended session still had queued and the reload guard reads only
114
+ * the diagram shown. Throws when one stays unanswered, so the save does not
115
+ * report success and the switch keeps the diagram shown.
116
+ */
117
+ export async function settleDiagramEdits(diagram: GlspDiagramMount | undefined): Promise<void> {
118
+ if ((await diagram?.whenEditsSettled()) === false) {
119
+ throw new Error('the server has not answered an edit of this diagram yet');
120
+ }
121
+ }
@@ -0,0 +1,102 @@
1
+ // The LSP part of the page: one Monaco editor over the LSP head, showing the
2
+ // file the page shows, and Save. The worker holds the files, so the page saves
3
+ // through the LSP head and the worker writes them to IndexedDB.
4
+
5
+ import { initializeLanguageClient, type WorkerHeads } from '@hydranium/client-browser';
6
+ import { MONACO_CLIENT_CAPABILITIES, MonacoLspClient } from '@hydranium/client-browser/monaco';
7
+ import { describeError } from '@hydranium/protocol';
8
+ import { ALL_LANGUAGES } from '__PROTOCOL_PACKAGE__';
9
+ import { PublishDiagnosticsNotification } from 'vscode-languageserver-protocol';
10
+ import { appendLog, setStatus } from './log.js';
11
+ import type { ShownFile } from './shown-file.js';
12
+ import { WORKSPACE_ROOT_URI } from './workspace.js';
13
+
14
+ /** Where Monaco's own worker bundle is served, substituted by `esbuild.mjs`. */
15
+ declare const __MONACO_WORKER_BUNDLE_URL__: string;
16
+
17
+ // Before any editor exists: Monaco otherwise looks for a worker script no
18
+ // bundle emits, and fails on the first edit.
19
+ self.MonacoEnvironment = { getWorker: () => new Worker(__MONACO_WORKER_BUNDLE_URL__) };
20
+
21
+ /** Each language as Monaco registers it, from the ids and extensions the server generates. */
22
+ const LANGUAGES = ALL_LANGUAGES.map(language => ({
23
+ id: language.languageId,
24
+ extensions: [...language.fileExtensions]
25
+ }));
26
+
27
+ /**
28
+ * Run the LSP handshake, then show the page's shown file in the editor,
29
+ * following each switch, and hand back the client {@link armEditorSave} saves
30
+ * through.
31
+ */
32
+ export async function startEditor(heads: WorkerHeads, shownFile: ShownFile): Promise<MonacoLspClient> {
33
+ let client!: MonacoLspClient;
34
+ await initializeLanguageClient(heads.lsp, {
35
+ rootUri: WORKSPACE_ROOT_URI,
36
+ capabilities: MONACO_CLIENT_CAPABILITIES,
37
+ onLogMessage: params => appendLog(params.message),
38
+ // Before `initialized`, since the server publishes from then on.
39
+ beforeInitialized: result => {
40
+ client = new MonacoLspClient(heads.lsp, result.capabilities, { languages: LANGUAGES });
41
+ heads.lsp.onNotification(PublishDiagnosticsNotification.type, params => client.applyDiagnostics(params));
42
+ }
43
+ });
44
+
45
+ const modelOf = (uri: string) => client.openDocument(uri, shownFile.files.get(uri) as string);
46
+ const editor = client.createEditor(document.getElementById('editor') as HTMLElement, modelOf(shownFile.current()), {
47
+ automaticLayout: true,
48
+ minimap: { enabled: false },
49
+ scrollBeyondLastLine: false,
50
+ wordWrap: 'on'
51
+ });
52
+ shownFile.onDidChange(uri => editor.setModel(modelOf(uri)));
53
+
54
+ // Go to definition across files switches the editor to the target file.
55
+ client.registerEditorOpener((uri, position) => {
56
+ if (!shownFile.files.has(uri)) {
57
+ return false;
58
+ }
59
+ shownFile.show(uri);
60
+ editor.setPosition(position);
61
+ editor.revealPositionInCenter(position);
62
+ return true;
63
+ });
64
+
65
+ // Asks before a reload or a closed tab drops an unsaved edit.
66
+ window.addEventListener('beforeunload', event => {
67
+ if (client.dirtyDocuments().length > 0) {
68
+ event.preventDefault();
69
+ }
70
+ });
71
+ (document.getElementById('editor-pane') as HTMLElement).hidden = false;
72
+ return client;
73
+ }
74
+
75
+ /**
76
+ * Arm the page's Save and Ctrl+S, which save every file the editor holds
77
+ * unsaved once `beforeSave` has settled: an edit made elsewhere, such as in the
78
+ * diagram, shows in the editor only once the server has answered it. When
79
+ * `beforeSave` throws, nothing is saved and the status says why.
80
+ */
81
+ export function armEditorSave(client: MonacoLspClient, beforeSave: () => Promise<void> = () => Promise.resolve()): void {
82
+ const save = (): void => {
83
+ // The wait can last up to its bound on a server that stopped answering.
84
+ setStatus('saving…');
85
+ beforeSave()
86
+ .then(() => client.saveAll())
87
+ .then(
88
+ saved => setStatus(`saved ${saved.length} file(s)`),
89
+ (error: unknown) => setStatus(`save failed: ${describeError(error)}`)
90
+ );
91
+ };
92
+ const saveButton = document.getElementById('save') as HTMLButtonElement;
93
+ saveButton.addEventListener('click', save);
94
+ // Ctrl+S (Cmd+S) saves too, in place of the browser's "Save page as".
95
+ window.addEventListener('keydown', event => {
96
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
97
+ event.preventDefault();
98
+ save();
99
+ }
100
+ });
101
+ saveButton.hidden = false;
102
+ }
@@ -0,0 +1,14 @@
1
+ // The page's status line and log area: what the page is told and the errors
2
+ // it reports.
3
+
4
+ /** Replace the one-line status in the title bar. */
5
+ export function setStatus(text: string): void {
6
+ (document.getElementById('status') as HTMLElement).textContent = text;
7
+ }
8
+
9
+ /** Add a line to the log area, kept scrolled to its end. */
10
+ export function appendLog(text: string): void {
11
+ const log = document.getElementById('log') as HTMLElement;
12
+ log.append(`${text}\n`);
13
+ log.scrollTop = log.scrollHeight;
14
+ }
@@ -0,0 +1,7 @@
1
+ // `monaco-editor-core` types its editor API only, so the worker entry
2
+ // `monaco-editor-worker.ts` imports is declared here.
3
+
4
+ declare module 'monaco-editor-core/esm/vs/editor/editor.worker.start.js' {
5
+ /** Start the editor worker's dispatcher, with `createClient`'s result answering requests of the embedder's own. */
6
+ export function start(createClient: () => object): void;
7
+ }