collabmd 0.1.44 → 0.1.45
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/dist/client/assets/{chunk-AXR3VHVW-0GTk_evH.js → chunk-ERSWUTIN-ME9RVNel.js} +3 -3
- package/dist/client/assets/{chunk-CZQZEIFA-DT3N9fo7.js → chunk-YGJVY4SA-CjSe7Bx7.js} +2 -2
- package/dist/client/assets/{comment-threads-D-iKphIp.js → comment-threads-DDegmaFO.js} +1 -1
- package/dist/client/assets/drawio-editor-ClE8jpoG.js +9 -0
- package/dist/client/assets/drawioEditor-CXaTM9Ds.js +2 -0
- package/dist/client/assets/editor-session-GTwZS6Df.js +16 -0
- package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +1 -0
- package/dist/client/assets/{en-GADW4C6J-DQ6zyCEM.js → en-7NFCYGKX-BrgYZAEe.js} +1 -1
- package/dist/client/assets/{excalidraw-editor-BHu6k8d4.js → excalidraw-editor-BZVM81BO.js} +2 -2
- package/dist/client/assets/excalidraw-editor-CyGwOhLz.css +1 -0
- package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +176 -0
- package/dist/client/assets/excalidrawEditor-DxJanCze.js +2 -0
- package/dist/client/assets/export-page-CUfws2T-.js +39 -0
- package/dist/client/assets/exportDocument-BCuxqXDY.js +2 -0
- package/dist/client/assets/exportDocument-DV-OX74E.css +1 -0
- package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +1 -0
- package/dist/client/assets/index-C3YnUgMD.css +1 -0
- package/dist/client/assets/index-D2wXraJ1.js +2 -0
- package/dist/client/assets/main-DtiGz7uI.js +795 -0
- package/dist/client/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
- package/dist/client/assets/preview-diagram-utils-txBCoW8d.js +1 -0
- package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +12 -0
- package/dist/client/assets/room-CQLoqtMo.js +1 -0
- package/dist/client/assets/runtime-config-DSGmGE9V.js +1 -0
- package/dist/client/assets/vault-api-client-C6bcGwy-.js +5 -0
- package/dist/client/assets/{yjs-provider-reset-guard-Cox4nxwu.js → yjs-provider-reset-guard-DE0vINDP.js} +1 -1
- package/dist/client/drawio-editor.html +3 -2
- package/dist/client/excalidraw-editor.html +3 -2
- package/dist/client/export-document.html +4 -3
- package/dist/client/index.html +97 -27
- package/package.json +10 -12
- package/src/client/app/drawio-editor-entry.js +0 -3
- package/src/client/app/drawio-editor.html +1 -0
- package/src/client/app/excalidraw-editor-entry.js +0 -3
- package/src/client/app/excalidraw-editor.html +1 -0
- package/src/client/app/export-document-entry.js +0 -3
- package/src/client/app/export-document.html +1 -0
- package/src/client/app/index.html +94 -24
- package/src/client/app/main-entry.js +0 -5
- package/src/client/application/app-shell/chat-feature.js +180 -124
- package/src/client/application/app-shell/comments-feature.js +1 -1
- package/src/client/application/app-shell/git-feature.js +4 -9
- package/src/client/application/app-shell/presence-feature.js +16 -10
- package/src/client/application/app-shell/ui-feature-shell.js +33 -15
- package/src/client/application/app-shell/ui-feature-sidebar.js +74 -1
- package/src/client/application/app-shell/ui-feature-tab-activity.js +2 -2
- package/src/client/application/app-shell/workspace-feature.js +22 -83
- package/src/client/application/app-shell-elements.js +7 -1
- package/src/client/application/diagram-chrome.js +161 -166
- package/src/client/application/diagram-preview-export.js +7 -34
- package/src/client/application/diagram-preview-hydrator.js +1 -19
- package/src/client/application/markdown-frontmatter.js +0 -2
- package/src/client/application/mermaid-preview-hydrator.js +1 -14
- package/src/client/application/plantuml-preview-hydrator.js +0 -10
- package/src/client/application/preview-diagram-utils.js +31 -2
- package/src/client/application/preview-render-compiler.js +2 -1
- package/src/client/application/preview-renderer.js +2 -2
- package/src/client/application/quick-switcher-loader.js +3 -1
- package/src/client/application/workspace-preview-controller.js +0 -3
- package/src/client/bootstrap/collabmd-app-shell.js +125 -39
- package/src/client/domain/excalidraw-collaboration.js +0 -2
- package/src/client/domain/room.js +38 -0
- package/src/client/export/export-pipeline.js +6 -34
- package/src/client/infrastructure/auth-client.js +4 -10
- package/src/client/infrastructure/browser-notification-port.js +33 -20
- package/src/client/infrastructure/browser-preferences-port.js +36 -9
- package/src/client/infrastructure/editor-view-adapter.js +87 -3
- package/src/client/infrastructure/lobby-presence.js +1 -0
- package/src/client/infrastructure/vault-api-client.js +25 -0
- package/src/client/infrastructure/workspace-sync-client.js +13 -1
- package/src/client/presentation/comment-overview-controller.js +18 -53
- package/src/client/presentation/comment-ui/comment-ui-card.js +1 -12
- package/src/client/presentation/comment-ui/comment-ui-layout.js +242 -219
- package/src/client/presentation/comment-ui/comment-ui-render.js +32 -45
- package/src/client/presentation/comment-ui/comment-ui-shared.js +65 -14
- package/src/client/presentation/comment-ui-controller.js +5 -1
- package/src/client/presentation/file-action-controller.js +32 -0
- package/src/client/presentation/file-explorer-controller.js +33 -3
- package/src/client/presentation/file-explorer-view.js +49 -5
- package/src/client/presentation/file-tree-state.js +10 -4
- package/src/client/presentation/quick-switcher-controller.js +243 -72
- package/src/client/presentation/quick-switcher-text-search.js +2 -2
- package/src/client/presentation/toast-controller.js +3 -0
- package/src/client/styles/components/feedback.css +6 -0
- package/src/client/styles/features/collaboration-chat.css +49 -53
- package/src/client/styles/features/collaboration-presence.css +9 -7
- package/src/client/styles/features/comment-card.css +25 -11
- package/src/client/styles/features/comment-overlays.css +0 -4
- package/src/client/styles/features/comment-overview.css +44 -21
- package/src/client/styles/features/comments-drawer.css +2 -100
- package/src/client/styles/features/diagram-preview.css +5 -18
- package/src/client/styles/features/embedded-preview.css +8 -33
- package/src/client/styles/features/quick-switcher.css +8 -0
- package/src/client/styles/foundation/tokens.css +1 -0
- package/src/client/styles/layout/editor-page.css +40 -0
- package/src/client/styles/layout/responsive.css +31 -1
- package/src/client/styles/layout/shell.css +25 -6
- package/src/client/styles/layout/sidebar.css +47 -15
- package/src/client/styles/overrides/codemirror.css +217 -15
- package/src/server/application/workspace-reconciliation.js +1 -1
- package/src/server/create-app-server.js +2 -3
- package/src/server/domain/bases/base-query-service.js +1 -77
- package/src/server/domain/comment-overview.js +10 -3
- package/src/server/domain/docx-exporter.js +24 -33
- package/src/server/domain/ripgrep-search-service.js +3 -1
- package/src/server/domain/workspace-state.js +32 -11
- package/src/server/infrastructure/git/diff-service.js +24 -13
- package/src/server/infrastructure/git/git-service.js +40 -10
- package/src/server/infrastructure/git/history-service.js +42 -29
- package/src/server/infrastructure/git/path-utils.js +1 -1
- package/src/server/infrastructure/git/responses.js +0 -95
- package/src/server/infrastructure/github/github-app-client.js +22 -15
- package/src/server/infrastructure/http/create-request-handler.js +2 -8
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +5 -5
- package/src/server/infrastructure/http/http-errors.js +7 -1
- package/dist/client/assets/bases-preview-controller-2pLHk0uk.js +0 -143
- package/dist/client/assets/button-BCEiw4Rw.js +0 -1
- package/dist/client/assets/diagram-action-icons-aa_KOv9h.js +0 -36
- package/dist/client/assets/drawio-editor-DDvFoarP.js +0 -9
- package/dist/client/assets/drawio-embed-controller-BD1GTRwn.js +0 -1
- package/dist/client/assets/drawioEditor-Bu5grlH5.js +0 -2
- package/dist/client/assets/editor-session-DGlpiAsK.js +0 -16
- package/dist/client/assets/embedded-editor-base-C9roKYpW.css +0 -1
- package/dist/client/assets/excalidraw-editor-Cr_184EF.css +0 -1
- package/dist/client/assets/excalidraw-embed-controller-y2fenN_g.js +0 -1
- package/dist/client/assets/excalidraw-embed-reconciler-DGiagExg.js +0 -1
- package/dist/client/assets/excalidraw-scene-BvsQ6mLP.js +0 -176
- package/dist/client/assets/excalidrawEditor-4vFE--Uw.js +0 -2
- package/dist/client/assets/export-page-Cby8JzPO.js +0 -39
- package/dist/client/assets/exportDocument-C9v-bc7I.css +0 -1
- package/dist/client/assets/exportDocument-CRHCfsdo.js +0 -2
- package/dist/client/assets/file-history-view-controller-BDjEV9xJ.js +0 -112
- package/dist/client/assets/git-diff-view-controller-DgdM1aqb.js +0 -150
- package/dist/client/assets/git-panel-controller-BDTR2HVp.js +0 -207
- package/dist/client/assets/image-XGD7JQR6-XDUWHRpO.js +0 -1
- package/dist/client/assets/index-7xOyXDhP.css +0 -1
- package/dist/client/assets/index-Bz1KuQ6y.js +0 -2
- package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/client/assets/main-Ci07m9oP.js +0 -152
- package/dist/client/assets/preview-diagram-utils-n_bbJA9V.js +0 -1
- package/dist/client/assets/quick-switcher-controller-CoiYvllm.js +0 -12
- package/dist/client/assets/room-CDrmIdDt.js +0 -1
- package/dist/client/assets/runtime-config-CDgeuPWN.js +0 -1
- package/dist/client/assets/runtime-config-loader-BHGniHP5.js +0 -1
- package/dist/client/assets/runtime-paths-DvpihCEq.js +0 -1
- package/dist/client/assets/segmented-control-DJrHSz8M.js +0 -1
- package/dist/client/assets/vault-api-client-sLB2bo56.js +0 -1
- package/dist/client/assets/vault-paths-CVkuh0j-.js +0 -5
- package/src/client/bootstrap/lazy-controller-feature.js +0 -433
- package/src/client/infrastructure/backlinks-api-client.js +0 -16
- package/src/client/infrastructure/plantuml-api-client.js +0 -19
- package/src/client/infrastructure/runtime-config-loader.js +0 -56
- package/src/server/infrastructure/git/command-runner.js +0 -49
- package/src/server/infrastructure/git/errors.js +0 -8
- package/src/server/infrastructure/http/create-esm-proxy-handler.js +0 -130
|
@@ -1,36 +1,49 @@
|
|
|
1
1
|
export class BrowserNotificationPort {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this.NotificationImpl = NotificationImpl;
|
|
7
|
-
this.focusWindow = focusWindow;
|
|
2
|
+
getApi() {
|
|
3
|
+
return typeof globalThis.Notification === 'function'
|
|
4
|
+
? globalThis.Notification
|
|
5
|
+
: null;
|
|
8
6
|
}
|
|
9
7
|
|
|
10
8
|
getPermission() {
|
|
11
|
-
return
|
|
12
|
-
? this.NotificationImpl.permission
|
|
13
|
-
: 'unsupported';
|
|
9
|
+
return this.getApi()?.permission ?? 'unsupported';
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
async requestPermission() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const api = this.getApi();
|
|
14
|
+
if (!api || typeof api.requestPermission !== 'function') {
|
|
15
|
+
return 'unsupported';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (api.permission !== 'default') {
|
|
19
|
+
return api.permission;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return await api.requestPermission();
|
|
24
|
+
} catch {
|
|
25
|
+
return api.permission ?? 'default';
|
|
26
|
+
}
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
show({ body, onClick, tag, title } = {}) {
|
|
30
|
+
const api = this.getApi();
|
|
31
|
+
if (!api || api.permission !== 'granted' || !body) {
|
|
24
32
|
return null;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
try {
|
|
36
|
+
const notification = new api(title, {
|
|
37
|
+
body,
|
|
38
|
+
renotify: true,
|
|
39
|
+
tag,
|
|
32
40
|
});
|
|
41
|
+
if (onClick) {
|
|
42
|
+
notification.onclick = onClick;
|
|
43
|
+
}
|
|
44
|
+
return notification;
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
33
47
|
}
|
|
34
|
-
return notification;
|
|
35
48
|
}
|
|
36
49
|
}
|
|
@@ -15,16 +15,20 @@ function writeStorage(storage, key, value) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const MAX_RECENT_FILES = 20;
|
|
19
|
+
|
|
18
20
|
export class BrowserPreferencesPort {
|
|
19
21
|
constructor({
|
|
20
|
-
|
|
22
|
+
fileTreeShowExtensionsKey,
|
|
21
23
|
lineWrappingKey,
|
|
24
|
+
recentFilesKey = 'collabmd-recent-files',
|
|
22
25
|
sidebarVisibleKey,
|
|
23
26
|
userNameKey,
|
|
24
27
|
storage = globalThis.localStorage,
|
|
25
28
|
}) {
|
|
26
|
-
this.
|
|
29
|
+
this.fileTreeShowExtensionsKey = fileTreeShowExtensionsKey;
|
|
27
30
|
this.lineWrappingKey = lineWrappingKey;
|
|
31
|
+
this.recentFilesKey = recentFilesKey;
|
|
28
32
|
this.sidebarVisibleKey = sidebarVisibleKey;
|
|
29
33
|
this.storage = storage;
|
|
30
34
|
this.userNameKey = userNameKey;
|
|
@@ -38,6 +42,14 @@ export class BrowserPreferencesPort {
|
|
|
38
42
|
writeStorage(this.storage, this.userNameKey, name);
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
getFileTreeShowExtensions() {
|
|
46
|
+
return readStorage(this.storage, this.fileTreeShowExtensionsKey, null) === 'true';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setFileTreeShowExtensions(showFileExtensions) {
|
|
50
|
+
writeStorage(this.storage, this.fileTreeShowExtensionsKey, showFileExtensions ? 'true' : 'false');
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
getLineWrappingEnabled() {
|
|
42
54
|
return readStorage(this.storage, this.lineWrappingKey, null) !== 'false';
|
|
43
55
|
}
|
|
@@ -46,6 +58,28 @@ export class BrowserPreferencesPort {
|
|
|
46
58
|
writeStorage(this.storage, this.lineWrappingKey, String(enabled));
|
|
47
59
|
}
|
|
48
60
|
|
|
61
|
+
getRecentFiles() {
|
|
62
|
+
const stored = readStorage(this.storage, this.recentFilesKey, '[]');
|
|
63
|
+
try {
|
|
64
|
+
const recentFiles = JSON.parse(stored);
|
|
65
|
+
return Array.isArray(recentFiles)
|
|
66
|
+
? [...new Set(recentFiles.filter((filePath) => typeof filePath === 'string' && filePath))]
|
|
67
|
+
: [];
|
|
68
|
+
} catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
recordRecentFile(filePath) {
|
|
74
|
+
if (!filePath) return;
|
|
75
|
+
|
|
76
|
+
const recentFiles = [
|
|
77
|
+
filePath,
|
|
78
|
+
...this.getRecentFiles().filter((recentFilePath) => recentFilePath !== filePath),
|
|
79
|
+
];
|
|
80
|
+
writeStorage(this.storage, this.recentFilesKey, JSON.stringify(recentFiles.slice(0, MAX_RECENT_FILES)));
|
|
81
|
+
}
|
|
82
|
+
|
|
49
83
|
getSidebarVisible() {
|
|
50
84
|
return readStorage(this.storage, this.sidebarVisibleKey, null);
|
|
51
85
|
}
|
|
@@ -54,11 +88,4 @@ export class BrowserPreferencesPort {
|
|
|
54
88
|
writeStorage(this.storage, this.sidebarVisibleKey, showSidebar ? 'true' : 'false');
|
|
55
89
|
}
|
|
56
90
|
|
|
57
|
-
getChatNotificationsEnabled() {
|
|
58
|
-
return readStorage(this.storage, this.chatNotificationsKey, null) === 'true';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
setChatNotificationsEnabled(enabled) {
|
|
62
|
-
writeStorage(this.storage, this.chatNotificationsKey, String(enabled));
|
|
63
|
-
}
|
|
64
91
|
}
|
|
@@ -20,7 +20,14 @@ import {
|
|
|
20
20
|
syntaxHighlighting,
|
|
21
21
|
} from '@codemirror/language';
|
|
22
22
|
import { languages } from '@codemirror/language-data';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
findNext,
|
|
25
|
+
getSearchQuery,
|
|
26
|
+
highlightSelectionMatches,
|
|
27
|
+
openSearchPanel,
|
|
28
|
+
searchKeymap,
|
|
29
|
+
searchPanelOpen,
|
|
30
|
+
} from '@codemirror/search';
|
|
24
31
|
import { Compartment, EditorSelection, EditorState, Prec, StateEffect, StateField } from '@codemirror/state';
|
|
25
32
|
import { oneDark } from '@codemirror/theme-one-dark';
|
|
26
33
|
import {
|
|
@@ -54,10 +61,82 @@ const REMOTE_UPDATE_FLASH_DURATION_MS = 1350;
|
|
|
54
61
|
const REMOTE_UPDATE_CARET_MAX_LENGTH = 160;
|
|
55
62
|
const RECENT_LOCAL_INPUT_WINDOW_MS = 900;
|
|
56
63
|
const TASK_LIST_MARKER_PATTERN = /^(\s*(?:>\s*)*(?:[-+*]|\d+[.)])\s+\[)( |x|X)(\])(\s.*)?$/;
|
|
64
|
+
|
|
65
|
+
function openSearchPanelPreservingViewport(view) {
|
|
66
|
+
const scrollTop = view.scrollDOM.scrollTop;
|
|
67
|
+
const selection = view.state.selection;
|
|
68
|
+
const applied = openSearchPanel(view);
|
|
69
|
+
|
|
70
|
+
if (applied) {
|
|
71
|
+
requestAnimationFrame(() => {
|
|
72
|
+
if (view.dom.isConnected && view.state.selection.eq(selection)) {
|
|
73
|
+
view.scrollDOM.scrollTop = scrollTop;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return applied;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getSearchQueryKey(query) {
|
|
82
|
+
return JSON.stringify([query.search, query.caseSensitive, query.regexp, query.wholeWord]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createSearchNavigationListener() {
|
|
86
|
+
let lastQueryKey = '';
|
|
87
|
+
let panelWasOpen = false;
|
|
88
|
+
let pendingFrame = 0;
|
|
89
|
+
|
|
90
|
+
return EditorView.updateListener.of((update) => {
|
|
91
|
+
const panelOpen = searchPanelOpen(update.state);
|
|
92
|
+
const query = getSearchQuery(update.state);
|
|
93
|
+
const queryKey = getSearchQueryKey(query);
|
|
94
|
+
const shouldReveal = panelOpen
|
|
95
|
+
&& query.valid
|
|
96
|
+
&& (!panelWasOpen || queryKey !== lastQueryKey);
|
|
97
|
+
|
|
98
|
+
panelWasOpen = panelOpen;
|
|
99
|
+
lastQueryKey = queryKey;
|
|
100
|
+
|
|
101
|
+
if (!shouldReveal) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (pendingFrame) {
|
|
106
|
+
cancelAnimationFrame(pendingFrame);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pendingFrame = requestAnimationFrame(() => {
|
|
110
|
+
pendingFrame = 0;
|
|
111
|
+
if (!update.view.dom.isConnected) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const currentQuery = getSearchQuery(update.view.state);
|
|
116
|
+
if (
|
|
117
|
+
!searchPanelOpen(update.view.state)
|
|
118
|
+
|| !currentQuery.valid
|
|
119
|
+
|| getSearchQueryKey(currentQuery) !== queryKey
|
|
120
|
+
) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const searchInput = update.view.dom.querySelector('.cm-search input[name="search"]');
|
|
125
|
+
const inputSelection = searchInput === update.view.root.activeElement && searchInput
|
|
126
|
+
? { end: searchInput.selectionEnd, start: searchInput.selectionStart }
|
|
127
|
+
: null;
|
|
128
|
+
findNext(update.view);
|
|
129
|
+
if (inputSelection && searchInput?.setSelectionRange) {
|
|
130
|
+
searchInput.setSelectionRange(inputSelection.start, inputSelection.end);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
57
136
|
const EDITOR_COMMANDS = Object.freeze({
|
|
58
137
|
indentLess,
|
|
59
138
|
indentMore,
|
|
60
|
-
openSearch:
|
|
139
|
+
openSearch: openSearchPanelPreservingViewport,
|
|
61
140
|
redo,
|
|
62
141
|
undo,
|
|
63
142
|
});
|
|
@@ -398,11 +477,16 @@ export class EditorViewAdapter {
|
|
|
398
477
|
crosshairCursor(),
|
|
399
478
|
highlightActiveLine(),
|
|
400
479
|
highlightSelectionMatches(),
|
|
480
|
+
createSearchNavigationListener(),
|
|
401
481
|
keymap.of([
|
|
402
482
|
...closeBracketsKeymap,
|
|
403
483
|
...defaultKeymap,
|
|
404
484
|
...historyKeymap,
|
|
405
|
-
...searchKeymap
|
|
485
|
+
...searchKeymap.map((binding) => (
|
|
486
|
+
binding.run === openSearchPanel
|
|
487
|
+
? { ...binding, run: openSearchPanelPreservingViewport }
|
|
488
|
+
: binding
|
|
489
|
+
)),
|
|
406
490
|
...foldKeymap,
|
|
407
491
|
indentWithTab,
|
|
408
492
|
]),
|
|
@@ -42,6 +42,31 @@ export class VaultApiClient {
|
|
|
42
42
|
return parseApiResponse(response, 'Failed to read file');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
async readBacklinks(filePath, { signal } = {}) {
|
|
46
|
+
const response = await fetch(
|
|
47
|
+
resolveApiUrl(`/backlinks?file=${encodeURIComponent(filePath)}`),
|
|
48
|
+
{ signal },
|
|
49
|
+
);
|
|
50
|
+
const data = await parseApiResponse(response, 'Failed to load backlinks');
|
|
51
|
+
return Array.isArray(data.backlinks) ? data.backlinks : [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async renderSvg(source) {
|
|
55
|
+
const response = await fetch(resolveApiUrl('/plantuml/render'), {
|
|
56
|
+
body: JSON.stringify({ source }),
|
|
57
|
+
headers: {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
},
|
|
60
|
+
method: 'POST',
|
|
61
|
+
});
|
|
62
|
+
const data = await parseApiResponse(response, 'Failed to render PlantUML');
|
|
63
|
+
if (!data.ok || typeof data.svg !== 'string') {
|
|
64
|
+
throw new Error(data.error || 'Failed to render PlantUML');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return data.svg;
|
|
68
|
+
}
|
|
69
|
+
|
|
45
70
|
async searchText({ limit = 50, query = '', signal = null } = {}) {
|
|
46
71
|
const searchParams = new URLSearchParams({
|
|
47
72
|
limit: String(limit),
|
|
@@ -43,11 +43,18 @@ function createNode(entry) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const node = {
|
|
47
47
|
name: entry.name,
|
|
48
48
|
path: entry.path,
|
|
49
49
|
type: entry.type,
|
|
50
50
|
};
|
|
51
|
+
|
|
52
|
+
const mtimeMs = Number(entry.mtimeMs);
|
|
53
|
+
if (Number.isFinite(mtimeMs)) {
|
|
54
|
+
node.mtimeMs = mtimeMs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return node;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
function sortNodes(nodes = []) {
|
|
@@ -184,6 +191,11 @@ class WorkspaceTreeModel {
|
|
|
184
191
|
node.name = nextNode.name;
|
|
185
192
|
node.path = nextNode.path;
|
|
186
193
|
node.type = nextNode.type;
|
|
194
|
+
if (nextNode.mtimeMs === undefined) {
|
|
195
|
+
delete node.mtimeMs;
|
|
196
|
+
} else {
|
|
197
|
+
node.mtimeMs = nextNode.mtimeMs;
|
|
198
|
+
}
|
|
187
199
|
if (node.type === 'directory' && !Array.isArray(node.children)) {
|
|
188
200
|
node.children = [];
|
|
189
201
|
}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { stripVaultFileExtension } from '../../domain/file-kind.js';
|
|
2
2
|
import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
|
|
3
|
+
import {
|
|
4
|
+
createCommentOverviewThread,
|
|
5
|
+
formatAnchorLabel,
|
|
6
|
+
} from './comment-ui/comment-ui-shared.js';
|
|
3
7
|
|
|
4
8
|
function asArray(value) {
|
|
5
9
|
return Array.isArray(value) ? value : [];
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
function formatLineLabel(anchor = {}) {
|
|
9
|
-
const startLine = Number(anchor.startLine || 0);
|
|
10
|
-
const endLine = Number(anchor.endLine || startLine);
|
|
11
|
-
if (!Number.isFinite(startLine) || startLine <= 0) {
|
|
12
|
-
return 'No source anchor';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return startLine === endLine ? `Line ${startLine}` : `Lines ${startLine}-${endLine}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
function createOverviewState({ copy, title, tone = '' }) {
|
|
19
13
|
const state = document.createElement('div');
|
|
20
14
|
state.className = 'comment-overview-empty ui-empty-state ui-empty-state--compact';
|
|
@@ -80,7 +74,7 @@ export class CommentOverviewController {
|
|
|
80
74
|
try {
|
|
81
75
|
const payload = await this.vaultApiClient.readCommentOverview();
|
|
82
76
|
this.errorMessage = '';
|
|
83
|
-
this.setOverview(payload?.overview ?? payload ?? {});
|
|
77
|
+
this.setOverview(payload?.overview ?? payload ?? {}, { render: false });
|
|
84
78
|
} catch (error) {
|
|
85
79
|
console.error('[comments] Failed to load comment overview:', error.message);
|
|
86
80
|
this.errorMessage = 'Unable to load open comments.';
|
|
@@ -95,7 +89,7 @@ export class CommentOverviewController {
|
|
|
95
89
|
return this.refreshPromise;
|
|
96
90
|
}
|
|
97
91
|
|
|
98
|
-
setOverview(overview = {}) {
|
|
92
|
+
setOverview(overview = {}, { render = true } = {}) {
|
|
99
93
|
this.errorMessage = '';
|
|
100
94
|
this.overview = {
|
|
101
95
|
files: asArray(overview.files),
|
|
@@ -105,7 +99,9 @@ export class CommentOverviewController {
|
|
|
105
99
|
this.onOverviewChange?.(this.overview, {
|
|
106
100
|
threadCounts: this.getThreadCounts(),
|
|
107
101
|
});
|
|
108
|
-
|
|
102
|
+
if (render) {
|
|
103
|
+
this.render();
|
|
104
|
+
}
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
render() {
|
|
@@ -187,9 +183,15 @@ export class CommentOverviewController {
|
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
createThreadButton(filePath, thread) {
|
|
190
|
-
const
|
|
191
|
-
button
|
|
192
|
-
|
|
186
|
+
const messageCount = Number(thread.messageCount || 0);
|
|
187
|
+
const button = createCommentOverviewThread({
|
|
188
|
+
authorName: thread.latestMessage?.userName || thread.createdByName || 'Anonymous',
|
|
189
|
+
lineLabel: formatAnchorLabel(thread.anchor),
|
|
190
|
+
messageCount,
|
|
191
|
+
previewBody: thread.latestMessage?.bodyPreview || '',
|
|
192
|
+
quote: thread.anchor?.quote || 'Source anchored comment',
|
|
193
|
+
timestamp: this.formatTimestamp(thread.latestActivityAt),
|
|
194
|
+
});
|
|
193
195
|
button.addEventListener('click', () => {
|
|
194
196
|
this.onThreadSelect?.({
|
|
195
197
|
anchor: thread.anchor,
|
|
@@ -197,43 +199,6 @@ export class CommentOverviewController {
|
|
|
197
199
|
threadId: thread.id,
|
|
198
200
|
});
|
|
199
201
|
});
|
|
200
|
-
|
|
201
|
-
const header = document.createElement('div');
|
|
202
|
-
header.className = 'comment-overview-thread-header';
|
|
203
|
-
|
|
204
|
-
const line = document.createElement('span');
|
|
205
|
-
line.className = 'comment-overview-thread-line';
|
|
206
|
-
line.textContent = formatLineLabel(thread.anchor);
|
|
207
|
-
|
|
208
|
-
const meta = document.createElement('span');
|
|
209
|
-
meta.className = 'comment-overview-thread-meta';
|
|
210
|
-
meta.textContent = this.formatTimestamp(thread.latestActivityAt);
|
|
211
|
-
header.append(line, meta);
|
|
212
|
-
|
|
213
|
-
const body = document.createElement('div');
|
|
214
|
-
body.className = 'comment-overview-thread-body';
|
|
215
|
-
|
|
216
|
-
const quote = document.createElement('span');
|
|
217
|
-
quote.className = 'comment-overview-thread-quote';
|
|
218
|
-
quote.textContent = thread.anchor?.quote || 'Source anchored comment';
|
|
219
|
-
|
|
220
|
-
const preview = document.createElement('p');
|
|
221
|
-
preview.className = 'comment-overview-thread-preview';
|
|
222
|
-
preview.textContent = thread.latestMessage?.bodyPreview || '';
|
|
223
|
-
body.append(preview, quote);
|
|
224
|
-
|
|
225
|
-
const footer = document.createElement('div');
|
|
226
|
-
footer.className = 'comment-overview-thread-footer';
|
|
227
|
-
const author = document.createElement('span');
|
|
228
|
-
author.className = 'comment-overview-thread-author';
|
|
229
|
-
author.textContent = thread.latestMessage?.userName || thread.createdByName || 'Anonymous';
|
|
230
|
-
const replies = document.createElement('span');
|
|
231
|
-
replies.className = 'comment-overview-thread-message-count';
|
|
232
|
-
const messageCount = Number(thread.messageCount || 0);
|
|
233
|
-
replies.textContent = `${messageCount} message${messageCount === 1 ? '' : 's'}`;
|
|
234
|
-
footer.append(author, replies);
|
|
235
|
-
|
|
236
|
-
button.append(header, body, footer);
|
|
237
202
|
return button;
|
|
238
203
|
}
|
|
239
204
|
|
|
@@ -360,17 +360,6 @@ function createThreadElement(thread) {
|
|
|
360
360
|
const actions = document.createElement('div');
|
|
361
361
|
actions.className = 'ui-record-actions comment-thread-card-actions';
|
|
362
362
|
|
|
363
|
-
const jump = document.createElement('button');
|
|
364
|
-
jump.type = 'button';
|
|
365
|
-
jump.className = buttonClassNames({
|
|
366
|
-
variant: 'ghost',
|
|
367
|
-
size: 'compact',
|
|
368
|
-
pill: true,
|
|
369
|
-
extra: ['ui-action-pill', 'comment-thread-card-action'],
|
|
370
|
-
});
|
|
371
|
-
jump.textContent = 'Jump';
|
|
372
|
-
jump.addEventListener('click', () => this.onNavigateToLine?.(thread.anchor?.startLine ?? 1));
|
|
373
|
-
|
|
374
363
|
const reply = document.createElement('button');
|
|
375
364
|
reply.type = 'button';
|
|
376
365
|
reply.className = buttonClassNames({
|
|
@@ -409,7 +398,7 @@ function createThreadElement(thread) {
|
|
|
409
398
|
await this.onResolveThread?.(thread.id);
|
|
410
399
|
});
|
|
411
400
|
|
|
412
|
-
actions.append(
|
|
401
|
+
actions.append(reply, resolve);
|
|
413
402
|
|
|
414
403
|
thread.messages.forEach((message, index) => {
|
|
415
404
|
article.appendChild(this.createMessageElement(thread, message, {
|