collabmd 0.1.45 → 0.1.46

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 (190) hide show
  1. package/README.md +39 -19
  2. package/bin/collabmd.js +27 -1
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
  4. package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
  5. package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
  6. package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
  7. package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
  8. package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
  9. package/dist/client/assets/dist-B5HTH2BO.js +1 -0
  10. package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
  11. package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
  12. package/dist/client/assets/dist-BEwK4csK.js +1 -0
  13. package/dist/client/assets/dist-BKW4PALF.js +1 -0
  14. package/dist/client/assets/dist-BWcHHOx-.js +1 -0
  15. package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
  16. package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
  17. package/dist/client/assets/dist-BjHJDtyU.js +1 -0
  18. package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
  19. package/dist/client/assets/dist-BoVBdSjk.js +1 -0
  20. package/dist/client/assets/dist-CC5IrQMG.js +1 -0
  21. package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
  22. package/dist/client/assets/dist-CZgOmxK6.js +1 -0
  23. package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
  24. package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
  25. package/dist/client/assets/dist-DLMiIsNC.js +11 -0
  26. package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
  27. package/dist/client/assets/dist-DaYdhcVs.js +1 -0
  28. package/dist/client/assets/dist-FWJsgGYI.js +1 -0
  29. package/dist/client/assets/dist-Kh2COra_.js +1 -0
  30. package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
  31. package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
  32. package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
  33. package/dist/client/assets/dist-jv4WdPnK.js +9 -0
  34. package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
  35. package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
  36. package/dist/client/assets/dist-tOaegDYS2.js +1 -0
  37. package/dist/client/assets/dist-ule2n6ak.js +1 -0
  38. package/dist/client/assets/dist-xElaxbia.js +1 -0
  39. package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-FzY1cVw8.js} +1 -1
  40. package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
  41. package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
  42. package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
  43. package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
  44. package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
  45. package/dist/client/assets/{excalidraw-editor-CyGwOhLz.css → excalidraw-editor-Bt7gMSoh.css} +1 -1
  46. package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
  47. package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
  48. package/dist/client/assets/{export-page-CUfws2T-.js → export-page-A7NHawJY.js} +3 -3
  49. package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
  50. package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
  51. package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
  52. package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
  53. package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
  54. package/dist/client/assets/index-ClbjMBVt.js +2 -0
  55. package/dist/client/assets/index-Pum8ET-5.css +1 -0
  56. package/dist/client/assets/main-B3uPyqic.js +893 -0
  57. package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
  58. package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
  59. package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
  60. package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
  61. package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
  62. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
  63. package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
  64. package/dist/client/assets/prod-Dy20X7eT.js +1 -0
  65. package/dist/client/assets/prod-hnwh7wRM.js +177 -0
  66. package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
  67. package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
  68. package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
  69. package/dist/client/drawio-editor.html +1 -1
  70. package/dist/client/excalidraw-editor.html +1 -1
  71. package/dist/client/export-document.html +2 -2
  72. package/dist/client/index.html +15 -3
  73. package/docker-compose.yml +42 -0
  74. package/package.json +17 -4
  75. package/scripts/local-structurizr-compose.mjs +101 -0
  76. package/src/client/app/index.html +13 -1
  77. package/src/client/application/app-shell/comments-feature.js +19 -10
  78. package/src/client/application/app-shell/git-feature.js +12 -0
  79. package/src/client/application/app-shell/presence-feature.js +2 -1
  80. package/src/client/application/app-shell/ui-feature-shell.js +45 -2
  81. package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
  82. package/src/client/application/app-shell/workspace-feature.js +5 -1
  83. package/src/client/application/app-shell-elements.js +2 -0
  84. package/src/client/application/pdf-preview-controller.js +992 -0
  85. package/src/client/application/structurizr-preview-controller.js +395 -0
  86. package/src/client/application/workspace-coordinator.js +25 -3
  87. package/src/client/application/workspace-preview-controller.js +91 -2
  88. package/src/client/bootstrap/collabmd-app-shell.js +31 -5
  89. package/src/client/browser-utils.js +44 -0
  90. package/src/client/domain/runtime-paths.js +26 -5
  91. package/src/client/domain/structurizr-language.js +99 -0
  92. package/src/client/domain/vault-paths.js +48 -0
  93. package/src/client/excalidraw-editor.js +833 -51
  94. package/src/client/infrastructure/browser-preferences-port.js +10 -0
  95. package/src/client/infrastructure/comment-thread-store.js +34 -13
  96. package/src/client/infrastructure/editor-session.js +10 -0
  97. package/src/client/infrastructure/editor-view-adapter.js +46 -2
  98. package/src/client/infrastructure/excalidraw-room-client.js +103 -14
  99. package/src/client/infrastructure/git-api-client.js +5 -0
  100. package/src/client/infrastructure/vault-api-client.js +26 -0
  101. package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
  102. package/src/client/presentation/drawio-embed-controller.js +9 -102
  103. package/src/client/presentation/drawio-viewer.js +118 -0
  104. package/src/client/presentation/excalidraw-embed-controller.js +74 -8
  105. package/src/client/presentation/file-action-controller.js +102 -0
  106. package/src/client/presentation/file-explorer-view.js +10 -29
  107. package/src/client/presentation/file-icon-svg.js +40 -0
  108. package/src/client/presentation/file-tree-state.js +2 -0
  109. package/src/client/presentation/git-diff-view-controller.js +731 -3
  110. package/src/client/presentation/outline-controller.js +86 -0
  111. package/src/client/presentation/quick-switcher-controller.js +25 -13
  112. package/src/client/presentation/quick-switcher-text-search.js +1 -1
  113. package/src/client/styles/components/navigation.css +11 -1
  114. package/src/client/styles/features/diagram-preview.css +76 -0
  115. package/src/client/styles/features/diff-special.css +309 -0
  116. package/src/client/styles/foundation/themes.css +6 -0
  117. package/src/client/styles/layout/shell.css +358 -1
  118. package/src/client/styles/layout/view-modes.css +1 -0
  119. package/src/client/styles/style.css +1 -0
  120. package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
  121. package/src/domain/comment-threads.js +63 -5
  122. package/src/domain/excalidraw-room-codec.js +6 -4
  123. package/src/domain/file-kind.js +35 -3
  124. package/src/server/config/env.js +79 -0
  125. package/src/server/create-app-server.js +12 -0
  126. package/src/server/domain/bases/base-definition.js +1 -1
  127. package/src/server/domain/bases/base-expression-runtime.js +1 -4
  128. package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
  129. package/src/server/domain/bases/base-transform.js +1 -4
  130. package/src/server/domain/collaboration/collaboration-room.js +55 -21
  131. package/src/server/domain/comment-overview.js +15 -5
  132. package/src/server/domain/ripgrep-search-service.js +4 -0
  133. package/src/server/infrastructure/git/diff-service.js +5 -0
  134. package/src/server/infrastructure/git/git-service.js +21 -0
  135. package/src/server/infrastructure/git/history-service.js +54 -0
  136. package/src/server/infrastructure/git/image-mime.js +14 -0
  137. package/src/server/infrastructure/git/untracked-files.js +28 -1
  138. package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
  139. package/src/server/infrastructure/http/create-request-handler.js +16 -23
  140. package/src/server/infrastructure/http/create-static-handler.js +5 -2
  141. package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
  142. package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
  143. package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
  144. package/src/server/infrastructure/http/http-request-helpers.js +22 -0
  145. package/src/server/infrastructure/persistence/path-utils.js +1 -1
  146. package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
  147. package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
  148. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
  149. package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
  150. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
  151. package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
  152. package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
  153. package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
  154. package/dist/client/assets/dist-BH87JoHx.js +0 -11
  155. package/dist/client/assets/dist-BIkPRZO7.js +0 -6
  156. package/dist/client/assets/dist-BqCsFpx4.js +0 -1
  157. package/dist/client/assets/dist-C2JHCt8U.js +0 -1
  158. package/dist/client/assets/dist-CZjTtmTu.js +0 -1
  159. package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
  160. package/dist/client/assets/dist-CqQEC8gR.js +0 -1
  161. package/dist/client/assets/dist-D4Rzqod1.js +0 -1
  162. package/dist/client/assets/dist-D5sn-mwk.js +0 -9
  163. package/dist/client/assets/dist-D7G-n87v.js +0 -1
  164. package/dist/client/assets/dist-DRqOrNgN.js +0 -1
  165. package/dist/client/assets/dist-DetkMS_4.js +0 -1
  166. package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
  167. package/dist/client/assets/dist-DtbjNB28.js +0 -1
  168. package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
  169. package/dist/client/assets/dist-GrygD2zE.js +0 -1
  170. package/dist/client/assets/dist-HmEpWUow.js +0 -1
  171. package/dist/client/assets/dist-RTa81Km7.js +0 -1
  172. package/dist/client/assets/drawioEditor-CXaTM9Ds.js +0 -2
  173. package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
  174. package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
  175. package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
  176. package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
  177. package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
  178. package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
  179. package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
  180. package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
  181. package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
  182. package/dist/client/assets/index-C3YnUgMD.css +0 -1
  183. package/dist/client/assets/index-D2wXraJ1.js +0 -2
  184. package/dist/client/assets/main-DtiGz7uI.js +0 -795
  185. package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
  186. package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
  187. package/dist/client/assets/room-CQLoqtMo.js +0 -1
  188. package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
  189. package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
  190. package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
