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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const DRAWIO_VIEWER_SCRIPT_URL = 'https://viewer.diagrams.net/js/viewer-static.min.js';
|
|
2
|
+
|
|
3
|
+
let drawioViewerLoadPromise = null;
|
|
4
|
+
|
|
5
|
+
export function ensureDrawioViewerLoaded() {
|
|
6
|
+
if (window.GraphViewer?.processElements) {
|
|
7
|
+
return Promise.resolve(window.GraphViewer);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (drawioViewerLoadPromise) {
|
|
11
|
+
return drawioViewerLoadPromise;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
drawioViewerLoadPromise = new Promise((resolve, reject) => {
|
|
15
|
+
const existingScript = document.querySelector('script[data-collabmd-drawio-viewer]');
|
|
16
|
+
const script = existingScript instanceof HTMLScriptElement
|
|
17
|
+
? existingScript
|
|
18
|
+
: document.createElement('script');
|
|
19
|
+
|
|
20
|
+
const cleanup = () => {
|
|
21
|
+
script.removeEventListener('error', handleError);
|
|
22
|
+
script.removeEventListener('load', handleLoad);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const handleError = () => {
|
|
26
|
+
cleanup();
|
|
27
|
+
drawioViewerLoadPromise = null;
|
|
28
|
+
reject(new Error('Failed to load draw.io viewer'));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleLoad = () => {
|
|
32
|
+
cleanup();
|
|
33
|
+
if (!window.GraphViewer?.processElements) {
|
|
34
|
+
drawioViewerLoadPromise = null;
|
|
35
|
+
reject(new Error('draw.io viewer did not initialize'));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
resolve(window.GraphViewer);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
script.addEventListener('error', handleError, { once: true });
|
|
43
|
+
script.addEventListener('load', handleLoad, { once: true });
|
|
44
|
+
|
|
45
|
+
if (!existingScript) {
|
|
46
|
+
script.src = DRAWIO_VIEWER_SCRIPT_URL;
|
|
47
|
+
script.async = true;
|
|
48
|
+
script.dataset.collabmdDrawioViewer = 'true';
|
|
49
|
+
document.head.append(script);
|
|
50
|
+
} else if (window.GraphViewer?.processElements) {
|
|
51
|
+
handleLoad();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return drawioViewerLoadPromise;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createDrawioViewerElement({
|
|
59
|
+
ariaLabel = '',
|
|
60
|
+
className = 'drawio-viewer-frame',
|
|
61
|
+
onActivate = null,
|
|
62
|
+
source = '',
|
|
63
|
+
theme = 'dark',
|
|
64
|
+
} = {}) {
|
|
65
|
+
const graphElement = document.createElement('div');
|
|
66
|
+
graphElement.className = className;
|
|
67
|
+
if (ariaLabel) {
|
|
68
|
+
graphElement.setAttribute('aria-label', ariaLabel);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof onActivate === 'function') {
|
|
72
|
+
graphElement.dataset.action = 'open-file';
|
|
73
|
+
graphElement.setAttribute('role', 'button');
|
|
74
|
+
graphElement.setAttribute('tabindex', '0');
|
|
75
|
+
graphElement.addEventListener('click', (event) => {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
onActivate();
|
|
78
|
+
});
|
|
79
|
+
graphElement.addEventListener('keydown', (event) => {
|
|
80
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
onActivate();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
graphElement.dataset.mxgraph = JSON.stringify({
|
|
90
|
+
'check-visible-state': false,
|
|
91
|
+
center: true,
|
|
92
|
+
border: 0,
|
|
93
|
+
'dark-mode': theme === 'light' ? 'light' : 'dark',
|
|
94
|
+
editable: false,
|
|
95
|
+
fit: 1,
|
|
96
|
+
lightbox: false,
|
|
97
|
+
nav: false,
|
|
98
|
+
resize: false,
|
|
99
|
+
tooltips: false,
|
|
100
|
+
xml: String(source ?? ''),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return graphElement;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function renderDrawioViewer(host, options = {}) {
|
|
107
|
+
const viewer = await ensureDrawioViewerLoaded();
|
|
108
|
+
const graphElement = createDrawioViewerElement(options);
|
|
109
|
+
host.replaceChildren(graphElement);
|
|
110
|
+
|
|
111
|
+
if (typeof viewer.createViewerForElement === 'function') {
|
|
112
|
+
viewer.createViewerForElement(graphElement);
|
|
113
|
+
} else {
|
|
114
|
+
viewer.processElements();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return graphElement;
|
|
118
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
|
|
2
2
|
import { reconcileEmbedEntries } from './excalidraw-embed-reconciler.js';
|
|
3
|
-
import { resolveAppUrl } from '../domain/runtime-paths.js';
|
|
3
|
+
import { resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
|
|
4
4
|
import {
|
|
5
5
|
cancelIdleRender,
|
|
6
6
|
isNearViewport,
|
|
@@ -15,6 +15,13 @@ const MAX_IFRAME_BOOT_ATTEMPTS = 3;
|
|
|
15
15
|
const MIN_HEIGHT = 200;
|
|
16
16
|
const EDITOR_IFRAME_PATH = '/excalidraw-editor.html';
|
|
17
17
|
|
|
18
|
+
function findEmbedPlaceholder(previewElement, key) {
|
|
19
|
+
const placeholders = previewElement?.querySelectorAll?.(
|
|
20
|
+
'.excalidraw-embed-placeholder[data-embed-key]',
|
|
21
|
+
);
|
|
22
|
+
return Array.from(placeholders || []).find((placeholder) => placeholder.dataset?.embedKey === key) || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export class ExcalidrawEmbedController {
|
|
19
26
|
constructor({
|
|
20
27
|
getTheme,
|
|
@@ -70,12 +77,16 @@ export class ExcalidrawEmbedController {
|
|
|
70
77
|
|
|
71
78
|
this.embedEntries.forEach((entry) => {
|
|
72
79
|
this._clearEntryBootTimeout(entry);
|
|
80
|
+
entry.resizerCleanup?.();
|
|
81
|
+
entry.resizerCleanup = null;
|
|
73
82
|
entry.wrapper?.remove();
|
|
74
83
|
entry.placeholder = null;
|
|
75
84
|
});
|
|
76
85
|
this.embedEntries.clear();
|
|
77
86
|
if (this.warmEntry) {
|
|
78
87
|
this._clearEntryBootTimeout(this.warmEntry);
|
|
88
|
+
this.warmEntry.resizerCleanup?.();
|
|
89
|
+
this.warmEntry.resizerCleanup = null;
|
|
79
90
|
this.warmEntry.wrapper?.remove();
|
|
80
91
|
this.warmEntry = null;
|
|
81
92
|
}
|
|
@@ -219,6 +230,25 @@ export class ExcalidrawEmbedController {
|
|
|
219
230
|
});
|
|
220
231
|
}
|
|
221
232
|
|
|
233
|
+
openCommentThread(filePath, threadId) {
|
|
234
|
+
if (!filePath || !threadId) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const entry = this._findEntryByFilePath(filePath);
|
|
239
|
+
if (!entry) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
entry.pendingCommentThreadId = threadId;
|
|
244
|
+
if (entry.isReady) {
|
|
245
|
+
this._postPendingCommentThread(entry);
|
|
246
|
+
} else if (!entry.wrapper) {
|
|
247
|
+
this._enqueueHydration(entry, { prioritize: true });
|
|
248
|
+
}
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
|
|
222
252
|
prepareFileDisconnect(filePath, { timeoutMs = 10000 } = {}) {
|
|
223
253
|
const entry = this._findEntryByFilePath(filePath);
|
|
224
254
|
if (!entry?.iframe?.contentWindow) {
|
|
@@ -394,6 +424,7 @@ export class ExcalidrawEmbedController {
|
|
|
394
424
|
entry.maxButton = mount.maxButton;
|
|
395
425
|
entry.openButton = mount.openButton;
|
|
396
426
|
entry.instanceId = mount.instanceId;
|
|
427
|
+
entry.resizerCleanup = mount.resizerCleanup;
|
|
397
428
|
entry.isReady = false;
|
|
398
429
|
entry.bootFilePath = entry.filePath;
|
|
399
430
|
entry.bootMode = this._getEntryMode(entry);
|
|
@@ -414,7 +445,7 @@ export class ExcalidrawEmbedController {
|
|
|
414
445
|
_attachWrapper(entry) {
|
|
415
446
|
const placeholder = entry.placeholder?.isConnected
|
|
416
447
|
? entry.placeholder
|
|
417
|
-
: this.previewElement
|
|
448
|
+
: findEmbedPlaceholder(this.previewElement, entry.key);
|
|
418
449
|
|
|
419
450
|
if (!placeholder) {
|
|
420
451
|
return;
|
|
@@ -476,6 +507,8 @@ export class ExcalidrawEmbedController {
|
|
|
476
507
|
}
|
|
477
508
|
|
|
478
509
|
this._clearEntryBootTimeout(entry);
|
|
510
|
+
entry.resizerCleanup?.();
|
|
511
|
+
entry.resizerCleanup = null;
|
|
479
512
|
if (this.warmEntry === entry) {
|
|
480
513
|
this.warmEntry = null;
|
|
481
514
|
}
|
|
@@ -634,7 +667,7 @@ export class ExcalidrawEmbedController {
|
|
|
634
667
|
}
|
|
635
668
|
|
|
636
669
|
const hostSearchParams = new URLSearchParams(window.location.search);
|
|
637
|
-
const serverOverride =
|
|
670
|
+
const serverOverride = resolveWsServerOverride();
|
|
638
671
|
if (serverOverride) {
|
|
639
672
|
iframeUrl.searchParams.set('server', serverOverride);
|
|
640
673
|
}
|
|
@@ -702,7 +735,7 @@ export class ExcalidrawEmbedController {
|
|
|
702
735
|
const resizer = document.createElement('div');
|
|
703
736
|
resizer.className = 'excalidraw-embed-resizer';
|
|
704
737
|
resizer.title = 'Drag to resize';
|
|
705
|
-
this._setupResizer(resizer, iframe, () => {
|
|
738
|
+
const resizerCleanup = this._setupResizer(resizer, iframe, () => {
|
|
706
739
|
this._syncEntryLayout(entry);
|
|
707
740
|
this._requestPreviewViewportFit(entry);
|
|
708
741
|
});
|
|
@@ -769,6 +802,7 @@ export class ExcalidrawEmbedController {
|
|
|
769
802
|
labelElement: label,
|
|
770
803
|
maxButton: maxBtn,
|
|
771
804
|
openButton: openBtn,
|
|
805
|
+
resizerCleanup,
|
|
772
806
|
wrapper,
|
|
773
807
|
};
|
|
774
808
|
}
|
|
@@ -793,6 +827,7 @@ export class ExcalidrawEmbedController {
|
|
|
793
827
|
_setupResizer(resizer, iframe, onResizeEnd = null) {
|
|
794
828
|
let startY = 0;
|
|
795
829
|
let startHeight = 0;
|
|
830
|
+
let isDragging = false;
|
|
796
831
|
|
|
797
832
|
const onPointerMove = (event) => {
|
|
798
833
|
const delta = event.clientY - startY;
|
|
@@ -801,20 +836,37 @@ export class ExcalidrawEmbedController {
|
|
|
801
836
|
iframe.style.pointerEvents = 'none';
|
|
802
837
|
};
|
|
803
838
|
|
|
804
|
-
const
|
|
839
|
+
const stopDragging = () => {
|
|
840
|
+
if (!isDragging) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
isDragging = false;
|
|
805
845
|
iframe.style.pointerEvents = '';
|
|
806
846
|
document.removeEventListener('pointermove', onPointerMove);
|
|
807
847
|
document.removeEventListener('pointerup', onPointerUp);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const onPointerUp = () => {
|
|
851
|
+
stopDragging();
|
|
808
852
|
onResizeEnd?.();
|
|
809
853
|
};
|
|
810
854
|
|
|
811
|
-
|
|
855
|
+
const onPointerDown = (event) => {
|
|
812
856
|
event.preventDefault();
|
|
857
|
+
isDragging = true;
|
|
813
858
|
startY = event.clientY;
|
|
814
859
|
startHeight = iframe.offsetHeight;
|
|
815
860
|
document.addEventListener('pointermove', onPointerMove);
|
|
816
861
|
document.addEventListener('pointerup', onPointerUp);
|
|
817
|
-
}
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
resizer.addEventListener('pointerdown', onPointerDown);
|
|
865
|
+
|
|
866
|
+
return () => {
|
|
867
|
+
resizer.removeEventListener?.('pointerdown', onPointerDown);
|
|
868
|
+
stopDragging();
|
|
869
|
+
};
|
|
818
870
|
}
|
|
819
871
|
|
|
820
872
|
_ensureOverlayRoot() {
|
|
@@ -1064,7 +1116,7 @@ export class ExcalidrawEmbedController {
|
|
|
1064
1116
|
|
|
1065
1117
|
const placeholder = entry.placeholder?.isConnected
|
|
1066
1118
|
? entry.placeholder
|
|
1067
|
-
: this.previewElement
|
|
1119
|
+
: findEmbedPlaceholder(this.previewElement, entry.key);
|
|
1068
1120
|
|
|
1069
1121
|
if (!placeholder) {
|
|
1070
1122
|
return;
|
|
@@ -1163,6 +1215,7 @@ export class ExcalidrawEmbedController {
|
|
|
1163
1215
|
theme: this.getTheme?.() || 'dark',
|
|
1164
1216
|
});
|
|
1165
1217
|
this._syncEntryFollowState(entry);
|
|
1218
|
+
this._postPendingCommentThread?.(entry);
|
|
1166
1219
|
if (this._entryNeedsHardReload(entry)) {
|
|
1167
1220
|
void this._hydrateEntry(entry);
|
|
1168
1221
|
}
|
|
@@ -1227,6 +1280,19 @@ export class ExcalidrawEmbedController {
|
|
|
1227
1280
|
entry?.iframe?.contentWindow?.postMessage(payload, window.location.origin);
|
|
1228
1281
|
}
|
|
1229
1282
|
|
|
1283
|
+
_postPendingCommentThread(entry) {
|
|
1284
|
+
if (!entry?.isReady || !entry.pendingCommentThreadId) {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
this._postMessageToEntry(entry, {
|
|
1289
|
+
source: 'collabmd-host',
|
|
1290
|
+
threadId: entry.pendingCommentThreadId,
|
|
1291
|
+
type: 'open-comment-thread',
|
|
1292
|
+
});
|
|
1293
|
+
entry.pendingCommentThreadId = null;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1230
1296
|
_requestPreviewViewportFit(entry) {
|
|
1231
1297
|
if (this._isFilePreviewEntry(entry)) {
|
|
1232
1298
|
return;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getVaultFileExtension,
|
|
3
|
+
isVaultFilePath,
|
|
4
|
+
VAULT_FILE_EXTENSIONS,
|
|
3
5
|
} from '../../domain/file-kind.js';
|
|
6
|
+
import { pickFiles } from '../browser-utils.js';
|
|
4
7
|
import {
|
|
5
8
|
createBaseStarter,
|
|
6
9
|
composeVaultChildPath,
|
|
@@ -8,6 +11,7 @@ import {
|
|
|
8
11
|
createMarkdownStarter,
|
|
9
12
|
createMermaidStarter,
|
|
10
13
|
createPlantUmlStarter,
|
|
14
|
+
createStructurizrStarter,
|
|
11
15
|
ensureVaultExtension,
|
|
12
16
|
getVaultPathLeaf,
|
|
13
17
|
getVaultPathParent,
|
|
@@ -28,6 +32,8 @@ function formatCount(value, singularLabel, pluralLabel) {
|
|
|
28
32
|
return `${value} ${value === 1 ? singularLabel : pluralLabel}`;
|
|
29
33
|
}
|
|
30
34
|
|
|
35
|
+
const VAULT_FILE_PICKER_ACCEPT = VAULT_FILE_EXTENSIONS.join(',');
|
|
36
|
+
|
|
31
37
|
function createEmptyExcalidrawScene() {
|
|
32
38
|
return JSON.stringify({
|
|
33
39
|
type: 'excalidraw',
|
|
@@ -116,6 +122,18 @@ export class FileActionController {
|
|
|
116
122
|
|
|
117
123
|
getCreateActions({ parentDir = '' } = {}) {
|
|
118
124
|
return [
|
|
125
|
+
{
|
|
126
|
+
contextLabel: 'Upload files',
|
|
127
|
+
group: 'File',
|
|
128
|
+
hint: 'Supported vault files',
|
|
129
|
+
icon: this.getCreateActionIcon('upload'),
|
|
130
|
+
id: 'upload',
|
|
131
|
+
label: 'Upload files',
|
|
132
|
+
meta: 'Multiple',
|
|
133
|
+
onSelect: () => {
|
|
134
|
+
void this.handleUploadFiles({ parentDir });
|
|
135
|
+
},
|
|
136
|
+
},
|
|
119
137
|
{
|
|
120
138
|
contextLabel: 'New markdown file',
|
|
121
139
|
group: 'Note',
|
|
@@ -176,6 +194,16 @@ export class FileActionController {
|
|
|
176
194
|
meta: '.puml',
|
|
177
195
|
onSelect: () => this.handleNewPlantUml({ parentDir }),
|
|
178
196
|
},
|
|
197
|
+
{
|
|
198
|
+
contextLabel: 'New Structurizr workspace',
|
|
199
|
+
group: 'Diagram',
|
|
200
|
+
hint: 'Structurizr DSL',
|
|
201
|
+
icon: this.getCreateActionIcon('structurizr'),
|
|
202
|
+
id: 'structurizr',
|
|
203
|
+
label: 'Structurizr workspace',
|
|
204
|
+
meta: '.dsl',
|
|
205
|
+
onSelect: () => this.handleNewStructurizr({ parentDir }),
|
|
206
|
+
},
|
|
179
207
|
{
|
|
180
208
|
contextLabel: 'New folder',
|
|
181
209
|
group: 'Structure',
|
|
@@ -241,10 +269,13 @@ export class FileActionController {
|
|
|
241
269
|
|
|
242
270
|
getCreateActionIcon(kind) {
|
|
243
271
|
switch (kind) {
|
|
272
|
+
case 'upload':
|
|
273
|
+
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 20h14"/></svg>';
|
|
244
274
|
case 'base':
|
|
245
275
|
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 4h7l5 5v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M14 4v5h5"/><path d="M8 13h8"/><path d="M8 17h6"/><path d="M8 9h3"/></svg>';
|
|
246
276
|
case 'drawio':
|
|
247
277
|
case 'plantuml':
|
|
278
|
+
case 'structurizr':
|
|
248
279
|
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="7" height="6" rx="1"/><rect x="14" y="4" width="7" height="6" rx="1"/><rect x="8.5" y="14" width="7" height="6" rx="1"/><path d="M10 7h4"/><path d="M17.5 10v2.5"/><path d="M6.5 10v2.5"/><path d="M6.5 12.5h11"/></svg>';
|
|
249
280
|
case 'excalidraw':
|
|
250
281
|
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>';
|
|
@@ -476,6 +507,47 @@ export class FileActionController {
|
|
|
476
507
|
}
|
|
477
508
|
}
|
|
478
509
|
|
|
510
|
+
async uploadVaultFiles(files = [], { parentDir = '' } = {}) {
|
|
511
|
+
const normalizedParentDir = normalizeVaultPathInput(parentDir);
|
|
512
|
+
let uploaded = false;
|
|
513
|
+
|
|
514
|
+
for (const file of Array.from(files ?? [])) {
|
|
515
|
+
const fileName = getVaultPathLeaf(file?.name);
|
|
516
|
+
const filePath = composeVaultChildPath(normalizedParentDir, fileName);
|
|
517
|
+
if (!fileName || !isVaultFilePath(filePath)) {
|
|
518
|
+
this.showToast(`Unsupported file type: ${fileName || 'unnamed file'}`);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
await this.runWorkspaceMutation((requestId) => this.vaultClient.uploadFile({
|
|
524
|
+
file,
|
|
525
|
+
path: filePath,
|
|
526
|
+
requestId,
|
|
527
|
+
}));
|
|
528
|
+
uploaded = true;
|
|
529
|
+
this.state.expandDirectoryPath(filePath, { includeLeaf: false });
|
|
530
|
+
} catch (error) {
|
|
531
|
+
this.showToast(`${fileName}: ${error?.message || 'Failed to upload file'}`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (uploaded) {
|
|
536
|
+
await this.refresh();
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return uploaded;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async handleUploadFiles({ parentDir = '' } = {}) {
|
|
543
|
+
const files = await pickFiles({ accept: VAULT_FILE_PICKER_ACCEPT, multiple: true });
|
|
544
|
+
if (files.length === 0) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return this.uploadVaultFiles(files, { parentDir });
|
|
549
|
+
}
|
|
550
|
+
|
|
479
551
|
async createVaultFile(filePath, content, { openAfterCreate = false, errorMessage = 'Failed to create file' } = {}) {
|
|
480
552
|
try {
|
|
481
553
|
await this.runWorkspaceMutation((requestId) => this.vaultClient.createFile(withRequestId({
|
|
@@ -953,6 +1025,36 @@ export class FileActionController {
|
|
|
953
1025
|
});
|
|
954
1026
|
}
|
|
955
1027
|
|
|
1028
|
+
handleNewStructurizr({ parentDir = '' } = {}) {
|
|
1029
|
+
const context = this.getCreateContext(parentDir);
|
|
1030
|
+
|
|
1031
|
+
this.openActionDialog({
|
|
1032
|
+
title: 'Create Structurizr workspace',
|
|
1033
|
+
copy: context.normalizedParentDir
|
|
1034
|
+
? 'Create a collaborative C4 workspace inside the selected folder.'
|
|
1035
|
+
: 'Create a collaborative C4 workspace in the vault.',
|
|
1036
|
+
label: `Workspace ${context.inputLabelSuffix}`,
|
|
1037
|
+
hint: `${context.hintPrefix} ".dsl" is added automatically. workspace.dsl is conventional; any workspace .dsl file can render.`,
|
|
1038
|
+
note: context.note,
|
|
1039
|
+
placeholder: 'workspace',
|
|
1040
|
+
submitLabel: 'Create workspace',
|
|
1041
|
+
emptyMessage: 'Workspace path is required',
|
|
1042
|
+
onSubmit: (value) => {
|
|
1043
|
+
const normalizedPath = normalizeVaultPathInput(value);
|
|
1044
|
+
if (!normalizedPath) {
|
|
1045
|
+
this.showToast('Workspace path is required');
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const starter = createStructurizrStarter(composeVaultChildPath(context.normalizedParentDir, normalizedPath));
|
|
1050
|
+
return this.createVaultFile(starter.path, starter.content, {
|
|
1051
|
+
errorMessage: 'Failed to create Structurizr workspace',
|
|
1052
|
+
openAfterCreate: true,
|
|
1053
|
+
});
|
|
1054
|
+
},
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
|
|
956
1058
|
handleRenameFile(filePath) {
|
|
957
1059
|
const extension = getVaultFileExtension(filePath) || '.md';
|
|
958
1060
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import { escapeHtml } from '../domain/vault-utils.js';
|
|
6
6
|
import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
|
|
7
7
|
import { buttonClassNames } from './components/ui/button.js';
|
|
8
|
+
import { getVaultFileIconSvg } from './file-icon-svg.js';
|
|
8
9
|
|
|
9
10
|
function findNodeByPath(nodes = [], pathValue = '') {
|
|
10
11
|
for (const node of nodes) {
|
|
@@ -460,8 +461,10 @@ export class FileExplorerView {
|
|
|
460
461
|
const isExcalidraw = fileType === 'excalidraw';
|
|
461
462
|
const isBase = fileType === 'base';
|
|
462
463
|
const isImage = fileType === 'image';
|
|
464
|
+
const isPdf = fileType === 'pdf';
|
|
463
465
|
const isMermaid = fileType === 'mermaid';
|
|
464
466
|
const isPlantUml = fileType === 'plantuml';
|
|
467
|
+
const isStructurizr = fileType === 'structurizr';
|
|
465
468
|
|
|
466
469
|
if (isBase) {
|
|
467
470
|
button.classList.add('is-base');
|
|
@@ -475,12 +478,18 @@ export class FileExplorerView {
|
|
|
475
478
|
if (isImage) {
|
|
476
479
|
button.classList.add('is-image');
|
|
477
480
|
}
|
|
481
|
+
if (isPdf) {
|
|
482
|
+
button.classList.add('is-pdf');
|
|
483
|
+
}
|
|
478
484
|
if (isMermaid) {
|
|
479
485
|
button.classList.add('is-mermaid');
|
|
480
486
|
}
|
|
481
487
|
if (isPlantUml) {
|
|
482
488
|
button.classList.add('is-plantuml');
|
|
483
489
|
}
|
|
490
|
+
if (isStructurizr) {
|
|
491
|
+
button.classList.add('is-structurizr');
|
|
492
|
+
}
|
|
484
493
|
if (filePath === activeFilePath) {
|
|
485
494
|
button.classList.add('active');
|
|
486
495
|
}
|
|
@@ -497,7 +506,7 @@ export class FileExplorerView {
|
|
|
497
506
|
}
|
|
498
507
|
const displayName = this.showFileExtensions ? String(name ?? '') : stripVaultFileExtension(name);
|
|
499
508
|
button.innerHTML = `
|
|
500
|
-
${
|
|
509
|
+
${getVaultFileIconSvg(filePath)}
|
|
501
510
|
<span class="file-tree-name">${escapeHtml(displayName)}</span>
|
|
502
511
|
${threadCount > 0 ? `<span class="file-tree-comment-count" aria-label="${threadCount} open comment thread${threadCount === 1 ? '' : 's'}">${threadCount}</span>` : ''}
|
|
503
512
|
`;
|
|
@@ -952,34 +961,6 @@ export class FileExplorerView {
|
|
|
952
961
|
return true;
|
|
953
962
|
}
|
|
954
963
|
|
|
955
|
-
getFileIconSvg({ isBase, isDrawio, isExcalidraw, isImage, isMermaid, isPlantUml }) {
|
|
956
|
-
if (isBase) {
|
|
957
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 4h7l5 5v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M14 4v5h5"/><path d="M8 13h8"/><path d="M8 17h6"/><path d="M8 9h3"/></svg>';
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
if (isDrawio) {
|
|
961
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="7" height="6" rx="1"/><rect x="14" y="4" width="7" height="6" rx="1"/><rect x="8.5" y="14" width="7" height="6" rx="1"/><path d="M10 7h4"/><path d="M17.5 10v2.5"/><path d="M6.5 10v2.5"/><path d="M6.5 12.5h11"/></svg>';
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
if (isExcalidraw) {
|
|
965
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>';
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
if (isImage) {
|
|
969
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.5"/><path d="m21 16-5-5L7 20"/><path d="m14 14 2 2"/></svg>';
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
if (isMermaid) {
|
|
973
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 7.5c0-1.38 1.12-2.5 2.5-2.5 1.04 0 1.93.64 2.3 1.56A2.5 2.5 0 0 1 14 8.5v1"/><path d="M19 16.5c0 1.38-1.12 2.5-2.5 2.5-1.04 0-1.93-.64-2.3-1.56A2.5 2.5 0 0 1 10 15.5v-1"/><path d="M8 10.5h8"/><path d="M8 13.5h8"/><path d="M10 8.5v7"/><path d="M14 8.5v7"/></svg>';
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
if (isPlantUml) {
|
|
977
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="7" height="6" rx="1"/><rect x="14" y="4" width="7" height="6" rx="1"/><rect x="8.5" y="14" width="7" height="6" rx="1"/><path d="M10 7h4"/><path d="M17.5 10v2.5"/><path d="M6.5 10v2.5"/><path d="M6.5 12.5h11"/></svg>';
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
return '<svg class="file-tree-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
981
|
-
}
|
|
982
|
-
|
|
983
964
|
showContextMenu(event, items) {
|
|
984
965
|
this.removeContextMenu();
|
|
985
966
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getVaultFileKind } from '../../domain/file-kind.js';
|
|
2
|
+
|
|
3
|
+
const SVG_ATTRIBUTES = 'width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
|
|
4
|
+
|
|
5
|
+
function createIcon(className, content) {
|
|
6
|
+
return `<svg class="${className}" ${SVG_ATTRIBUTES}>${content}</svg>`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getVaultFileIconSvg(filePath, {
|
|
10
|
+
className = 'file-tree-icon',
|
|
11
|
+
} = {}) {
|
|
12
|
+
const kind = getVaultFileKind(filePath) ?? 'file';
|
|
13
|
+
const iconClass = className;
|
|
14
|
+
|
|
15
|
+
if (kind === 'base') {
|
|
16
|
+
return createIcon(iconClass, '<path d="M7 4h7l5 5v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/><path d="M14 4v5h5"/><path d="M8 13h8"/><path d="M8 17h6"/><path d="M8 9h3"/>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (kind === 'drawio' || kind === 'plantuml' || kind === 'structurizr') {
|
|
20
|
+
return createIcon(iconClass, '<rect x="3" y="4" width="7" height="6" rx="1"/><rect x="14" y="4" width="7" height="6" rx="1"/><rect x="8.5" y="14" width="7" height="6" rx="1"/><path d="M10 7h4"/><path d="M17.5 10v2.5"/><path d="M6.5 10v2.5"/><path d="M6.5 12.5h11"/>');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (kind === 'excalidraw') {
|
|
24
|
+
return createIcon(iconClass, '<path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/>');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (kind === 'image') {
|
|
28
|
+
return createIcon(iconClass, '<rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.5"/><path d="m21 16-5-5L7 20"/><path d="m14 14 2 2"/>');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (kind === 'pdf') {
|
|
32
|
+
return createIcon(iconClass, '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><path d="M8 15h8"/><path d="M8 18h5"/>');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (kind === 'mermaid') {
|
|
36
|
+
return createIcon(iconClass, '<path d="M5 7.5c0-1.38 1.12-2.5 2.5-2.5 1.04 0 1.93.64 2.3 1.56A2.5 2.5 0 0 1 14 8.5v1"/><path d="M19 16.5c0 1.38-1.12 2.5-2.5 2.5-1.04 0-1.93-.64-2.3-1.56A2.5 2.5 0 0 1 10 15.5v-1"/><path d="M8 10.5h8"/><path d="M8 13.5h8"/><path d="M10 8.5v7"/><path d="M14 8.5v7"/>');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return createIcon(iconClass, '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>');
|
|
40
|
+
}
|
|
@@ -20,6 +20,8 @@ function flattenTree(nodes, files = [], searchEntries = [], fileEntries = []) {
|
|
|
20
20
|
|| node.type === 'drawio'
|
|
21
21
|
|| node.type === 'mermaid'
|
|
22
22
|
|| node.type === 'plantuml'
|
|
23
|
+
|| node.type === 'structurizr'
|
|
24
|
+
|| node.type === 'pdf'
|
|
23
25
|
|| node.type === 'image'
|
|
24
26
|
) {
|
|
25
27
|
files.push(node.path);
|