collabmd 0.1.29 → 0.1.30
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 +5 -0
- package/dist/client/assets/{CodeMirrorEditor-UCMRAMCV-9k1Shd4H.js → CodeMirrorEditor-UCMRAMCV-tj0_ycIx.js} +1 -1
- package/dist/client/assets/architectureDiagram-2XIMDMQ5-BPG5b65m.js +36 -0
- package/dist/client/assets/base-CaIEJUF9.css +1 -0
- package/dist/client/assets/{blockDiagram-WCTKOSBZ-0rR9ivwb.js → blockDiagram-WCTKOSBZ-F38TRzI3.js} +1 -1
- package/dist/client/assets/{chunk-ETSVUAFD-CN-gPl_j.js → chunk-AXZTUZBK-Cxfm7hmm.js} +1 -1
- package/dist/client/assets/{chunk-GLR3WWYH-ZnuaBvXp.js → chunk-GLR3WWYH-CQ1LryrI.js} +2 -2
- package/dist/client/assets/{chunk-NQ4KR5QH-BCjGNd0g.js → chunk-NQ4KR5QH-BBX89uUa.js} +1 -1
- package/dist/client/assets/chunk-NU2UP6EN-CGeKI3BV.js +35 -0
- package/dist/client/assets/{chunk-WL4C6EOR-OuH0lN6M.js → chunk-WL4C6EOR-zSV-8h7F.js} +1 -1
- package/dist/client/assets/classDiagram-VBA2DB6C-BQqG4q1b.js +1 -0
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-BUKbprX1.js +1 -0
- package/dist/client/assets/dagre-KLK3FWXG-CAG8WeES.js +4 -0
- package/dist/client/assets/diagram-E7M64L7V-Bm5FD0I5.js +24 -0
- package/dist/client/assets/diagram-IFDJBPK2-pqicH-av.js +43 -0
- package/dist/client/assets/diagram-P4PSJMXO-DwIXO27H.js +24 -0
- package/dist/client/assets/dist-3bQB_we3.js +1 -0
- package/dist/client/assets/dist-3uAMa5w5.js +1 -0
- package/dist/client/assets/{dist-D9c0A4Uh.js → dist-B-02vKOA.js} +1 -1
- package/dist/client/assets/{dist-A0eSMIyy.js → dist-BLAlZer_.js} +1 -1
- package/dist/client/assets/dist-BVB8rn7o.js +1 -0
- package/dist/client/assets/{dist-DveOCUUJ.js → dist-Bc2ND6i3.js} +1 -1
- package/dist/client/assets/{dist-BNcU46Ik.js → dist-BqQjnEWr.js} +1 -1
- package/dist/client/assets/{dist-Bay3vk5z.js → dist-BtnXjRx0.js} +1 -1
- package/dist/client/assets/{dist-CEYpWxod.js → dist-BzbYwa8m.js} +1 -1
- package/dist/client/assets/{dist-Ct1bmQnL.js → dist-BzpfHRaE.js} +1 -1
- package/dist/client/assets/{dist-BbKpzsTB.js → dist-CGV1S_as.js} +1 -1
- package/dist/client/assets/dist-CL-jjdvG.js +1 -0
- package/dist/client/assets/{dist-D0e6Qsad.js → dist-CXpKYW9s.js} +1 -1
- package/dist/client/assets/{dist-BchrxlnW.js → dist-CjII8VDS.js} +1 -1
- package/dist/client/assets/{dist-DnrO-JZd.js → dist-CxfohGIV.js} +1 -1
- package/dist/client/assets/{dist-CMHIjMVe.js → dist-D5QOYy7y.js} +1 -1
- package/dist/client/assets/{dist-CEtbxXlI.js → dist-DIi0uqLJ.js} +1 -1
- package/dist/client/assets/dist-DJ16N7ol.js +1 -0
- package/dist/client/assets/dist-DUvaQ-Bj.js +1 -0
- package/dist/client/assets/{dist-DnxPQ0-w.js → dist-DWJL3_bF.js} +6 -6
- package/dist/client/assets/dist-DsL7sqZR.js +1 -0
- package/dist/client/assets/{dist-DbhQ5ANS.js → dist-Dyt7n5Ui.js} +1 -1
- package/dist/client/assets/{dist-BOrlJDrR.js → dist-E_jPb93e.js} +1 -1
- package/dist/client/assets/{dist-D-gRgZ6e.js → dist-O1z0Ayg0.js} +1 -1
- package/dist/client/assets/dist-OQE68wIX.js +1 -0
- package/dist/client/assets/dist-k47-Io7D.js +1 -0
- package/dist/client/assets/{dist-Cdzvi8cf.js → dist-lDhnP-K1.js} +1 -1
- package/dist/client/assets/{dist-DS6mEQYy.js → dist-rMhng5DK.js} +1 -1
- package/dist/client/assets/dist-xO1ImhKz.js +1 -0
- package/dist/client/assets/{drawio-editor-CgbaOvpF.js → drawio-editor-CTZtMo1J.js} +1 -1
- package/dist/client/assets/{drawioEditor-Eb2yYhjE.js → drawioEditor-keSJVTcv.js} +2 -2
- package/dist/client/assets/editor-session-C9l9XwVg.js +16 -0
- package/dist/client/assets/{embedded-editor-base-DXRHYz6R.css → embedded-editor-base-DgmRcsR8.css} +1 -1
- package/dist/client/assets/{en-CTDJDZ6K-BHbXrqU0.js → en-IQZW2TQM-CZeIyvu2.js} +1 -1
- package/dist/client/assets/{erDiagram-INFDFZHY-DP3f20BF.js → erDiagram-INFDFZHY-DlhPv4az.js} +1 -1
- package/dist/client/assets/{excalidraw-editor-Dj0c0PO6.js → excalidraw-editor-CBEWfuhb.js} +2 -2
- package/dist/client/assets/excalidraw-editor-Dj3i59LW.css +1 -0
- package/dist/client/assets/excalidraw-scene-BL-OtvCd.js +176 -0
- package/dist/client/assets/excalidrawEditor-Ws-fwGOc.js +2 -0
- package/dist/client/assets/{export-page-CUOMczdp.js → export-page-B6xYnwy7.js} +2 -2
- package/dist/client/assets/exportDocument-8JBw0p8O.js +2 -0
- package/dist/client/assets/{exportDocument-CLPYN_gg.css → exportDocument-CtX_4PSk.css} +1 -1
- package/dist/client/assets/{flowDiagram-PKNHOUZH-CRbK5_5h.js → flowDiagram-PKNHOUZH-BU4MOSq8.js} +1 -1
- package/dist/client/assets/ganttDiagram-A5KZAMGK-CbX67lZu.js +292 -0
- package/dist/client/assets/gitGraphDiagram-K3NZZRJ6-DhyvWcW1.js +65 -0
- package/dist/client/assets/image-XLNPCCAD-B4wZgh-Y.js +1 -0
- package/dist/client/assets/{index-CVgVzuh6.js → index-CywRKxsk.js} +2 -2
- package/dist/client/assets/index-R4OArFku.css +1 -0
- package/dist/client/assets/infoDiagram-LFFYTUFH-Bap6MyHX.js +2 -0
- package/dist/client/assets/{ishikawaDiagram-PHBUUO56-BDVPgkv4.js → ishikawaDiagram-PHBUUO56-DbftskqN.js} +1 -1
- package/dist/client/assets/{journeyDiagram-4ABVD52K-DtNnwL48.js → journeyDiagram-4ABVD52K-165YoX6B.js} +1 -1
- package/dist/client/assets/{jsx-runtime-B8dyIONw.js → jsx-runtime-DiaMmmG6.js} +1 -1
- package/dist/client/assets/{kanban-definition-K7BYSVSG-C565sUNo.js → kanban-definition-K7BYSVSG-MlixoNyx.js} +1 -1
- package/dist/client/assets/{main-C8adLQnt.js → main-CB97rY6B.js} +172 -133
- package/dist/client/assets/mermaid-parser.core-lf87rdG-.js +4 -0
- package/dist/client/assets/{mermaid-runtime-DqncQoYK.js → mermaid-runtime-C4a7TrTS.js} +3 -3
- package/dist/client/assets/{mindmap-definition-YRQLILUH-BbYzGfTD.js → mindmap-definition-YRQLILUH-_YbQSc4Q.js} +1 -1
- package/dist/client/assets/pieDiagram-SKSYHLDU-CqCgXFom.js +30 -0
- package/dist/client/assets/preview-render-compiler-TmHpBDlh.js +1 -0
- package/dist/client/assets/preview-render-compiler-aYpnXmNm.js +35 -0
- package/dist/client/assets/preview-render-worker-BVbVXXPS.js +59 -0
- package/dist/client/assets/{quadrantDiagram-337W2JSQ-DhlKLozd.js → quadrantDiagram-337W2JSQ-B8eMuhg4.js} +1 -1
- package/dist/client/assets/{requirementDiagram-Z7DCOOCP-B7ZOMsIb.js → requirementDiagram-Z7DCOOCP-B6POvqz4.js} +1 -1
- package/dist/client/assets/{room-B0UGbzQj.js → room-COWSP--i.js} +1 -1
- package/dist/client/assets/stateDiagram-RAJIS63D-DbewhqwJ.js +1 -0
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-DbW-jVkS.js +1 -0
- package/dist/client/assets/{vennDiagram-LZ73GAT5-Cs9aQmVM.js → vennDiagram-LZ73GAT5-C1hrSbf2.js} +1 -1
- package/dist/client/assets/{xychartDiagram-JWTSCODW-B40ybbzC.js → xychartDiagram-JWTSCODW-DhpWp8LV.js} +1 -1
- package/dist/client/assets/{yjs-provider-reset-guard-DUP9XupO.js → yjs-provider-reset-guard-DWGhQClh.js} +1 -1
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +4 -4
- package/dist/client/index.html +4 -4
- package/docker-compose.yml +1 -0
- package/package.json +23 -21
- package/src/client/application/app-shell/ui-feature-shell.js +47 -1
- package/src/client/application/app-shell/ui-feature-toolbar.js +25 -3
- package/src/client/application/app-shell-elements.js +2 -0
- package/src/client/application/diagram-preview-hydrator.js +1 -1
- package/src/client/application/markdown-frontmatter.js +152 -0
- package/src/client/application/preview-render-compiler.js +10 -1
- package/src/client/application/preview-render-executor.js +8 -1
- package/src/client/application/preview-render-worker.js +4 -0
- package/src/client/application/preview-renderer.js +51 -1
- package/src/client/application/workspace-preview-controller.js +1 -0
- package/src/client/bootstrap/collabmd-app-shell.js +5 -0
- package/src/client/domain/markdown-formatting.js +1 -1
- package/src/client/domain/markdown-toolbar-actions.js +36 -0
- package/src/client/export/export-pipeline.js +3 -1
- package/src/client/infrastructure/editor-session.js +4 -0
- package/src/client/infrastructure/editor-view-adapter.js +36 -2
- package/src/client/infrastructure/excalidraw-room-client.js +12 -4
- package/src/client/presentation/app-shell-template.js +15 -0
- package/src/client/presentation/file-action-controller.js +1 -2
- package/src/client/presentation/git-diff-view-controller.js +7 -10
- package/src/client/styles/base.css +5 -0
- package/src/client/styles/features/preview-markdown.css +138 -0
- package/src/client/styles/layout/editor-page.css +6 -0
- package/src/client/styles/layout/responsive.css +89 -0
- package/src/client/styles/layout/shell.css +6 -0
- package/src/client/styles/overrides/codemirror.css +74 -0
- package/src/server/auth/create-auth-service.js +32 -6
- package/src/server/config/env.js +27 -0
- package/src/server/config/perf-logging.js +39 -0
- package/src/server/create-app-server.js +102 -5
- package/src/server/domain/backlink-index.js +31 -7
- package/src/server/domain/collaboration/collaboration-room.js +28 -5
- package/src/server/infrastructure/git/history-service.js +1 -1
- package/src/server/infrastructure/persistence/pull-backup-store.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +74 -31
- package/src/server/infrastructure/workspace/file-system-sync-service.js +46 -12
- package/src/server/infrastructure/workspace/workspace-mutation-coordinator.js +32 -7
- package/src/server/shared/async-utils.js +25 -0
- package/dist/client/assets/architectureDiagram-2XIMDMQ5-Bhh8YQHn.js +0 -36
- package/dist/client/assets/base-B5gONpJK.css +0 -1
- package/dist/client/assets/chunk-27N2SPOM-DbHKsQ_j.js +0 -34
- package/dist/client/assets/classDiagram-VBA2DB6C-Bpdym3dj.js +0 -1
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-CRAsdVDB.js +0 -1
- package/dist/client/assets/dagre-KLK3FWXG-CXSnjrX-.js +0 -4
- package/dist/client/assets/diagram-E7M64L7V-De0StFef.js +0 -24
- package/dist/client/assets/diagram-IFDJBPK2-B2m6AJbU.js +0 -43
- package/dist/client/assets/diagram-P4PSJMXO-YBWLVENw.js +0 -24
- package/dist/client/assets/dist-BUEzOS23.js +0 -1
- package/dist/client/assets/dist-BflL-_8D.js +0 -1
- package/dist/client/assets/dist-CI_qEj5K.js +0 -1
- package/dist/client/assets/dist-CtFNDttZ.js +0 -1
- package/dist/client/assets/dist-DS_4VpEd.js +0 -1
- package/dist/client/assets/dist-DwsFUbuB.js +0 -1
- package/dist/client/assets/dist-VCYAp958.js +0 -1
- package/dist/client/assets/dist-Yxt8txtM.js +0 -1
- package/dist/client/assets/dist-npxgF_6V.js +0 -1
- package/dist/client/assets/dist-u0zDRMQt.js +0 -1
- package/dist/client/assets/editor-session-DFzBn4RY.js +0 -16
- package/dist/client/assets/excalidraw-editor-Bat1FRoT.css +0 -1
- package/dist/client/assets/excalidraw-scene-CixfM-jL.js +0 -174
- package/dist/client/assets/excalidrawEditor-diK4VITg.js +0 -2
- package/dist/client/assets/exportDocument-BM4_f2n8.js +0 -2
- package/dist/client/assets/ganttDiagram-A5KZAMGK-R-AMIgxR.js +0 -292
- package/dist/client/assets/gitGraphDiagram-K3NZZRJ6-CIPit_Sh.js +0 -65
- package/dist/client/assets/image-32MOCVDX-BB6utPzr.js +0 -1
- package/dist/client/assets/index-BOOhEiLo.css +0 -1
- package/dist/client/assets/infoDiagram-LFFYTUFH-CF8jXFTm.js +0 -2
- package/dist/client/assets/mermaid-parser.core-By_5k2SF.js +0 -4
- package/dist/client/assets/pieDiagram-SKSYHLDU-CAU_gC7U.js +0 -30
- package/dist/client/assets/preview-render-compiler-CVfDHnqM.js +0 -1
- package/dist/client/assets/preview-render-compiler-CapHJDHT.js +0 -1
- package/dist/client/assets/preview-render-worker-wMS710Bv.js +0 -23
- package/dist/client/assets/stateDiagram-RAJIS63D-CPoSrNwn.js +0 -1
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-5GW0Y21a.js +0 -1
- /package/dist/client/assets/{base-r5Zg2n3k.js → base-h1uscjTi.js} +0 -0
|
@@ -29,7 +29,9 @@ function normalizeToolbarAction(action) {
|
|
|
29
29
|
|
|
30
30
|
function renderToolbarButtons(actions) {
|
|
31
31
|
return actions.map((item) => (
|
|
32
|
-
`<button type="button" class="ui-icon-button ui-icon-button--toolbar ui-toolbar-action"
|
|
32
|
+
`<button type="button" class="ui-icon-button ui-icon-button--toolbar ui-toolbar-action" ${item.commandId
|
|
33
|
+
? `data-editor-command="${escapeHtml(item.commandId)}"`
|
|
34
|
+
: `data-markdown-action="${escapeHtml(item.action)}"`} aria-label="${escapeHtml(item.label)}" title="${escapeHtml(item.title)}">${item.icon}</button>`
|
|
33
35
|
)).join('');
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -80,7 +82,7 @@ function renderMarkdownToolbarMarkup(activeAction) {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
return `
|
|
83
|
-
<div class="markdown-toolbar-group" role="group" aria-label="${escapeHtml(group.groupLabel)}">
|
|
85
|
+
<div class="markdown-toolbar-group${group.mobileOnly ? ' markdown-toolbar-group--mobile-only' : ''}" role="group" aria-label="${escapeHtml(group.groupLabel)}">
|
|
84
86
|
${renderToolbarButtons(group.actions)}
|
|
85
87
|
</div>
|
|
86
88
|
`;
|
|
@@ -95,7 +97,7 @@ function renderMarkdownToolbarMarkup(activeAction) {
|
|
|
95
97
|
* @property {{ refresh(): Promise<void> }} fileExplorer
|
|
96
98
|
* @property {{ show(message: string): void }} toastController
|
|
97
99
|
* @property {{ uploadImageAttachment(payload: { file: File, fileName: string, sourcePath: string }): Promise<{ markdown?: string, path?: string }>} } vaultApiClient
|
|
98
|
-
* @property {{ applyMarkdownToolbarAction(action: string): boolean, insertText(text: string): void } | null} session
|
|
100
|
+
* @property {{ applyMarkdownToolbarAction(action: string): boolean, insertText(text: string): void, runEditorCommand?(commandId: string): boolean } | null} session
|
|
99
101
|
* @property {() => Promise<File | null>} pickImageFile
|
|
100
102
|
* @property {(file: File) => Promise<boolean>} handleEditorImageInsert
|
|
101
103
|
* @property {() => Promise<void>} handleToolbarImageInsert
|
|
@@ -273,6 +275,14 @@ function handleMarkdownToolbarClick(event) {
|
|
|
273
275
|
return;
|
|
274
276
|
}
|
|
275
277
|
|
|
278
|
+
const editorCommandButton = target.closest('[data-editor-command]');
|
|
279
|
+
const editorCommandId = editorCommandButton?.getAttribute('data-editor-command');
|
|
280
|
+
if (editorCommandId) {
|
|
281
|
+
event.preventDefault();
|
|
282
|
+
this.runEditorCommand(editorCommandId);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
276
286
|
const blockMenuToggle = target.closest('[data-markdown-block-menu-toggle]');
|
|
277
287
|
if (blockMenuToggle) {
|
|
278
288
|
event.preventDefault();
|
|
@@ -379,6 +389,17 @@ function applyMarkdownToolbarAction(action) {
|
|
|
379
389
|
}
|
|
380
390
|
}
|
|
381
391
|
|
|
392
|
+
/** @this {UiToolbarContext} */
|
|
393
|
+
function runEditorCommand(commandId) {
|
|
394
|
+
const ran = this.session?.runEditorCommand?.(commandId);
|
|
395
|
+
if (!ran) {
|
|
396
|
+
this.toastController.show('Editor action is unavailable');
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
|
|
382
403
|
/** @this {UiToolbarContext} */
|
|
383
404
|
async function handleToolbarImageInsert() {
|
|
384
405
|
const file = await this.pickImageFile();
|
|
@@ -514,6 +535,7 @@ export const uiFeatureToolbarMethods = {
|
|
|
514
535
|
renderMarkdownBlockMenuPopover,
|
|
515
536
|
pickImageFile,
|
|
516
537
|
renderMarkdownToolbar,
|
|
538
|
+
runEditorCommand,
|
|
517
539
|
setActiveMarkdownBlockAction,
|
|
518
540
|
syncMarkdownToolbarBlockUi,
|
|
519
541
|
toggleMarkdownBlockMenu,
|
|
@@ -33,6 +33,7 @@ export function bindAppShellElements(doc = document) {
|
|
|
33
33
|
displayNameSubmit: doc.getElementById('displayNameSubmit'),
|
|
34
34
|
displayNameTitle: doc.getElementById('displayNameTitle'),
|
|
35
35
|
editNameButton: doc.getElementById('editNameBtn'),
|
|
36
|
+
editorFindButton: doc.getElementById('editorFindBtn'),
|
|
36
37
|
editorContainer: doc.getElementById('editorContainer'),
|
|
37
38
|
exportDocxButton: doc.getElementById('exportDocxBtn'),
|
|
38
39
|
exportMenuGroup: doc.getElementById('exportMenuGroup'),
|
|
@@ -52,6 +53,7 @@ export function bindAppShellElements(doc = document) {
|
|
|
52
53
|
mobileViewToggle: doc.getElementById('mobileViewToggle'),
|
|
53
54
|
previewContent: doc.getElementById('previewContent'),
|
|
54
55
|
previewContainer: doc.getElementById('previewContainer'),
|
|
56
|
+
searchFilesButton: doc.getElementById('searchFilesBtn'),
|
|
55
57
|
shareButton: doc.getElementById('shareBtn'),
|
|
56
58
|
toastContainer: doc.getElementById('toastContainer'),
|
|
57
59
|
toggleWrapButton: doc.getElementById('toggleWrapBtn'),
|
|
@@ -291,7 +291,7 @@ export class DiagramPreviewHydrator {
|
|
|
291
291
|
this.hydrationInProgress = true;
|
|
292
292
|
this.renderer.setPhase('hydrating');
|
|
293
293
|
|
|
294
|
-
let batchContext
|
|
294
|
+
let batchContext;
|
|
295
295
|
try {
|
|
296
296
|
batchContext = await this.prepareHydrationBatch(shells);
|
|
297
297
|
} catch (error) {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
import { escapeHtml } from '../domain/vault-utils.js';
|
|
4
|
+
|
|
5
|
+
function getDocumentNewline(markdownText = '') {
|
|
6
|
+
return String(markdownText).includes('\r\n') ? '\r\n' : '\n';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeLine(line = '') {
|
|
10
|
+
return String(line).replace(/\r$/, '');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isPlainObject(value) {
|
|
14
|
+
return Object.prototype.toString.call(value) === '[object Object]';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isScalarValue(value) {
|
|
18
|
+
return (
|
|
19
|
+
value == null
|
|
20
|
+
|| typeof value === 'string'
|
|
21
|
+
|| typeof value === 'number'
|
|
22
|
+
|| typeof value === 'boolean'
|
|
23
|
+
|| value instanceof Date
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function formatScalarValue(value) {
|
|
28
|
+
if (value instanceof Date) {
|
|
29
|
+
return value.toISOString();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (value == null) {
|
|
33
|
+
return 'null';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return String(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function serializeComplexValue(value) {
|
|
40
|
+
return yaml.dump(value, {
|
|
41
|
+
lineWidth: -1,
|
|
42
|
+
noRefs: true,
|
|
43
|
+
}).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createFrontmatterValueMarkup(value) {
|
|
47
|
+
if (isScalarValue(value)) {
|
|
48
|
+
return `<span class="frontmatter-value-text">${escapeHtml(formatScalarValue(value))}</span>`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(value) && value.every((item) => isScalarValue(item))) {
|
|
52
|
+
return `<div class="frontmatter-value-list">${value
|
|
53
|
+
.map((item) => `<span class="frontmatter-value-pill">${escapeHtml(formatScalarValue(item))}</span>`)
|
|
54
|
+
.join('')}</div>`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `<pre class="frontmatter-value-code"><code>${escapeHtml(serializeComplexValue(value))}</code></pre>`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createFrontmatterEntries(data) {
|
|
61
|
+
if (isPlainObject(data)) {
|
|
62
|
+
return Object.entries(data);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return [['value', data]];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createHiddenSummary(entries) {
|
|
69
|
+
if (entries.length === 0) {
|
|
70
|
+
return 'No properties';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `${entries.length} propert${entries.length === 1 ? 'y' : 'ies'} hidden`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function extractYamlFrontmatter(markdownText = '') {
|
|
77
|
+
const normalizedMarkdown = String(markdownText ?? '');
|
|
78
|
+
if (!normalizedMarkdown.startsWith('---')) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const newline = getDocumentNewline(normalizedMarkdown);
|
|
83
|
+
const lines = normalizedMarkdown.split(/\r?\n/);
|
|
84
|
+
if (normalizeLine(lines[0]) !== '---') {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let closingIndex = -1;
|
|
89
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
90
|
+
if (normalizeLine(lines[index]) === '---') {
|
|
91
|
+
closingIndex = index;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (closingIndex < 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const rawFrontmatter = lines.slice(0, closingIndex + 1).join(newline);
|
|
101
|
+
const frontmatterSource = lines.slice(1, closingIndex).join(newline);
|
|
102
|
+
|
|
103
|
+
let parsedData;
|
|
104
|
+
try {
|
|
105
|
+
parsedData = yaml.load(frontmatterSource || '{}');
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const lineCount = closingIndex + 1;
|
|
111
|
+
const bodyMarkdown = [
|
|
112
|
+
...Array.from({ length: lineCount }, () => ''),
|
|
113
|
+
...lines.slice(closingIndex + 1),
|
|
114
|
+
].join(newline);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
bodyMarkdown,
|
|
118
|
+
data: parsedData ?? {},
|
|
119
|
+
endLine: lineCount,
|
|
120
|
+
rawFrontmatter,
|
|
121
|
+
startLine: 1,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function renderFrontmatterBlock(frontmatter, {
|
|
126
|
+
collapsed = false,
|
|
127
|
+
interactive = false,
|
|
128
|
+
} = {}) {
|
|
129
|
+
if (!frontmatter) {
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const entries = createFrontmatterEntries(frontmatter.data);
|
|
134
|
+
const collapsedState = interactive && collapsed;
|
|
135
|
+
const sourceAttributes = ` data-source-line="${frontmatter.startLine}" data-source-line-end="${frontmatter.endLine}"${interactive ? ` data-collapsed="${collapsedState ? 'true' : 'false'}"` : ''}`;
|
|
136
|
+
const bodyId = `frontmatter-body-${frontmatter.startLine}-${frontmatter.endLine}`;
|
|
137
|
+
const emptyState = entries.length === 0
|
|
138
|
+
? '<div class="frontmatter-empty">No properties</div>'
|
|
139
|
+
: '';
|
|
140
|
+
const items = entries.map(([key, value]) => (
|
|
141
|
+
`<div class="frontmatter-property"><dt class="frontmatter-key">${escapeHtml(key)}</dt><dd class="frontmatter-value">${createFrontmatterValueMarkup(value)}</dd></div>`
|
|
142
|
+
)).join('');
|
|
143
|
+
const header = interactive
|
|
144
|
+
? `<div class="frontmatter-header"><div class="frontmatter-label">Properties</div><button type="button" class="frontmatter-toggle" aria-controls="${bodyId}" aria-expanded="${collapsedState ? 'false' : 'true'}">${collapsedState ? 'Show' : 'Hide'}</button></div>`
|
|
145
|
+
: '<div class="frontmatter-label">Properties</div>';
|
|
146
|
+
const summary = interactive
|
|
147
|
+
? `<div class="frontmatter-summary"${collapsedState ? '' : ' hidden'}>${escapeHtml(createHiddenSummary(entries))}</div>`
|
|
148
|
+
: '';
|
|
149
|
+
const bodyAttributes = ` class="frontmatter-content"${interactive ? ` id="${bodyId}"` : ''}${collapsedState ? ' hidden' : ''}`;
|
|
150
|
+
|
|
151
|
+
return `<section class="frontmatter-block"${sourceAttributes}>${header}${summary}<div${bodyAttributes}><dl class="frontmatter-properties">${items}</dl>${emptyState}</div></section>`;
|
|
152
|
+
}
|
|
@@ -3,6 +3,7 @@ import hljs from 'highlight.js';
|
|
|
3
3
|
|
|
4
4
|
import { isImageAttachmentFilePath } from '../../domain/file-kind.js';
|
|
5
5
|
import { escapeHtml, resolveVaultRelativePath, resolveWikiTarget } from '../domain/vault-utils.js';
|
|
6
|
+
import { extractYamlFrontmatter, renderFrontmatterBlock } from './markdown-frontmatter.js';
|
|
6
7
|
import { analyzeMarkdownComplexity } from './preview-render-profile.js';
|
|
7
8
|
|
|
8
9
|
const DIRECT_VIDEO_MIME_TYPES = Object.freeze({
|
|
@@ -480,17 +481,25 @@ function createMarkdownRenderer(fileList = [], {
|
|
|
480
481
|
export function compilePreviewDocument({
|
|
481
482
|
attachmentApiPath = '/api/attachment',
|
|
482
483
|
fileList = [],
|
|
484
|
+
frontmatterCollapsed = false,
|
|
485
|
+
frontmatterInteractive = false,
|
|
483
486
|
markdownText = '',
|
|
484
487
|
sourceFilePath = '',
|
|
485
488
|
} = {}) {
|
|
486
489
|
const normalizedMarkdown = String(markdownText);
|
|
490
|
+
const frontmatter = extractYamlFrontmatter(normalizedMarkdown);
|
|
487
491
|
const renderer = createMarkdownRenderer(fileList, {
|
|
488
492
|
attachmentApiPath,
|
|
489
493
|
sourceFilePath,
|
|
490
494
|
});
|
|
495
|
+
const renderedMarkdown = frontmatter ? frontmatter.bodyMarkdown : normalizedMarkdown;
|
|
496
|
+
const frontmatterHtml = renderFrontmatterBlock(frontmatter, {
|
|
497
|
+
collapsed: frontmatterCollapsed,
|
|
498
|
+
interactive: frontmatterInteractive,
|
|
499
|
+
});
|
|
491
500
|
|
|
492
501
|
return {
|
|
493
|
-
html: renderer.render(
|
|
502
|
+
html: `${frontmatterHtml}${renderer.render(renderedMarkdown)}`,
|
|
494
503
|
stats: analyzeMarkdownComplexity(normalizedMarkdown),
|
|
495
504
|
};
|
|
496
505
|
}
|
|
@@ -97,7 +97,10 @@ export class PreviewRenderExecutor {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
async compile(markdownText, renderVersion
|
|
100
|
+
async compile(markdownText, renderVersion, {
|
|
101
|
+
frontmatterCollapsed = false,
|
|
102
|
+
frontmatterInteractive = false,
|
|
103
|
+
} = {}) {
|
|
101
104
|
const worker = this.ensureWorker();
|
|
102
105
|
|
|
103
106
|
if (worker) {
|
|
@@ -111,6 +114,8 @@ export class PreviewRenderExecutor {
|
|
|
111
114
|
activeWorker.postMessage({
|
|
112
115
|
attachmentApiPath: this.attachmentApiPath,
|
|
113
116
|
fileList: this.getFileList?.() ?? [],
|
|
117
|
+
frontmatterCollapsed,
|
|
118
|
+
frontmatterInteractive,
|
|
114
119
|
markdownText,
|
|
115
120
|
renderVersion,
|
|
116
121
|
sourceFilePath: this.getSourceFilePath?.() ?? '',
|
|
@@ -122,6 +127,8 @@ export class PreviewRenderExecutor {
|
|
|
122
127
|
return compilePreviewDocument({
|
|
123
128
|
attachmentApiPath: this.attachmentApiPath,
|
|
124
129
|
fileList: this.getFileList?.() ?? [],
|
|
130
|
+
frontmatterCollapsed,
|
|
131
|
+
frontmatterInteractive,
|
|
125
132
|
markdownText,
|
|
126
133
|
sourceFilePath: this.getSourceFilePath?.() ?? '',
|
|
127
134
|
});
|
|
@@ -4,6 +4,8 @@ self.onmessage = (event) => {
|
|
|
4
4
|
const {
|
|
5
5
|
attachmentApiPath,
|
|
6
6
|
fileList,
|
|
7
|
+
frontmatterCollapsed,
|
|
8
|
+
frontmatterInteractive,
|
|
7
9
|
markdownText,
|
|
8
10
|
renderVersion,
|
|
9
11
|
sourceFilePath,
|
|
@@ -13,6 +15,8 @@ self.onmessage = (event) => {
|
|
|
13
15
|
const result = compilePreviewDocument({
|
|
14
16
|
attachmentApiPath,
|
|
15
17
|
fileList,
|
|
18
|
+
frontmatterCollapsed,
|
|
19
|
+
frontmatterInteractive,
|
|
16
20
|
markdownText,
|
|
17
21
|
sourceFilePath,
|
|
18
22
|
});
|
|
@@ -13,6 +13,7 @@ export class PreviewRenderer {
|
|
|
13
13
|
getSourceFilePath,
|
|
14
14
|
onAfterRenderCommit,
|
|
15
15
|
onBeforeRenderCommit,
|
|
16
|
+
onPreviewLayoutChange,
|
|
16
17
|
onRenderComplete,
|
|
17
18
|
outlineController,
|
|
18
19
|
previewContainer,
|
|
@@ -23,6 +24,7 @@ export class PreviewRenderer {
|
|
|
23
24
|
this.getSourceFilePath = getSourceFilePath;
|
|
24
25
|
this.onAfterRenderCommit = onAfterRenderCommit;
|
|
25
26
|
this.onBeforeRenderCommit = onBeforeRenderCommit;
|
|
27
|
+
this.onPreviewLayoutChange = onPreviewLayoutChange;
|
|
26
28
|
this.onRenderComplete = onRenderComplete;
|
|
27
29
|
this.outlineController = outlineController;
|
|
28
30
|
this.previewContainer = previewContainer;
|
|
@@ -35,8 +37,18 @@ export class PreviewRenderer {
|
|
|
35
37
|
this.currentStats = null;
|
|
36
38
|
this.isLargeDocument = false;
|
|
37
39
|
this.hydrationPaused = false;
|
|
40
|
+
this.frontmatterCollapsed = false;
|
|
38
41
|
|
|
39
42
|
this.handlePreviewClick = (event) => {
|
|
43
|
+
const frontmatterToggle = event.target.closest('.frontmatter-toggle');
|
|
44
|
+
if (frontmatterToggle) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
this.frontmatterCollapsed = !this.frontmatterCollapsed;
|
|
47
|
+
this.applyFrontmatterState();
|
|
48
|
+
this.handleFrontmatterLayoutChange();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
const mermaidButton = event.target.closest('.mermaid-placeholder-btn');
|
|
41
53
|
if (mermaidButton) {
|
|
42
54
|
const shell = mermaidButton.closest('.mermaid-shell');
|
|
@@ -134,6 +146,7 @@ export class PreviewRenderer {
|
|
|
134
146
|
this.readyRenderVersion = 0;
|
|
135
147
|
this.currentStats = null;
|
|
136
148
|
this.isLargeDocument = false;
|
|
149
|
+
this.frontmatterCollapsed = false;
|
|
137
150
|
if (this.renderExecutor.hasPendingJob()) {
|
|
138
151
|
this.resetWorker('Document changed');
|
|
139
152
|
}
|
|
@@ -272,7 +285,10 @@ export class PreviewRenderer {
|
|
|
272
285
|
}
|
|
273
286
|
|
|
274
287
|
async compilePreview(markdownText, renderVersion) {
|
|
275
|
-
return this.renderExecutor.compile(markdownText, renderVersion
|
|
288
|
+
return this.renderExecutor.compile(markdownText, renderVersion, {
|
|
289
|
+
frontmatterCollapsed: this.frontmatterCollapsed,
|
|
290
|
+
frontmatterInteractive: true,
|
|
291
|
+
});
|
|
276
292
|
}
|
|
277
293
|
|
|
278
294
|
commitBaseRender({ html, stats }, renderVersion) {
|
|
@@ -294,6 +310,7 @@ export class PreviewRenderer {
|
|
|
294
310
|
if (renderHost) {
|
|
295
311
|
renderHost.innerHTML = html;
|
|
296
312
|
}
|
|
313
|
+
this.applyFrontmatterState();
|
|
297
314
|
this.reconcileHydratedMermaids();
|
|
298
315
|
this.reconcileHydratedPlantUmls();
|
|
299
316
|
this.setPhase('base');
|
|
@@ -313,6 +330,39 @@ export class PreviewRenderer {
|
|
|
313
330
|
}
|
|
314
331
|
}
|
|
315
332
|
|
|
333
|
+
applyFrontmatterState() {
|
|
334
|
+
const block = this.renderHost?.querySelector?.('.frontmatter-block');
|
|
335
|
+
if (!(block instanceof HTMLElement)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const collapsed = this.frontmatterCollapsed;
|
|
340
|
+
const toggle = block.querySelector('.frontmatter-toggle');
|
|
341
|
+
const summary = block.querySelector('.frontmatter-summary');
|
|
342
|
+
const content = block.querySelector('.frontmatter-content');
|
|
343
|
+
|
|
344
|
+
block.dataset.collapsed = collapsed ? 'true' : 'false';
|
|
345
|
+
if (toggle instanceof HTMLButtonElement) {
|
|
346
|
+
toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
347
|
+
toggle.textContent = collapsed ? 'Show' : 'Hide';
|
|
348
|
+
}
|
|
349
|
+
if (summary instanceof HTMLElement) {
|
|
350
|
+
summary.hidden = !collapsed;
|
|
351
|
+
}
|
|
352
|
+
if (content instanceof HTMLElement) {
|
|
353
|
+
content.hidden = collapsed;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
handleFrontmatterLayoutChange() {
|
|
358
|
+
this.outlineController.refresh();
|
|
359
|
+
this.onPreviewLayoutChange?.({
|
|
360
|
+
isLargeDocument: this.isLargeDocument,
|
|
361
|
+
renderVersion: this.activeRenderVersion,
|
|
362
|
+
stats: this.currentStats,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
316
366
|
preserveHydratedMermaidsForCommit() {
|
|
317
367
|
this.mermaidHydrator.preserveHydratedShellsForCommit();
|
|
318
368
|
}
|
|
@@ -92,6 +92,7 @@ export class WorkspacePreviewController {
|
|
|
92
92
|
const isPlantUml = this.isPlantUmlFile(filePath);
|
|
93
93
|
const isDiagramFile = isDiagramFilePath(filePath);
|
|
94
94
|
|
|
95
|
+
this.elements.editorFindButton?.classList.toggle('hidden', !isMarkdown);
|
|
95
96
|
this.elements.markdownToolbar?.classList.toggle('hidden', !isMarkdown);
|
|
96
97
|
this.elements.exportMenuGroup?.classList.toggle('hidden', !isMarkdown);
|
|
97
98
|
this.elements.outlineToggle?.classList.toggle('hidden', isDiagramFile || isImage);
|
|
@@ -212,6 +212,11 @@ export class CollabMdAppShell {
|
|
|
212
212
|
this.drawioEmbed.detachForCommit();
|
|
213
213
|
this.excalidrawEmbed.detachForCommit();
|
|
214
214
|
},
|
|
215
|
+
onPreviewLayoutChange: () => {
|
|
216
|
+
this.scrollSyncController.invalidatePreviewBlocks();
|
|
217
|
+
this.schedulePreviewLayoutSync({ delayMs: 0 });
|
|
218
|
+
this.refreshCommentUiLayout();
|
|
219
|
+
},
|
|
215
220
|
onRenderComplete: () => {
|
|
216
221
|
this.videoEmbed.syncLayout();
|
|
217
222
|
this.drawioEmbed.syncLayout();
|
|
@@ -434,7 +434,7 @@ function formatHorizontalRule(text, range) {
|
|
|
434
434
|
export function createMarkdownToolbarEdit(documentText, selectionRange, action) {
|
|
435
435
|
const text = String(documentText ?? '');
|
|
436
436
|
const range = normalizeRange(selectionRange, text.length);
|
|
437
|
-
let edit
|
|
437
|
+
let edit;
|
|
438
438
|
|
|
439
439
|
switch (action) {
|
|
440
440
|
case 'bold':
|
|
@@ -59,6 +59,34 @@ const ICONS = Object.freeze({
|
|
|
59
59
|
<path d="M9 16h6"/>
|
|
60
60
|
</svg>
|
|
61
61
|
`,
|
|
62
|
+
indent: `
|
|
63
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
64
|
+
<path d="M4 6h16"/>
|
|
65
|
+
<path d="M10 12h10"/>
|
|
66
|
+
<path d="M10 18h10"/>
|
|
67
|
+
<path d="m4 9 4 3-4 3"/>
|
|
68
|
+
</svg>
|
|
69
|
+
`,
|
|
70
|
+
outdent: `
|
|
71
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
72
|
+
<path d="M4 6h16"/>
|
|
73
|
+
<path d="M10 12h10"/>
|
|
74
|
+
<path d="M10 18h10"/>
|
|
75
|
+
<path d="m8 9-4 3 4 3"/>
|
|
76
|
+
</svg>
|
|
77
|
+
`,
|
|
78
|
+
redo: `
|
|
79
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
80
|
+
<path d="M21 7v6h-6"/>
|
|
81
|
+
<path d="M20 13a8 8 0 0 0-14-3"/>
|
|
82
|
+
</svg>
|
|
83
|
+
`,
|
|
84
|
+
undo: `
|
|
85
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
86
|
+
<path d="M3 7v6h6"/>
|
|
87
|
+
<path d="M4 13a8 8 0 0 1 14-3"/>
|
|
88
|
+
</svg>
|
|
89
|
+
`,
|
|
62
90
|
chevronDown: `
|
|
63
91
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
64
92
|
<path d="m6 9 6 6 6-6"/>
|
|
@@ -148,6 +176,13 @@ const INLINE_ACTIONS = Object.freeze([
|
|
|
148
176
|
{ action: 'code', label: 'Inline code', title: 'Inline code', icon: ICONS.code },
|
|
149
177
|
]);
|
|
150
178
|
|
|
179
|
+
const MOBILE_EDIT_COMMANDS = Object.freeze([
|
|
180
|
+
{ commandId: 'undo', label: 'Undo', title: 'Undo', icon: ICONS.undo },
|
|
181
|
+
{ commandId: 'redo', label: 'Redo', title: 'Redo', icon: ICONS.redo },
|
|
182
|
+
{ commandId: 'indentMore', label: 'Indent', title: 'Indent', icon: ICONS.indent },
|
|
183
|
+
{ commandId: 'indentLess', label: 'Outdent', title: 'Outdent', icon: ICONS.outdent },
|
|
184
|
+
]);
|
|
185
|
+
|
|
151
186
|
const MEDIA_ACTIONS = Object.freeze([
|
|
152
187
|
{ action: 'link', label: 'Insert link', title: 'Insert link', icon: ICONS.link },
|
|
153
188
|
{ action: 'image', label: 'Insert image', title: 'Insert image', icon: ICONS.image },
|
|
@@ -160,6 +195,7 @@ const INSERT_ACTIONS = Object.freeze([
|
|
|
160
195
|
]);
|
|
161
196
|
|
|
162
197
|
export const markdownToolbarLayout = Object.freeze([
|
|
198
|
+
{ kind: 'buttons', groupLabel: 'Edit', actions: MOBILE_EDIT_COMMANDS, mobileOnly: true },
|
|
163
199
|
{ kind: 'block-menu', actions: BLOCK_MENU_ACTIONS },
|
|
164
200
|
{ kind: 'buttons', groupLabel: 'Inline formatting', actions: INLINE_ACTIONS },
|
|
165
201
|
{ kind: 'buttons', groupLabel: 'Media and links', actions: MEDIA_ACTIONS },
|
|
@@ -217,7 +217,9 @@ async function fetchAsDataUrl(src, {
|
|
|
217
217
|
});
|
|
218
218
|
} catch (error) {
|
|
219
219
|
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
220
|
-
throw new Error(`Timed out while fetching asset: ${assetUrl.toString()}
|
|
220
|
+
throw new Error(`Timed out while fetching asset: ${assetUrl.toString()}`, {
|
|
221
|
+
cause: error,
|
|
222
|
+
});
|
|
221
223
|
}
|
|
222
224
|
throw error;
|
|
223
225
|
} finally {
|
|
@@ -258,6 +258,10 @@ export class EditorSession {
|
|
|
258
258
|
this.viewAdapter.requestMeasure();
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
runEditorCommand(commandId) {
|
|
262
|
+
return this.viewAdapter.runEditorCommand(commandId);
|
|
263
|
+
}
|
|
264
|
+
|
|
261
265
|
applyMarkdownToolbarAction(action) {
|
|
262
266
|
return this.viewAdapter.applyMarkdownToolbarAction(action);
|
|
263
267
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { autocompletion, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
defaultKeymap,
|
|
4
|
+
history,
|
|
5
|
+
historyKeymap,
|
|
6
|
+
indentLess,
|
|
7
|
+
indentMore,
|
|
8
|
+
indentWithTab,
|
|
9
|
+
redo,
|
|
10
|
+
undo,
|
|
11
|
+
} from '@codemirror/commands';
|
|
3
12
|
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
|
4
13
|
import {
|
|
5
14
|
bracketMatching,
|
|
@@ -10,7 +19,7 @@ import {
|
|
|
10
19
|
syntaxHighlighting,
|
|
11
20
|
} from '@codemirror/language';
|
|
12
21
|
import { languages } from '@codemirror/language-data';
|
|
13
|
-
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
|
|
22
|
+
import { highlightSelectionMatches, openSearchPanel, searchKeymap } from '@codemirror/search';
|
|
14
23
|
import { Compartment, EditorSelection, EditorState, Prec, StateEffect, StateField } from '@codemirror/state';
|
|
15
24
|
import { oneDark } from '@codemirror/theme-one-dark';
|
|
16
25
|
import {
|
|
@@ -42,6 +51,13 @@ const REMOTE_UPDATE_FLASH_DURATION_MS = 1350;
|
|
|
42
51
|
const REMOTE_UPDATE_CARET_MAX_LENGTH = 160;
|
|
43
52
|
const RECENT_LOCAL_INPUT_WINDOW_MS = 900;
|
|
44
53
|
const TASK_LIST_MARKER_PATTERN = /^(\s*(?:>\s*)*(?:[-+*]|\d+[.)])\s+\[)( |x|X)(\])(\s.*)?$/;
|
|
54
|
+
const EDITOR_COMMANDS = Object.freeze({
|
|
55
|
+
indentLess,
|
|
56
|
+
indentMore,
|
|
57
|
+
openSearch: openSearchPanel,
|
|
58
|
+
redo,
|
|
59
|
+
undo,
|
|
60
|
+
});
|
|
45
61
|
|
|
46
62
|
class RemoteUpdateCaretWidget extends WidgetType {
|
|
47
63
|
toDOM() {
|
|
@@ -557,6 +573,24 @@ export class EditorViewAdapter {
|
|
|
557
573
|
this.editorView?.requestMeasure();
|
|
558
574
|
}
|
|
559
575
|
|
|
576
|
+
runEditorCommand(commandId) {
|
|
577
|
+
if (!this.editorView) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const command = EDITOR_COMMANDS[String(commandId ?? '')];
|
|
582
|
+
if (typeof command !== 'function') {
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const applied = command(this.editorView);
|
|
587
|
+
if (applied && commandId !== 'openSearch') {
|
|
588
|
+
this.editorView.focus();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return applied;
|
|
592
|
+
}
|
|
593
|
+
|
|
560
594
|
flashRemoteRange({ from = 0, to = 0 } = {}, durationMs = REMOTE_UPDATE_FLASH_DURATION_MS) {
|
|
561
595
|
const state = this.editorView?.state;
|
|
562
596
|
if (!state || state.doc.length === 0) {
|
|
@@ -258,10 +258,14 @@ export class ExcalidrawRoomClient {
|
|
|
258
258
|
} catch (readError) {
|
|
259
259
|
if (readError?.status !== 404 || !createIfMissing) {
|
|
260
260
|
if (readError?.status === 404) {
|
|
261
|
-
throw new Error(readError.message || 'Excalidraw file not found'
|
|
261
|
+
throw new Error(readError.message || 'Excalidraw file not found', {
|
|
262
|
+
cause: readError,
|
|
263
|
+
});
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
throw new Error(readError?.message || 'Failed to load Excalidraw file'
|
|
266
|
+
throw new Error(readError?.message || 'Failed to load Excalidraw file', {
|
|
267
|
+
cause: readError,
|
|
268
|
+
});
|
|
265
269
|
}
|
|
266
270
|
}
|
|
267
271
|
|
|
@@ -278,11 +282,15 @@ export class ExcalidrawRoomClient {
|
|
|
278
282
|
const existingData = await this.vaultClient.readFile(this.filePath);
|
|
279
283
|
return parseSceneJson(existingData.content);
|
|
280
284
|
} catch (conflictReadError) {
|
|
281
|
-
throw new Error(conflictReadError?.message || 'Failed to load existing Excalidraw file after create conflict'
|
|
285
|
+
throw new Error(conflictReadError?.message || 'Failed to load existing Excalidraw file after create conflict', {
|
|
286
|
+
cause: conflictReadError,
|
|
287
|
+
});
|
|
282
288
|
}
|
|
283
289
|
}
|
|
284
290
|
|
|
285
|
-
throw new Error(createError?.message || 'Failed to create Excalidraw file'
|
|
291
|
+
throw new Error(createError?.message || 'Failed to create Excalidraw file', {
|
|
292
|
+
cause: createError,
|
|
293
|
+
});
|
|
286
294
|
}
|
|
287
295
|
}
|
|
288
296
|
|