@@ -1,8 +1,10 @@
1
+ import { PdfPreviewController } from '../application/pdf-preview-controller.js';
1
2
  import { PreviewRenderer } from '../application/preview-renderer.js';
2
3
  import { ensureQuickSwitcherInstance, toggleQuickSwitcherInstance } from '../application/quick-switcher-loader.js';
3
4
  import { WorkspaceRouteController } from '../application/workspace-route-controller.js';
4
5
  import { WikiLinkFileController } from '../application/wiki-link-file-controller.js';
5
6
  import { WorkspacePreviewController } from '../application/workspace-preview-controller.js';
7
+ import { StructurizrPreviewController } from '../application/structurizr-preview-controller.js';
6
8
  import { WorkspaceCoordinator } from '../application/workspace-coordinator.js';
7
9
  import { bindAppShellElements } from '../application/app-shell-elements.js';
8
10
  import { chatFeature } from '../application/app-shell/chat-feature.js';
@@ -108,6 +110,7 @@ export class CollabMdAppShell {
108
110
  fileTreeShowExtensionsKey: 'collabmd-file-tree-show-extensions',
109
111
  lineWrappingKey: 'collabmd-editor-line-wrap',
110
112
  recentFilesKey: 'collabmd-recent-files',
113
+ vimModeKey: 'collabmd-editor-vim-mode',
111
114
  sidebarVisibleKey: 'collabmd-sidebar-visible',
112
115
  userNameKey: 'collabmd-user-name',
113
116
  });
