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.
- package/README.md +39 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
- package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
- package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
- package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
- package/dist/client/assets/dist-B5HTH2BO.js +1 -0
- package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
- package/dist/client/assets/dist-BEwK4csK.js +1 -0
- package/dist/client/assets/dist-BKW4PALF.js +1 -0
- package/dist/client/assets/dist-BWcHHOx-.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
- package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
- package/dist/client/assets/dist-BjHJDtyU.js +1 -0
- package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
- package/dist/client/assets/dist-BoVBdSjk.js +1 -0
- package/dist/client/assets/dist-CC5IrQMG.js +1 -0
- package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
- package/dist/client/assets/dist-CZgOmxK6.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
- package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
- package/dist/client/assets/dist-DLMiIsNC.js +11 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
- package/dist/client/assets/dist-DaYdhcVs.js +1 -0
- package/dist/client/assets/dist-FWJsgGYI.js +1 -0
- package/dist/client/assets/dist-Kh2COra_.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
- package/dist/client/assets/dist-jv4WdPnK.js +9 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
- package/dist/client/assets/dist-tOaegDYS2.js +1 -0
- package/dist/client/assets/dist-ule2n6ak.js +1 -0
- package/dist/client/assets/dist-xElaxbia.js +1 -0
- package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-FzY1cVw8.js} +1 -1
- package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
- package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
- package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
- package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
- package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
- package/dist/client/assets/{excalidraw-editor-CyGwOhLz.css → excalidraw-editor-Bt7gMSoh.css} +1 -1
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
- package/dist/client/assets/{export-page-CUfws2T-.js → export-page-A7NHawJY.js} +3 -3
- package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
- package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
- package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
- package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
- package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
- package/dist/client/assets/index-ClbjMBVt.js +2 -0
- package/dist/client/assets/index-Pum8ET-5.css +1 -0
- package/dist/client/assets/main-B3uPyqic.js +893 -0
- package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
- package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
- package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
- package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
- package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
- package/dist/client/assets/prod-Dy20X7eT.js +1 -0
- package/dist/client/assets/prod-hnwh7wRM.js +177 -0
- package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +2 -2
- package/dist/client/index.html +15 -3
- package/docker-compose.yml +42 -0
- package/package.json +17 -4
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/index.html +13 -1
- package/src/client/application/app-shell/comments-feature.js +19 -10
- package/src/client/application/app-shell/git-feature.js +12 -0
- package/src/client/application/app-shell/presence-feature.js +2 -1
- package/src/client/application/app-shell/ui-feature-shell.js +45 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +5 -1
- package/src/client/application/app-shell-elements.js +2 -0
- package/src/client/application/pdf-preview-controller.js +992 -0
- package/src/client/application/structurizr-preview-controller.js +395 -0
- package/src/client/application/workspace-coordinator.js +25 -3
- package/src/client/application/workspace-preview-controller.js +91 -2
- package/src/client/bootstrap/collabmd-app-shell.js +31 -5
- package/src/client/browser-utils.js +44 -0
- package/src/client/domain/runtime-paths.js +26 -5
- package/src/client/domain/structurizr-language.js +99 -0
- package/src/client/domain/vault-paths.js +48 -0
- package/src/client/excalidraw-editor.js +833 -51
- package/src/client/infrastructure/browser-preferences-port.js +10 -0
- package/src/client/infrastructure/comment-thread-store.js +34 -13
- package/src/client/infrastructure/editor-session.js +10 -0
- package/src/client/infrastructure/editor-view-adapter.js +46 -2
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/vault-api-client.js +26 -0
- package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
- package/src/client/presentation/drawio-embed-controller.js +9 -102
- package/src/client/presentation/drawio-viewer.js +118 -0
- package/src/client/presentation/excalidraw-embed-controller.js +74 -8
- package/src/client/presentation/file-action-controller.js +102 -0
- package/src/client/presentation/file-explorer-view.js +10 -29
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +2 -0
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +25 -13
- package/src/client/presentation/quick-switcher-text-search.js +1 -1
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/diagram-preview.css +76 -0
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/foundation/themes.css +6 -0
- package/src/client/styles/layout/shell.css +358 -1
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
- package/src/domain/comment-threads.js +63 -5
- package/src/domain/excalidraw-room-codec.js +6 -4
- package/src/domain/file-kind.js +35 -3
- package/src/server/config/env.js +79 -0
- package/src/server/create-app-server.js +12 -0
- package/src/server/domain/bases/base-definition.js +1 -1
- package/src/server/domain/bases/base-expression-runtime.js +1 -4
- package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
- package/src/server/domain/bases/base-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +15 -5
- package/src/server/domain/ripgrep-search-service.js +4 -0
- package/src/server/infrastructure/git/diff-service.js +5 -0
- package/src/server/infrastructure/git/git-service.js +21 -0
- package/src/server/infrastructure/git/history-service.js +54 -0
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +16 -23
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- package/src/server/infrastructure/http/http-request-helpers.js +22 -0
- package/src/server/infrastructure/persistence/path-utils.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
- package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
- package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
- package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
- package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
- package/dist/client/assets/dist-BH87JoHx.js +0 -11
- package/dist/client/assets/dist-BIkPRZO7.js +0 -6
- package/dist/client/assets/dist-BqCsFpx4.js +0 -1
- package/dist/client/assets/dist-C2JHCt8U.js +0 -1
- package/dist/client/assets/dist-CZjTtmTu.js +0 -1
- package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
- package/dist/client/assets/dist-CqQEC8gR.js +0 -1
- package/dist/client/assets/dist-D4Rzqod1.js +0 -1
- package/dist/client/assets/dist-D5sn-mwk.js +0 -9
- package/dist/client/assets/dist-D7G-n87v.js +0 -1
- package/dist/client/assets/dist-DRqOrNgN.js +0 -1
- package/dist/client/assets/dist-DetkMS_4.js +0 -1
- package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
- package/dist/client/assets/dist-DtbjNB28.js +0 -1
- package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
- package/dist/client/assets/dist-GrygD2zE.js +0 -1
- package/dist/client/assets/dist-HmEpWUow.js +0 -1
- package/dist/client/assets/dist-RTa81Km7.js +0 -1
- package/dist/client/assets/drawioEditor-CXaTM9Ds.js +0 -2
- package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
- package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
- package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
- package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
- package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
- package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
- package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
- package/dist/client/assets/index-C3YnUgMD.css +0 -1
- package/dist/client/assets/index-D2wXraJ1.js +0 -2
- package/dist/client/assets/main-DtiGz7uI.js +0 -795
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
- package/dist/client/assets/room-CQLoqtMo.js +0 -1
- package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
- package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
|
@@ -22,6 +22,7 @@ export class BrowserPreferencesPort {
|
|
|
22
22
|
fileTreeShowExtensionsKey,
|
|
23
23
|
lineWrappingKey,
|
|
24
24
|
recentFilesKey = 'collabmd-recent-files',
|
|
25
|
+
vimModeKey = 'collabmd-editor-vim-mode',
|
|
25
26
|
sidebarVisibleKey,
|
|
26
27
|
userNameKey,
|
|
27
28
|
storage = globalThis.localStorage,
|
|
@@ -32,6 +33,7 @@ export class BrowserPreferencesPort {
|
|
|
32
33
|
this.sidebarVisibleKey = sidebarVisibleKey;
|
|
33
34
|
this.storage = storage;
|
|
34
35
|
this.userNameKey = userNameKey;
|
|
36
|
+
this.vimModeKey = vimModeKey;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
getUserName() {
|
|
@@ -58,6 +60,14 @@ export class BrowserPreferencesPort {
|
|
|
58
60
|
writeStorage(this.storage, this.lineWrappingKey, String(enabled));
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
getVimModeEnabled() {
|
|
64
|
+
return readStorage(this.storage, this.vimModeKey, null) === 'true';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setVimModeEnabled(enabled) {
|
|
68
|
+
writeStorage(this.storage, this.vimModeKey, String(enabled));
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
getRecentFiles() {
|
|
62
72
|
const stored = readStorage(this.storage, this.recentFilesKey, '[]');
|
|
63
73
|
try {
|
|
@@ -83,15 +83,21 @@ function normalizeSelectionAnchorPayload(payload, state) {
|
|
|
83
83
|
|
|
84
84
|
export class CommentThreadStore {
|
|
85
85
|
constructor({
|
|
86
|
+
canWrite = () => true,
|
|
87
|
+
createAnchor = null,
|
|
86
88
|
getDoc,
|
|
87
89
|
getEditorState,
|
|
88
90
|
getLocalUser,
|
|
89
91
|
onCommentsChange = null,
|
|
92
|
+
resolveThread = null,
|
|
90
93
|
}) {
|
|
94
|
+
this.canWrite = canWrite;
|
|
95
|
+
this.createAnchor = createAnchor;
|
|
91
96
|
this.getDoc = getDoc;
|
|
92
97
|
this.getEditorState = getEditorState;
|
|
93
98
|
this.getLocalUser = getLocalUser;
|
|
94
99
|
this.onCommentsChange = onCommentsChange;
|
|
100
|
+
this.resolveThread = resolveThread;
|
|
95
101
|
this.commentThreads = null;
|
|
96
102
|
this.ydoc = null;
|
|
97
103
|
this.ytext = null;
|
|
@@ -144,33 +150,44 @@ export class CommentThreadStore {
|
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
return serializeCommentThreads(this.commentThreads)
|
|
147
|
-
.map((thread) => this.
|
|
153
|
+
.map((thread) => this.resolveThread
|
|
154
|
+
? this.resolveThread(thread)
|
|
155
|
+
: this.resolveCommentThread(thread))
|
|
148
156
|
.filter(Boolean);
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
createCommentThread({ anchor, body }) {
|
|
152
|
-
|
|
153
|
-
if (!state || !this.commentThreads || !this.ytext || !this.ydoc) {
|
|
160
|
+
if (!this.canWrite() || !this.commentThreads || !this.ydoc) {
|
|
154
161
|
return null;
|
|
155
162
|
}
|
|
156
163
|
|
|
164
|
+
const state = this.getEditorState?.();
|
|
157
165
|
const normalizedBody = normalizeCommentBody(body);
|
|
158
|
-
|
|
166
|
+
if (!this.createAnchor && (!state || !this.ytext)) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const normalizedAnchor = this.createAnchor
|
|
171
|
+
? this.createAnchor(anchor)
|
|
172
|
+
: normalizeSelectionAnchorPayload(anchor, state);
|
|
159
173
|
if (!normalizedBody || !normalizedAnchor) {
|
|
160
174
|
return null;
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
const thread = createCommentThreadSharedType({
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
...normalizedAnchor,
|
|
179
|
+
...(this.createAnchor ? {} : {
|
|
180
|
+
anchorEnd: Y.relativePositionToJSON(
|
|
181
|
+
Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.endIndex),
|
|
182
|
+
),
|
|
183
|
+
}),
|
|
168
184
|
anchorKind: normalizedAnchor.anchorKind,
|
|
169
185
|
anchorQuote: normalizedAnchor.anchorQuote,
|
|
170
|
-
|
|
171
|
-
Y.
|
|
172
|
-
|
|
173
|
-
|
|
186
|
+
...(this.createAnchor ? {} : {
|
|
187
|
+
anchorStart: Y.relativePositionToJSON(
|
|
188
|
+
Y.createRelativePositionFromTypeIndex(this.ytext, normalizedAnchor.startIndex),
|
|
189
|
+
),
|
|
190
|
+
}),
|
|
174
191
|
createdAt: Date.now(),
|
|
175
192
|
createdByColor: this.getLocalUser()?.color ?? '',
|
|
176
193
|
createdByName: this.getLocalUser()?.name ?? 'Anonymous',
|
|
@@ -194,6 +211,10 @@ export class CommentThreadStore {
|
|
|
194
211
|
}
|
|
195
212
|
|
|
196
213
|
replyToCommentThread(threadId, body) {
|
|
214
|
+
if (!this.canWrite()) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
197
218
|
const normalizedBody = normalizeCommentBody(body);
|
|
198
219
|
if (!normalizedBody || !this.ydoc) {
|
|
199
220
|
return null;
|
|
@@ -291,7 +312,7 @@ export class CommentThreadStore {
|
|
|
291
312
|
}
|
|
292
313
|
|
|
293
314
|
deleteCommentThread(threadId) {
|
|
294
|
-
if (!this.commentThreads || !this.ydoc) {
|
|
315
|
+
if (!this.canWrite() || !this.commentThreads || !this.ydoc) {
|
|
295
316
|
return false;
|
|
296
317
|
}
|
|
297
318
|
|
|
@@ -8,6 +8,7 @@ export class EditorSession {
|
|
|
8
8
|
constructor({
|
|
9
9
|
editorContainer,
|
|
10
10
|
lineWrappingEnabled = true,
|
|
11
|
+
vimModeEnabled = false,
|
|
11
12
|
initialTheme,
|
|
12
13
|
lineInfoElement,
|
|
13
14
|
onAwarenessChange,
|
|
@@ -44,6 +45,7 @@ export class EditorSession {
|
|
|
44
45
|
initialTheme,
|
|
45
46
|
lineInfoElement,
|
|
46
47
|
lineWrappingEnabled,
|
|
48
|
+
vimModeEnabled,
|
|
47
49
|
onDocChanged: () => {
|
|
48
50
|
this.emitContentChange();
|
|
49
51
|
},
|
|
@@ -210,6 +212,14 @@ export class EditorSession {
|
|
|
210
212
|
return this.viewAdapter.isLineWrappingEnabled();
|
|
211
213
|
}
|
|
212
214
|
|
|
215
|
+
isVimModeEnabled() {
|
|
216
|
+
return this.viewAdapter.isVimModeEnabled();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setVimMode(enabled) {
|
|
220
|
+
return this.viewAdapter.setVimMode(enabled);
|
|
221
|
+
}
|
|
222
|
+
|
|
213
223
|
setLineWrapping(enabled) {
|
|
214
224
|
return this.viewAdapter.setLineWrapping(enabled);
|
|
215
225
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { vim } from '@replit/codemirror-vim';
|
|
1
2
|
import { autocompletion, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
2
3
|
import {
|
|
3
4
|
defaultKeymap,
|
|
@@ -45,15 +46,26 @@ import {
|
|
|
45
46
|
} from '@codemirror/view';
|
|
46
47
|
import { yCollab } from 'y-codemirror.next';
|
|
47
48
|
|
|
48
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
isBaseFilePath,
|
|
51
|
+
isMermaidFilePath,
|
|
52
|
+
isPlantUmlFilePath,
|
|
53
|
+
isStructurizrFilePath,
|
|
54
|
+
} from '../../domain/file-kind.js';
|
|
49
55
|
import { normalizeCommentQuote } from '../../domain/comment-threads.js';
|
|
50
56
|
import { createMarkdownToolbarEdit } from '../domain/markdown-formatting.js';
|
|
51
57
|
import { mermaidLanguage, mermaidLanguageDescription } from '../domain/mermaid-language.js';
|
|
52
58
|
import { wikiLinkCompletions } from '../domain/wiki-link-completions.js';
|
|
53
59
|
import { plantUmlLanguage, plantUmlLanguageDescription } from '../domain/plantuml-language.js';
|
|
60
|
+
import { structurizrLanguage, structurizrLanguageDescription } from '../domain/structurizr-language.js';
|
|
54
61
|
import { handleImagePasteEvent } from './editor-paste-utils.js';
|
|
55
62
|
|
|
56
|
-
const markdownCodeLanguages = [
|
|
63
|
+
const markdownCodeLanguages = [
|
|
64
|
+
...languages,
|
|
65
|
+
mermaidLanguageDescription,
|
|
66
|
+
plantUmlLanguageDescription,
|
|
67
|
+
structurizrLanguageDescription,
|
|
68
|
+
];
|
|
57
69
|
const pairedMatchingBracketMark = Decoration.mark({ class: 'cm-matchingBracket cm-matchingBracket-paired' });
|
|
58
70
|
const nonmatchingBracketMark = Decoration.mark({ class: 'cm-nonmatchingBracket' });
|
|
59
71
|
const remoteUpdateMark = Decoration.mark({ class: 'cm-remoteUpdateFlash' });
|
|
@@ -387,6 +399,10 @@ export function createLanguageExtension(filePath) {
|
|
|
387
399
|
return plantUmlLanguage;
|
|
388
400
|
}
|
|
389
401
|
|
|
402
|
+
if (isStructurizrFilePath(filePath)) {
|
|
403
|
+
return structurizrLanguage;
|
|
404
|
+
}
|
|
405
|
+
|
|
390
406
|
if (isBaseFilePath(filePath)) {
|
|
391
407
|
return yaml();
|
|
392
408
|
}
|
|
@@ -408,6 +424,7 @@ export class EditorViewAdapter {
|
|
|
408
424
|
initialTheme,
|
|
409
425
|
lineInfoElement,
|
|
410
426
|
lineWrappingEnabled = true,
|
|
427
|
+
vimModeEnabled = false,
|
|
411
428
|
onDocChanged = null,
|
|
412
429
|
onImagePaste = null,
|
|
413
430
|
onSelectionChanged = null,
|
|
@@ -418,6 +435,7 @@ export class EditorViewAdapter {
|
|
|
418
435
|
this.initialTheme = initialTheme;
|
|
419
436
|
this.lineInfoElement = lineInfoElement;
|
|
420
437
|
this.lineWrappingEnabled = lineWrappingEnabled;
|
|
438
|
+
this.vimModeEnabled = Boolean(vimModeEnabled);
|
|
421
439
|
this.onDocChanged = onDocChanged;
|
|
422
440
|
this.onImagePaste = onImagePaste;
|
|
423
441
|
this.onSelectionChanged = onSelectionChanged;
|
|
@@ -426,6 +444,7 @@ export class EditorViewAdapter {
|
|
|
426
444
|
this.themeCompartment = new Compartment();
|
|
427
445
|
this.syntaxThemeCompartment = new Compartment();
|
|
428
446
|
this.lineWrappingCompartment = new Compartment();
|
|
447
|
+
this.vimModeCompartment = new Compartment();
|
|
429
448
|
this.viewportFrame = 0;
|
|
430
449
|
this.remoteUpdateFlashTimer = 0;
|
|
431
450
|
this.lastLocalInputAt = 0;
|
|
@@ -478,6 +497,7 @@ export class EditorViewAdapter {
|
|
|
478
497
|
highlightActiveLine(),
|
|
479
498
|
highlightSelectionMatches(),
|
|
480
499
|
createSearchNavigationListener(),
|
|
500
|
+
this.vimModeCompartment.of(!readOnly && this.vimModeEnabled ? vim({ status: true }) : []),
|
|
481
501
|
keymap.of([
|
|
482
502
|
...closeBracketsKeymap,
|
|
483
503
|
...defaultKeymap,
|
|
@@ -645,6 +665,30 @@ export class EditorViewAdapter {
|
|
|
645
665
|
return this.lineWrappingEnabled;
|
|
646
666
|
}
|
|
647
667
|
|
|
668
|
+
isVimModeEnabled() {
|
|
669
|
+
return this.vimModeEnabled;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
setVimMode(enabled) {
|
|
673
|
+
const nextEnabled = Boolean(enabled);
|
|
674
|
+
if (nextEnabled === this.vimModeEnabled) {
|
|
675
|
+
return nextEnabled;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
this.vimModeEnabled = nextEnabled;
|
|
679
|
+
if (!this.editorView || this.editorView.state.readOnly) {
|
|
680
|
+
return nextEnabled;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this.editorView.dispatch({
|
|
684
|
+
effects: this.vimModeCompartment.reconfigure(
|
|
685
|
+
nextEnabled ? vim({ status: true }) : [],
|
|
686
|
+
),
|
|
687
|
+
});
|
|
688
|
+
this.editorView.focus();
|
|
689
|
+
return nextEnabled;
|
|
690
|
+
}
|
|
691
|
+
|
|
648
692
|
setLineWrapping(enabled) {
|
|
649
693
|
this.lineWrappingEnabled = Boolean(enabled);
|
|
650
694
|
|
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
tryParseSceneJson,
|
|
25
25
|
} from '../domain/excalidraw-scene.js';
|
|
26
26
|
import { resolveWsBaseUrl } from '../domain/runtime-paths.js';
|
|
27
|
+
import {
|
|
28
|
+
normalizeCommentQuote,
|
|
29
|
+
} from '../../domain/comment-threads.js';
|
|
30
|
+
import { CommentThreadStore } from './comment-thread-store.js';
|
|
27
31
|
import { stopReconnectOnControlledClose } from './yjs-provider-reset-guard.js';
|
|
28
32
|
|
|
29
33
|
const DEFAULT_EMPTY_SCENE_GUARD_MS = 250;
|
|
@@ -39,16 +43,21 @@ export const EXCALIDRAW_ROOM_CONNECTION_STATE = Object.freeze({
|
|
|
39
43
|
RECONNECTING_READONLY: 'reconnecting-readonly',
|
|
40
44
|
});
|
|
41
45
|
|
|
42
|
-
function
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
46
|
+
function areExcalidrawFilesEqual(left, right) {
|
|
47
|
+
if (left === right) {
|
|
48
|
+
return true;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
if (
|
|
48
|
-
return
|
|
51
|
+
if (!left || !right || typeof left !== 'object' || typeof right !== 'object') {
|
|
52
|
+
return false;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
return
|
|
55
|
+
return left.id === right.id
|
|
56
|
+
&& left.mimeType === right.mimeType
|
|
57
|
+
&& left.dataURL === right.dataURL
|
|
58
|
+
&& left.created === right.created
|
|
59
|
+
&& left.lastRetrieved === right.lastRetrieved
|
|
60
|
+
&& left.version === right.version;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
function normalizeElementVersion(value) {
|
|
@@ -81,6 +90,35 @@ function shouldSyncElementToRoom(localElement, roomElement) {
|
|
|
81
90
|
return localVersionNonce < roomVersionNonce;
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
function createDiagramCommentAnchor(element) {
|
|
94
|
+
const elementId = typeof element?.id === 'string' ? element.id.trim() : '';
|
|
95
|
+
const x = Number(element?.x);
|
|
96
|
+
const y = Number(element?.y);
|
|
97
|
+
const width = Number(element?.width);
|
|
98
|
+
const height = Number(element?.height);
|
|
99
|
+
if (!elementId || ![x, y, width, height].every(Number.isFinite)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
anchorKind: 'diagram-element',
|
|
105
|
+
anchorPoint: {
|
|
106
|
+
x: x + (width / 2),
|
|
107
|
+
y: y + (height / 2),
|
|
108
|
+
},
|
|
109
|
+
anchorQuote: normalizeCommentQuote(element.text),
|
|
110
|
+
anchorSnapshot: {
|
|
111
|
+
height,
|
|
112
|
+
text: normalizeCommentQuote(element.text),
|
|
113
|
+
type: typeof element.type === 'string' ? element.type : 'element',
|
|
114
|
+
width,
|
|
115
|
+
x,
|
|
116
|
+
y,
|
|
117
|
+
},
|
|
118
|
+
elementId,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
84
122
|
export class ExcalidrawRoomClient {
|
|
85
123
|
constructor({
|
|
86
124
|
cancelAnimationFrameFn = (frameId) => cancelAnimationFrame(frameId),
|
|
@@ -89,6 +127,7 @@ export class ExcalidrawRoomClient {
|
|
|
89
127
|
filePath = '',
|
|
90
128
|
now = () => Date.now(),
|
|
91
129
|
onCollaboratorsChange = () => {},
|
|
130
|
+
onCommentThreadsChange = () => {},
|
|
92
131
|
onConnectionStateChange = () => {},
|
|
93
132
|
onRemoteSceneJson = () => {},
|
|
94
133
|
requestAnimationFrameFn = (callback) => requestAnimationFrame(callback),
|
|
@@ -106,6 +145,7 @@ export class ExcalidrawRoomClient {
|
|
|
106
145
|
this.filePath = filePath;
|
|
107
146
|
this.now = now;
|
|
108
147
|
this.onCollaboratorsChange = onCollaboratorsChange;
|
|
148
|
+
this.onCommentThreadsChange = onCommentThreadsChange;
|
|
109
149
|
this.onConnectionStateChange = onConnectionStateChange;
|
|
110
150
|
this.onRemoteSceneJson = onRemoteSceneJson;
|
|
111
151
|
this.requestAnimationFrameFn = requestAnimationFrameFn;
|
|
@@ -138,12 +178,22 @@ export class ExcalidrawRoomClient {
|
|
|
138
178
|
this.lastViewportSignature = '';
|
|
139
179
|
this.lastSelectedIdsSignature = '';
|
|
140
180
|
this.pendingEmptySceneCandidate = null;
|
|
181
|
+
this.structuredObserversAttached = false;
|
|
141
182
|
this.canWriteToRoom = false;
|
|
142
183
|
this.waitingForAuthoritativeSync = false;
|
|
143
184
|
this.connectionState = EXCALIDRAW_ROOM_CONNECTION_STATE.CLOSED;
|
|
144
185
|
this.hasAuthoritativeSync = false;
|
|
145
186
|
this.applyingSharedSnapshotDepth = 0;
|
|
146
187
|
this.suppressStructuredSceneUpdateDepth = 0;
|
|
188
|
+
this.commentThreadStore = new CommentThreadStore({
|
|
189
|
+
canWrite: () => this.canWriteToRoom,
|
|
190
|
+
createAnchor: createDiagramCommentAnchor,
|
|
191
|
+
getDoc: () => this.ydoc,
|
|
192
|
+
getEditorState: () => null,
|
|
193
|
+
getLocalUser: () => this.getLocalUser(),
|
|
194
|
+
onCommentsChange: (threads) => this.onCommentThreadsChange(threads),
|
|
195
|
+
resolveThread: (thread) => thread,
|
|
196
|
+
});
|
|
147
197
|
|
|
148
198
|
this.handleAwarenessChange = () => {
|
|
149
199
|
this.onCollaboratorsChange(buildCollaboratorsMap(this.awareness));
|
|
@@ -177,6 +227,25 @@ export class ExcalidrawRoomClient {
|
|
|
177
227
|
return this.localUser;
|
|
178
228
|
}
|
|
179
229
|
|
|
230
|
+
getCommentThreads() {
|
|
231
|
+
return this.commentThreadStore.getCommentThreads();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
createCommentThread({ body, element } = {}) {
|
|
235
|
+
return this.commentThreadStore.createCommentThread({
|
|
236
|
+
anchor: element,
|
|
237
|
+
body,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
replyToCommentThread(threadId, body) {
|
|
242
|
+
return this.commentThreadStore.replyToCommentThread(threadId, body);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
deleteCommentThread(threadId) {
|
|
246
|
+
return this.commentThreadStore.deleteCommentThread(threadId);
|
|
247
|
+
}
|
|
248
|
+
|
|
180
249
|
getConnectionState() {
|
|
181
250
|
return this.connectionState;
|
|
182
251
|
}
|
|
@@ -268,7 +337,16 @@ export class ExcalidrawRoomClient {
|
|
|
268
337
|
return this.localUser;
|
|
269
338
|
}
|
|
270
339
|
|
|
271
|
-
async connect(
|
|
340
|
+
async connect(options = {}) {
|
|
341
|
+
try {
|
|
342
|
+
return await this.connectInternal(options);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
this.disconnect();
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async connectInternal({ initialUser = null } = {}) {
|
|
272
350
|
this.localUser = initialUser;
|
|
273
351
|
this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.CONNECTING);
|
|
274
352
|
|
|
@@ -277,6 +355,7 @@ export class ExcalidrawRoomClient {
|
|
|
277
355
|
this.lastSceneJson = JSON.stringify(scene);
|
|
278
356
|
this.hasAuthoritativeSync = true;
|
|
279
357
|
this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE);
|
|
358
|
+
this.onCommentThreadsChange([]);
|
|
280
359
|
return scene;
|
|
281
360
|
}
|
|
282
361
|
|
|
@@ -367,6 +446,11 @@ export class ExcalidrawRoomClient {
|
|
|
367
446
|
this.roomElements.observeDeep(this.handleStructuredSceneUpdate);
|
|
368
447
|
this.roomFiles.observeDeep(this.handleStructuredSceneUpdate);
|
|
369
448
|
this.roomAppState.observe(this.handleStructuredSceneUpdate);
|
|
449
|
+
this.structuredObserversAttached = true;
|
|
450
|
+
this.commentThreadStore.bind({
|
|
451
|
+
commentThreads: this.ydoc.getArray('comments'),
|
|
452
|
+
ydoc: this.ydoc,
|
|
453
|
+
});
|
|
370
454
|
if (usedApiFallback && this.canWriteToRoom) {
|
|
371
455
|
this.commitSceneJson(this.lastSceneJson, {
|
|
372
456
|
origin: 'excalidraw-api-fallback',
|
|
@@ -501,7 +585,7 @@ export class ExcalidrawRoomClient {
|
|
|
501
585
|
|
|
502
586
|
mergeLocallyChangedAppState(appState, baseSceneJson) {
|
|
503
587
|
const baseScene = parseSceneJson(baseSceneJson);
|
|
504
|
-
const roomScene = parseSceneJson(this.
|
|
588
|
+
const roomScene = parseSceneJson(this.lastSceneJson || this.getStructuredSceneJson());
|
|
505
589
|
const nextAppState = {
|
|
506
590
|
...roomScene.appState,
|
|
507
591
|
};
|
|
@@ -523,7 +607,7 @@ export class ExcalidrawRoomClient {
|
|
|
523
607
|
elements,
|
|
524
608
|
files,
|
|
525
609
|
} = {}) {
|
|
526
|
-
const roomSceneJson = this.
|
|
610
|
+
const roomSceneJson = this.lastSceneJson || this.getStructuredSceneJson() || baseSceneJson;
|
|
527
611
|
const roomScene = parseSceneJson(roomSceneJson);
|
|
528
612
|
const nextAppState = this.mergeLocallyChangedAppState(appState, baseSceneJson);
|
|
529
613
|
const roomElementsById = new Map(roomScene.elements.map((element) => [element.id, element]));
|
|
@@ -533,7 +617,7 @@ export class ExcalidrawRoomClient {
|
|
|
533
617
|
const nextFiles = files && typeof files === 'object' ? files : {};
|
|
534
618
|
const changedFiles = Object.fromEntries(
|
|
535
619
|
Object.entries(nextFiles).filter(([fileId, file]) => (
|
|
536
|
-
|
|
620
|
+
!areExcalidrawFilesEqual(file, roomScene.files?.[fileId])
|
|
537
621
|
)),
|
|
538
622
|
);
|
|
539
623
|
const appStateChanged = (
|
|
@@ -869,10 +953,15 @@ export class ExcalidrawRoomClient {
|
|
|
869
953
|
this.lastSelectedIdsSignature = '';
|
|
870
954
|
this.pendingEmptySceneCandidate = null;
|
|
871
955
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
956
|
+
if (this.structuredObserversAttached) {
|
|
957
|
+
this.roomMeta?.unobserve(this.handleStructuredSceneUpdate);
|
|
958
|
+
this.roomElements?.unobserveDeep(this.handleStructuredSceneUpdate);
|
|
959
|
+
this.roomFiles?.unobserveDeep(this.handleStructuredSceneUpdate);
|
|
960
|
+
this.roomAppState?.unobserve(this.handleStructuredSceneUpdate);
|
|
961
|
+
this.structuredObserversAttached = false;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
this.commentThreadStore.unbind();
|
|
876
965
|
|
|
877
966
|
if (this.awareness) {
|
|
878
967
|
this.awareness.off('change', this.handleAwarenessChange);
|
|
@@ -74,6 +74,11 @@ export class GitApiClient {
|
|
|
74
74
|
return parseApiResponse(response, 'Failed to load historical file preview');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
getFileAttachmentUrl({ hash, path } = {}) {
|
|
78
|
+
const params = createSearchParams({ hash, path });
|
|
79
|
+
return resolveApiUrl(`/git/file-attachment?${params.toString()}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
async stageFile({ path, requestId = null } = {}) {
|
|
78
83
|
return this.#postJson('/git/stage', { path }, { requestId, fallbackError: 'Failed to stage file' });
|
|
79
84
|
}
|
|
@@ -51,6 +51,20 @@ export class VaultApiClient {
|
|
|
51
51
|
return Array.isArray(data.backlinks) ? data.backlinks : [];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
async syncStructurizrWorkspace({ path, source }) {
|
|
55
|
+
const response = await fetch(resolveApiUrl('/structurizr/sync'), {
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
path,
|
|
58
|
+
source,
|
|
59
|
+
}),
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
},
|
|
63
|
+
method: 'POST',
|
|
64
|
+
});
|
|
65
|
+
return parseApiResponse(response, 'Failed to sync Structurizr workspace');
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
async renderSvg(source) {
|
|
55
69
|
const response = await fetch(resolveApiUrl('/plantuml/render'), {
|
|
56
70
|
body: JSON.stringify({ source }),
|
|
@@ -261,6 +275,18 @@ export class VaultApiClient {
|
|
|
261
275
|
return parseApiResponse(response, 'Failed to upload image');
|
|
262
276
|
}
|
|
263
277
|
|
|
278
|
+
async uploadFile({ file, path, requestId = null }) {
|
|
279
|
+
const response = await fetch(resolveApiUrl('/file/upload'), {
|
|
280
|
+
body: file,
|
|
281
|
+
headers: createRequestHeaders(requestId, {
|
|
282
|
+
'Content-Type': file?.type || 'application/octet-stream',
|
|
283
|
+
'X-CollabMD-File-Path': encodeHeaderMetadata(path),
|
|
284
|
+
}),
|
|
285
|
+
method: 'POST',
|
|
286
|
+
});
|
|
287
|
+
return parseApiResponse(response, 'Failed to upload file');
|
|
288
|
+
}
|
|
289
|
+
|
|
264
290
|
async downloadFile(path) {
|
|
265
291
|
const fallbackFileName = getVaultPathLeaf(path) || 'download';
|
|
266
292
|
return triggerDownload(resolveApiUrl(`/download/file?path=${encodeURIComponent(path)}`), {
|
|
@@ -51,6 +51,10 @@ export function areAnchorsEqual(left, right) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function formatAnchorLabel(anchor) {
|
|
54
|
+
if (getAnchorKind(anchor) === 'diagram-element') {
|
|
55
|
+
return 'Diagram element';
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
const startLine = Number(anchor?.startLine || 0);
|
|
55
59
|
const endLine = Number(anchor?.endLine || startLine);
|
|
56
60
|
if (!Number.isFinite(startLine) || startLine <= 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createDrawioLeaseRoomName } from '../../domain/drawio-room.js';
|
|
2
2
|
import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
|
|
3
3
|
import { resolveAppUrl } from '../domain/runtime-paths.js';
|
|
4
|
+
import { renderDrawioViewer } from './drawio-viewer.js';
|
|
4
5
|
import {
|
|
5
6
|
cancelIdleRender,
|
|
6
7
|
isNearViewport,
|
|
@@ -8,62 +9,6 @@ import {
|
|
|
8
9
|
} from '../browser-utils.js';
|
|
9
10
|
|
|
10
11
|
const HYDRATE_VIEWPORT_MARGIN_PX = 360;
|
|
11
|
-
const DRAWIO_VIEWER_SCRIPT_URL = 'https://viewer.diagrams.net/js/viewer-static.min.js';
|
|
12
|
-
|
|
13
|
-
let drawioViewerLoadPromise = null;
|
|
14
|
-
|
|
15
|
-
function ensureDrawioViewerLoaded() {
|
|
16
|
-
if (window.GraphViewer?.processElements) {
|
|
17
|
-
return Promise.resolve(window.GraphViewer);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (drawioViewerLoadPromise) {
|
|
21
|
-
return drawioViewerLoadPromise;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
drawioViewerLoadPromise = new Promise((resolve, reject) => {
|
|
25
|
-
const existingScript = document.querySelector('script[data-collabmd-drawio-viewer]');
|
|
26
|
-
const script = existingScript instanceof HTMLScriptElement
|
|
27
|
-
? existingScript
|
|
28
|
-
: document.createElement('script');
|
|
29
|
-
|
|
30
|
-
const cleanup = () => {
|
|
31
|
-
script.removeEventListener('error', handleError);
|
|
32
|
-
script.removeEventListener('load', handleLoad);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const handleError = () => {
|
|
36
|
-
cleanup();
|
|
37
|
-
drawioViewerLoadPromise = null;
|
|
38
|
-
reject(new Error('Failed to load draw.io viewer'));
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const handleLoad = () => {
|
|
42
|
-
cleanup();
|
|
43
|
-
if (!window.GraphViewer?.processElements) {
|
|
44
|
-
drawioViewerLoadPromise = null;
|
|
45
|
-
reject(new Error('draw.io viewer did not initialize'));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
resolve(window.GraphViewer);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
script.addEventListener('error', handleError, { once: true });
|
|
53
|
-
script.addEventListener('load', handleLoad, { once: true });
|
|
54
|
-
|
|
55
|
-
if (!existingScript) {
|
|
56
|
-
script.src = DRAWIO_VIEWER_SCRIPT_URL;
|
|
57
|
-
script.async = true;
|
|
58
|
-
script.dataset.collabmdDrawioViewer = 'true';
|
|
59
|
-
document.head.append(script);
|
|
60
|
-
} else if (window.GraphViewer?.processElements) {
|
|
61
|
-
handleLoad();
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return drawioViewerLoadPromise;
|
|
66
|
-
}
|
|
67
12
|
|
|
68
13
|
export class DrawioEmbedController {
|
|
69
14
|
constructor({
|
|
@@ -350,59 +295,21 @@ export class DrawioEmbedController {
|
|
|
350
295
|
}
|
|
351
296
|
|
|
352
297
|
async renderViewerEntry(entry) {
|
|
353
|
-
const
|
|
354
|
-
this.vaultApiClient.readFile(entry.filePath),
|
|
355
|
-
ensureDrawioViewerLoaded(),
|
|
356
|
-
]);
|
|
298
|
+
const { content } = await this.vaultApiClient.readFile(entry.filePath);
|
|
357
299
|
|
|
358
300
|
if (!entry.wrapper?.isConnected || !entry.viewerElement?.isConnected) {
|
|
359
301
|
return;
|
|
360
302
|
}
|
|
361
303
|
|
|
362
304
|
const theme = this.getTheme?.() === 'light' ? 'light' : 'dark';
|
|
363
|
-
const graphElement = document.createElement('div');
|
|
364
|
-
graphElement.className = 'mxgraph drawio-viewer-frame';
|
|
365
|
-
graphElement.dataset.action = 'open-file';
|
|
366
|
-
graphElement.dataset.filePath = entry.filePath;
|
|
367
|
-
graphElement.setAttribute('role', 'button');
|
|
368
|
-
graphElement.setAttribute('tabindex', '0');
|
|
369
|
-
graphElement.setAttribute('aria-label', `Open ${entry.label.replace(/\.drawio$/i, '')}`);
|
|
370
|
-
graphElement.addEventListener('click', (event) => {
|
|
371
|
-
event.preventDefault();
|
|
372
|
-
this.onOpenFile?.(entry.filePath);
|
|
373
|
-
});
|
|
374
|
-
graphElement.addEventListener('keydown', (event) => {
|
|
375
|
-
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
event.preventDefault();
|
|
380
|
-
this.onOpenFile?.(entry.filePath);
|
|
381
|
-
});
|
|
382
|
-
graphElement.dataset.mxgraph = JSON.stringify({
|
|
383
|
-
'check-visible-state': false,
|
|
384
|
-
center: true,
|
|
385
|
-
border: 0,
|
|
386
|
-
'dark-mode': theme,
|
|
387
|
-
editable: false,
|
|
388
|
-
fit: 1,
|
|
389
|
-
lightbox: false,
|
|
390
|
-
nav: false,
|
|
391
|
-
resize: false,
|
|
392
|
-
tooltips: false,
|
|
393
|
-
xml: String(content ?? ''),
|
|
394
|
-
});
|
|
395
|
-
|
|
396
305
|
const viewerHost = entry.wrapper.querySelector('.drawio-viewer-shell') ?? entry.viewerElement;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
viewer.processElements();
|
|
306
|
+
entry.viewerElement = await renderDrawioViewer(viewerHost, {
|
|
307
|
+
ariaLabel: `Open ${entry.label.replace(/\.drawio$/i, '')}`,
|
|
308
|
+
className: 'mxgraph drawio-viewer-frame',
|
|
309
|
+
onActivate: () => this.onOpenFile?.(entry.filePath),
|
|
310
|
+
source: String(content ?? ''),
|
|
311
|
+
theme,
|
|
312
|
+
});
|
|
406
313
|
}
|
|
407
314
|
|
|
408
315
|
buildIframeUrl(entry) {
|