@@ -146,13 +149,9 @@ export class CollabMdAppShell {
146
149
  });
147
150
  this.workspaceSync = new WorkspaceSyncClient({
148
151
  onTreeChange: (tree, metadata = {}) => {
149
- const wasReady = this.fileExplorerReady;
150
152
  this.fileExplorer.setTree(tree, metadata);
151
153
  this.handleCommentOverviewWorkspaceTreeChange?.();
152
154
  this.fileExplorerReady = true;
153
- if (!wasReady && this.isTabActive) {
154
- void this.workspaceRouteController.handleHashChange();
155
- }
156
155
  },
157
156
  onWorkspaceEvent: (event) => {
158
157
  void this.handleIncomingWorkspaceEvent(event);
@@ -288,6 +287,10 @@ export class CollabMdAppShell {
288
287
  previewElement: this.elements.previewContent,
289
288
  scrollEditorToLine: (lineNumber, viewportRatio) => this.session?.scrollToLine(lineNumber, viewportRatio),
290
289
  });
290
+ this.pdfPreview = new PdfPreviewController({
291
+ outlineController: this.outlineController,
292
+ previewContainer: this.elements.previewContainer,
293
+ });
291
294
  this.backlinksPanel = new BacklinksPanel({
292
295
  headerPanelElement: this.elements.backlinksHeaderPanel,
293
296
  inlinePanelElement: this.elements.backlinksInlinePanel,
@@ -351,6 +354,10 @@ export class CollabMdAppShell {
351
354
  onToggleReaction: (threadId, messageId, emoji) => this.session?.toggleCommentReaction(threadId, messageId, emoji),
352
355
  onResolveThread: (threadId) => this.resolveCommentThread(threadId),
353
356
  });
357
+ this.structurizrPreview = new StructurizrPreviewController({
358
+ enabled: this.runtimeConfig.structurizrEnabled === true,
359
+ syncWorkspace: (payload) => this.vaultApiClient.syncStructurizrWorkspace(payload),
360
+ });
354
361
  this.workspacePreviewController = new WorkspacePreviewController({
355
362
  backlinksPanel: this.backlinksPanel,
356
363
  basesPreview: this.basesPreview,
@@ -363,10 +370,13 @@ export class CollabMdAppShell {
363
370
  isDrawioFile: (filePath) => this.isDrawioFile(filePath),
364
371
  isExcalidrawFile: (filePath) => this.isExcalidrawFile(filePath),
365
372
  isImageFile: (filePath) => this.isImageFile(filePath),
373
+ isPdfFile: (filePath) => this.isPdfFile(filePath),
366
374
  isMermaidFile: (filePath) => this.isMermaidFile(filePath),
367
375
  isPlantUmlFile: (filePath) => this.isPlantUmlFile(filePath),
368
376
  layoutController: this.layoutController,
377
+ structurizrPreview: this.structurizrPreview,
369
378
  outlineController: this.outlineController,
379
+ pdfPreview: this.pdfPreview,
370
380
  previewRenderer: this.previewRenderer,
371
381
  schedulePreviewLayoutSync: (options) => this.schedulePreviewLayoutSync(options),
372
382
  scrollSyncController: this.scrollSyncController,
@@ -381,6 +391,7 @@ export class CollabMdAppShell {
381
391
  wikiLinkAutoCreate: this.runtimeConfig.wikiLinkAutoCreate !== false,
382
392
  });
383
393
  this.gitDiffView = new GitDiffViewController({
394
+ getTheme: () => this.themeController.getTheme(),
384
395
  gitApiClient: this.gitApiClient,
385
396
  onBackToHistory: ({ historyFilePath } = {}) => {
386
397
  if (historyFilePath) {
@@ -394,6 +405,7 @@ export class CollabMdAppShell {
394
405
  onStageFile: (filePath, { scope }) => this.stageGitFile(filePath, { scope }),
395
406
  onUnstageFile: (filePath, { scope }) => this.unstageGitFile(filePath, { scope }),
396
407
  toastController: this.toastController,
408
+ vaultApiClient: this.vaultApiClient,
397
409
  });
398
410
  this.fileHistoryView = new FileHistoryViewController({
399
411
  diffRenderer: this.gitDiffView,
@@ -437,6 +449,7 @@ export class CollabMdAppShell {
437
449
  lineInfoElement: this.elements.lineInfo,
438
450
  lineWrappingEnabled: options.lineWrappingEnabled,
439
451
  localUser: options.localUser,
452
+ vimModeEnabled: options.vimModeEnabled,
440
453
  onImagePaste: options.onImagePaste,
441
454
  onAwarenessChange: options.onAwarenessChange,
442
455
  onCommentsChange: options.onCommentsChange,
@@ -448,6 +461,7 @@ export class CollabMdAppShell {
448
461
  getDisplayName: (filePath) => this.getDisplayName(filePath),
449
462
  getFileList: () => this.fileExplorer.flatDocumentFiles,
450
463
  getLineWrappingEnabled: () => this.getStoredLineWrapping(),
464
+ getVimModeEnabled: () => this.getStoredVimMode(),
451
465
  getLocalUser: () => this.lobby.getLocalUser(),
452
466
  getStoredUserName: () => this.getStoredUserName(),
453
467
  getTheme: () => this.themeController.getTheme(),
@@ -455,8 +469,10 @@ export class CollabMdAppShell {
455
469
  isDrawioFile: (filePath) => this.isDrawioFile(filePath),
456
470
  isExcalidrawFile: (filePath) => this.isExcalidrawFile(filePath),
457
471
  isImageFile: (filePath) => this.isImageFile(filePath),
472
+ isPdfFile: (filePath) => this.isPdfFile(filePath),
458
473
  isMermaidFile: (filePath) => this.isMermaidFile(filePath),
459
474
  isPlantUmlFile: (filePath) => this.isPlantUmlFile(filePath),
475
+ isStructurizrWorkspaceFile: (filePath) => this.isStructurizrWorkspaceFile(filePath),
460
476
  isTabActive: () => this.isTabActive,
461
477
  loadBootstrapContent: async (filePath) => {
462
478
  const response = await this.vaultApiClient.readFile(filePath);
@@ -477,7 +493,7 @@ export class CollabMdAppShell {
477
493
  this.clearInitialFileBootstrap();
478
494
  },
479
495
  onConnectionChange: (state) => this.handleConnectionChange(state),
480
- onContentChange: ({ isBase, isMermaid, isPlantUml }) => {
496
+ onContentChange: ({ isBase, isMermaid, isPlantUml, isStructurizrWorkspace }) => {
481
497
  this.handleCommentEditorContentChange();
482
498
  if (isBase) {
483
499
  this.scheduleBaseFilePreview(this.currentFilePath, {
@@ -486,6 +502,14 @@ export class CollabMdAppShell {
486
502
  return;
487
503
  }
488
504
 
505
+ if (isStructurizrWorkspace) {
506
+ this.structurizrPreview.queueSync({
507
+ filePath: this.currentFilePath,
508
+ source: this.session?.getText?.() ?? '',
509
+ });
510
+ return;
511
+ }
512
+
489
513
  this.previewRenderer.queueRender();
490
514
  if (!isMermaid && !isPlantUml) {
491
515
  this.scheduleBacklinkRefresh();
@@ -515,6 +539,8 @@ export class CollabMdAppShell {
515
539
  onRenderBasePreview: (filePath) => this.renderBaseFilePreview(filePath),
516
540
  onRenderExcalidrawPreview: (filePath) => this.workspacePreviewController.renderExcalidrawFilePreview(filePath),
517
541
  onRenderImagePreview: (filePath) => this.workspacePreviewController.renderImageFilePreview(filePath),
542
+ onRenderPdfPreview: (filePath) => this.workspacePreviewController.renderPdfFilePreview(filePath),
543
+ onRenderStructurizrPreview: (filePath, options) => this.workspacePreviewController.renderStructurizrFilePreview(filePath, options),
518
544
  onSyncWrapToggle: () => this.syncWrapToggle(),
519
545
  onUpdateActiveFile: (filePath) => this.fileExplorer.setActiveFile(filePath),
520
546
  onUpdateCurrentFile: (filePath) => {
@@ -2,6 +2,50 @@ function getWindowRef() {
2
2
  return typeof window === 'undefined' ? globalThis : window;
3
3
  }
4
4
 
5
+ export function pickFiles({ accept = '', multiple = false } = {}) {
6
+ return new Promise((resolve) => {
7
+ const input = document.createElement('input');
8
+ const windowRef = getWindowRef();
9
+ input.type = 'file';
10
+ input.accept = accept;
11
+ input.multiple = multiple;
12
+ input.style.position = 'fixed';
13
+ input.style.left = '-9999px';
14
+ document.body.appendChild(input);
15
+ let settled = false;
16
+ let focusTimer = null;
17
+
18
+ const cleanup = (files = []) => {
19
+ if (settled) {
20
+ return;
21
+ }
22
+
23
+ settled = true;
24
+ if (focusTimer) {
25
+ windowRef.clearTimeout(focusTimer);
26
+ }
27
+ windowRef.removeEventListener('focus', handleWindowFocus);
28
+ input.remove();
29
+ resolve(Array.from(files ?? []));
30
+ };
31
+
32
+ const handleWindowFocus = () => {
33
+ focusTimer = windowRef.setTimeout(() => {
34
+ if (settled || input.files?.length) {
35
+ return;
36
+ }
37
+
38
+ cleanup();
39
+ }, 250);
40
+ };
41
+
42
+ input.addEventListener('change', () => cleanup(input.files), { once: true });
43
+ input.addEventListener('cancel', () => cleanup(), { once: true });
44
+ windowRef.addEventListener('focus', handleWindowFocus, { once: true });
45
+ input.click();
46
+ });
47
+ }
48
+
5
49
  export function requestIdleRender(callback, timeout) {
6
50
  const windowRef = getWindowRef();
7
51
  if (typeof windowRef.requestIdleCallback === 'function') {
@@ -86,6 +86,7 @@ export function getClientRuntimeConfig() {
86
86
  unavailableReason: 'ripgrep search is unavailable',
87
87
  version: '',
88
88
  },
89
+ structurizrEnabled: false,
89
90
  wikiLinkAutoCreate: true,
90
91
  wsBasePath: '/ws',
91
92
  ...(window.__COLLABMD_CONFIG__ ?? {}),
@@ -143,12 +144,32 @@ export function resolveApiUrl(pathValue = '/', config = getClientRuntimeConfig()
143
144
  return resolveAppPath(`/api${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`, config);
144
145
  }
145
146
 
146
- export function resolveWsBaseUrl(config = getClientRuntimeConfig()) {
147
- const params = new URLSearchParams(window.location.search);
148
- const customServerUrl = params.get('server');
147
+ export function resolveWsServerOverride(config = getClientRuntimeConfig()) {
148
+ if (!['development', 'test'].includes(config.environment)) {
149
+ return '';
150
+ }
151
+
152
+ const customServerUrl = new URLSearchParams(window.location.search).get('server');
153
+ if (!customServerUrl) {
154
+ return '';
155
+ }
156
+
157
+ try {
158
+ const parsedUrl = new URL(customServerUrl, window.location.origin);
159
+ if (!['http:', 'https:', 'ws:', 'wss:'].includes(parsedUrl.protocol)) {
160
+ return '';
161
+ }
149
162
 
150
- if (customServerUrl) {
151
- return trimTrailingSlash(customServerUrl);
163
+ return trimTrailingSlash(parsedUrl.toString());
164
+ } catch {
165
+ return '';
166
+ }
167
+ }
168
+
169
+ export function resolveWsBaseUrl(config = getClientRuntimeConfig()) {
170
+ const serverOverride = resolveWsServerOverride(config);
171
+ if (serverOverride) {
172
+ return serverOverride;
152
173
  }
153
174
 
154
175
  if (config.publicWsBaseUrl) {
@@ -0,0 +1,99 @@
1
+ import { LanguageDescription, LanguageSupport, StreamLanguage } from '@codemirror/language';
2
+
3
+ const KEYWORDS = new Set([
4
+ 'animation',
5
+ 'autoLayout',
6
+ 'component',
7
+ 'configuration',
8
+ 'container',
9
+ 'custom',
10
+ 'decisions',
11
+ 'deployment',
12
+ 'description',
13
+ 'dynamic',
14
+ 'element',
15
+ 'filtered',
16
+ 'group',
17
+ 'include',
18
+ 'image',
19
+ 'model',
20
+ 'person',
21
+ 'perspectives',
22
+ 'properties',
23
+ 'relationship',
24
+ 'relationships',
25
+ 'styles',
26
+ 'systemContext',
27
+ 'systemLandscape',
28
+ 'system',
29
+ 'theme',
30
+ 'views',
31
+ 'workspace',
32
+ ]);
33
+
34
+ function readWord(stream) {
35
+ const match = stream.match(/[A-Za-z_][\w.-]*/u, false);
36
+ if (!match) {
37
+ return '';
38
+ }
39
+
40
+ stream.match(/[A-Za-z_][\w.-]*/u);
41
+ return match[0];
42
+ }
43
+
44
+ const structurizrStreamLanguage = StreamLanguage.define({
45
+ languageData: {
46
+ commentTokens: { line: '//' },
47
+ },
48
+ token(stream) {
49
+ if (stream.eatSpace()) {
50
+ return null;
51
+ }
52
+
53
+ if (stream.match('//')) {
54
+ stream.skipToEnd();
55
+ return 'comment';
56
+ }
57
+
58
+ if (stream.match(/!\w+/u)) {
59
+ return 'meta';
60
+ }
61
+
62
+ if (stream.match(/"(?:[^"\\]|\\.)*"?/u)) {
63
+ return 'string';
64
+ }
65
+
66
+ if (stream.match(/\b\d+(?:\.\d+)?\b/u)) {
67
+ return 'number';
68
+ }
69
+
70
+ if (stream.match(/[-=]?>|[{}[\]():,=]/u)) {
71
+ return 'operator';
72
+ }
73
+
74
+ const word = readWord(stream);
75
+ if (word) {
76
+ if (KEYWORDS.has(word)) {
77
+ return 'keyword';
78
+ }
79
+
80
+ if (/^[A-Z][A-Z0-9_]*$/u.test(word)) {
81
+ return 'typeName';
82
+ }
83
+
84
+ return 'variableName';
85
+ }
86
+
87
+ stream.next();
88
+ return null;
89
+ },
90
+ });
91
+
92
+ export const structurizrLanguage = new LanguageSupport(structurizrStreamLanguage);
93
+
94
+ export const structurizrLanguageDescription = LanguageDescription.of({
95
+ alias: ['structurizr', 'structurizr-dsl'],
96
+ extensions: ['dsl'],
97
+ name: 'Structurizr DSL',
98
+ support: structurizrLanguage,
99
+ });
@@ -3,6 +3,7 @@ import {
3
3
  isDrawioFilePath,
4
4
  isMermaidFilePath,
5
5
  isPlantUmlFilePath,
6
+ isStructurizrFilePath,
6
7
  stripVaultFileExtension,
7
8
  } from '../../domain/file-kind.js';
8
9
 
@@ -135,3 +136,50 @@ export function createPlantUmlStarter(filePath) {
135
136
  path: nextPath,
136
137
  };
137
138
  }
139
+
140
+ export function createStructurizrStarter(filePath) {
141
+ const normalizedPath = normalizeVaultPathInput(filePath);
142
+ const nextPath = isStructurizrFilePath(normalizedPath)
143
+ ? normalizedPath
144
+ : `${normalizedPath}.dsl`;
145
+ return {
146
+ content: [
147
+ 'workspace "CollabMD Example" "A small Structurizr workspace" {',
148
+ ' model {',
149
+ ' user = person "User" "Uses the system"',
150
+ ' system = softwareSystem "Example System" "The system being documented" {',
151
+ ' web = container "Web Application" "Serves the user interface" "JavaScript" {',
152
+ ' home = component "Home Page" "Shows the landing page" "React"',
153
+ ' editor = component "Editor" "Edits workspace content" "React"',
154
+ ' }',
155
+ ' api = container "API" "Handles requests" "Node.js"',
156
+ ' }',
157
+ ' user -> system "Uses"',
158
+ ' web -> api "Calls" "HTTPS/JSON"',
159
+ ' home -> editor "Links to"',
160
+ ' }',
161
+ ' views {',
162
+ ' systemContext system "Context" {',
163
+ ' include *',
164
+ ' autoLayout',
165
+ ' }',
166
+ ' container system "Containers" {',
167
+ ' include *',
168
+ ' autoLayout',
169
+ ' }',
170
+ ' component web "Components" {',
171
+ ' include *',
172
+ ' autoLayout',
173
+ ' }',
174
+ ' styles {',
175
+ ' element "Person" {',
176
+ ' shape person',
177
+ ' }',
178
+ ' }',
179
+ ' }',
180
+ '}',
181
+ '',
182
+ ].join('\n'),
183
+ path: nextPath,
184
+ };
185
+ }