collabmd 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/client/assets/{drawio-editor-Br4qyh3r.js → drawio-editor-BMTwP5mD.js} +1 -1
  2. package/dist/client/assets/{drawioEditor-ClmnTx5J.js → drawioEditor-D6clB5OH.js} +2 -2
  3. package/dist/client/assets/{editor-session-Cf-iV5-3.js → editor-session-Dzy_85Wp.js} +1 -1
  4. package/dist/client/assets/{excalidraw-editor-CXAxVhlw.js → excalidraw-editor-CAP9F8CB.js} +3 -3
  5. package/dist/client/assets/{excalidrawEditor-DoH_kMdu.js → excalidrawEditor-DD4AvzYP.js} +2 -2
  6. package/dist/client/assets/{exportDocument-9wTrBZNS.css → exportDocument-BbkN85c4.css} +1 -1
  7. package/dist/client/assets/index-BIt5EZXz.css +1 -0
  8. package/dist/client/assets/{index-C8QZYHvV.js → index-BLrtj9YM.js} +2 -2
  9. package/dist/client/assets/main-CfSHaGes.js +1368 -0
  10. package/dist/client/assets/{vault-api-client-Bf9tB_GW.js → vault-api-client-p7h7cXKM.js} +1 -1
  11. package/dist/client/drawio-editor.html +1 -1
  12. package/dist/client/excalidraw-editor.html +1 -1
  13. package/dist/client/export-document.html +2 -2
  14. package/dist/client/index.html +2 -2
  15. package/package.json +6 -5
  16. package/src/client/application/app-shell/git-feature.js +44 -36
  17. package/src/client/application/app-shell/ui-feature-shell.js +84 -63
  18. package/src/client/bootstrap/collabmd-app-shell.js +6 -0
  19. package/src/client/infrastructure/editor-session.js +4 -0
  20. package/src/client/infrastructure/editor-view-adapter.js +23 -0
  21. package/src/client/infrastructure/vault-api-client.js +48 -0
  22. package/src/client/presentation/bases-preview-controller.js +1388 -28
  23. package/src/client/presentation/comments-panel.js +50 -5
  24. package/src/client/presentation/excalidraw-embed-controller.js +4 -1
  25. package/src/client/presentation/file-history-view-controller.js +9 -1
  26. package/src/client/presentation/git-panel-controller.js +101 -91
  27. package/src/client/styles/components/scrollbars.css +5 -0
  28. package/src/client/styles/features/preview-markdown.css +468 -23
  29. package/src/server/domain/backlink-index.js +202 -11
  30. package/src/server/domain/bases/base-definition.js +138 -11
  31. package/src/server/domain/bases/base-expression-runtime.js +54 -17
  32. package/src/server/domain/bases/base-index-snapshot-store.js +167 -20
  33. package/src/server/domain/bases/base-query-metadata.js +242 -0
  34. package/src/server/domain/bases/base-query-results.js +18 -8
  35. package/src/server/domain/bases/base-query-service.js +360 -49
  36. package/src/server/domain/bases/base-transform.js +116 -0
  37. package/src/server/infrastructure/http/create-request-handler.js +63 -28
  38. package/src/server/infrastructure/http/create-vault-api-command-handler.js +275 -256
  39. package/src/server/infrastructure/http/create-vault-api-query-handler.js +258 -184
  40. package/src/server/infrastructure/persistence/vault-file-store.js +68 -12
  41. package/dist/client/assets/index-CxSbUTs2.css +0 -1
  42. package/dist/client/assets/main-BjHHMlv0.js +0 -1249
  43. /package/dist/client/assets/{exportDocument-Bia2hI25.js → exportDocument-Zfwq1XVx.js} +0 -0
@@ -37,6 +37,47 @@ function sortThreads(threads = []) {
37
37
  ));
38
38
  }
39
39
 
40
+ export function countThreadsForSourceBlocks(blocks = [], threads = []) {
41
+ const normalizedBlocks = blocks
42
+ .map((block, index) => {
43
+ const startLine = Number(block?.startLine);
44
+ const endLine = Number(block?.endLine ?? startLine);
45
+ if (!Number.isFinite(startLine) || startLine <= 0) {
46
+ return null;
47
+ }
48
+
49
+ return {
50
+ endExclusive: Math.max(endLine, startLine + 1),
51
+ index,
52
+ startLine,
53
+ };
54
+ })
55
+ .filter(Boolean)
56
+ .sort((left, right) => (
57
+ left.startLine - right.startLine
58
+ || left.endExclusive - right.endExclusive
59
+ || left.index - right.index
60
+ ));
61
+ const counts = new Array(blocks.length).fill(0);
62
+ let threadIndex = 0;
63
+
64
+ normalizedBlocks.forEach((block) => {
65
+ while (threadIndex < threads.length && (threads[threadIndex]?.anchor?.startLine ?? 0) < block.startLine) {
66
+ threadIndex += 1;
67
+ }
68
+
69
+ let countIndex = threadIndex;
70
+ while (countIndex < threads.length && (threads[countIndex]?.anchor?.startLine ?? 0) < block.endExclusive) {
71
+ countIndex += 1;
72
+ }
73
+
74
+ counts[block.index] = countIndex - threadIndex;
75
+ threadIndex = countIndex;
76
+ });
77
+
78
+ return counts;
79
+ }
80
+
40
81
  export class CommentsPanel {
41
82
  constructor({
42
83
  panelElement,
@@ -236,18 +277,22 @@ export class CommentsPanel {
236
277
 
237
278
  const blocks = Array.from(this.previewElement.querySelectorAll('[data-source-line]'))
238
279
  .filter((element) => isLeafSourceBlock(element) && isCommentablePreviewBlock(element));
280
+ const threadCounts = countThreadsForSourceBlocks(
281
+ blocks.map((block) => ({
282
+ endLine: parseLineNumber(block.getAttribute('data-source-line-end')) ?? parseLineNumber(block.getAttribute('data-source-line')) ?? 1,
283
+ startLine: parseLineNumber(block.getAttribute('data-source-line')),
284
+ })),
285
+ this.threads,
286
+ );
239
287
 
240
- blocks.forEach((block) => {
288
+ blocks.forEach((block, index) => {
241
289
  const startLine = parseLineNumber(block.getAttribute('data-source-line'));
242
290
  const endLine = parseLineNumber(block.getAttribute('data-source-line-end')) ?? (startLine ?? 1);
243
291
  if (!startLine) {
244
292
  return;
245
293
  }
246
294
 
247
- const matchingThreads = this.threads.filter((thread) => (
248
- thread.anchor?.startLine >= startLine && thread.anchor?.startLine < Math.max(endLine, startLine + 1)
249
- ));
250
- const totalCount = matchingThreads.length;
295
+ const totalCount = threadCounts[index] ?? 0;
251
296
  const button = document.createElement('button');
252
297
  button.type = 'button';
253
298
  button.className = 'comment-anchor-btn';
@@ -153,6 +153,7 @@ export class ExcalidrawEmbedController {
153
153
  this.hydrationIdleId = null;
154
154
  this.hydrationQueue = [];
155
155
  this.hydrationInProgress = false;
156
+ this._exitMaximizedEmbed();
156
157
  if (this.overlayRoot) {
157
158
  this.overlayRoot.hidden = true;
158
159
  }
@@ -762,7 +763,9 @@ export class ExcalidrawEmbedController {
762
763
  enterMaximize();
763
764
  }
764
765
  });
765
- openBtn.addEventListener('click', () => {
766
+ openBtn.addEventListener('click', (event) => {
767
+ event.preventDefault();
768
+ this._exitMaximizedEmbed();
766
769
  this.onOpenFile?.(entry.filePath);
767
770
  });
768
771
 
@@ -516,9 +516,15 @@ export class FileHistoryViewController {
516
516
  additions: Number(selectedEntry.additions || 0),
517
517
  deletions: Number(selectedEntry.deletions || 0),
518
518
  } : null);
519
+ const hasSelection = Boolean(selectedEntry);
520
+
521
+ this.syncToolbarIndicators(selectedEntry, selectedSummary, hasSelection);
522
+ this.syncToolbarButtonStates(hasSelection);
523
+ }
524
+
525
+ syncToolbarIndicators(selectedEntry, selectedSummary, hasSelection) {
519
526
  const additions = Number(selectedSummary?.additions || 0);
520
527
  const deletions = Number(selectedSummary?.deletions || 0);
521
- const hasSelection = Boolean(selectedEntry);
522
528
 
523
529
  if (this.fileIndicator) {
524
530
  this.fileIndicator.textContent = selectedEntry?.type === 'commit'
@@ -535,7 +541,9 @@ export class FileHistoryViewController {
535
541
  `;
536
542
  this.stats.classList.toggle('hidden', !hasSelection);
537
543
  }
544
+ }
538
545
 
546
+ syncToolbarButtonStates(hasSelection) {
539
547
  this.backToHistoryButton?.classList.add('hidden');
540
548
  this.gitActionsGroup?.classList.add('hidden');
541
549
  this.layoutToggle?.classList.add('hidden');
@@ -149,115 +149,125 @@ export class GitPanelController {
149
149
  };
150
150
  }
151
151
 
152
- initialize() {
153
- this.panel?.addEventListener('click', (event) => {
154
- const modeButton = event.target instanceof Element
155
- ? event.target.closest('[data-git-panel-mode]')
156
- : null;
157
- if (modeButton) {
158
- const nextMode = modeButton.getAttribute('data-git-panel-mode');
159
- if (nextMode === 'changes' || nextMode === 'history') {
160
- void this.setMode(nextMode);
161
- }
162
- return;
152
+ _handlePanelModeClick(event) {
153
+ const modeButton = event.target instanceof Element
154
+ ? event.target.closest('[data-git-panel-mode]')
155
+ : null;
156
+ if (modeButton) {
157
+ const nextMode = modeButton.getAttribute('data-git-panel-mode');
158
+ if (nextMode === 'changes' || nextMode === 'history') {
159
+ void this.setMode(nextMode);
163
160
  }
161
+ return true;
162
+ }
163
+ return false;
164
+ }
164
165
 
165
- const toggleButton = event.target instanceof Element
166
- ? event.target.closest('[data-git-section-toggle]')
167
- : null;
168
- if (toggleButton) {
169
- const sectionKey = toggleButton.getAttribute('data-git-section-toggle');
170
- this.toggleSection(sectionKey);
171
- return;
172
- }
166
+ _handleSectionToggleClick(event) {
167
+ const toggleButton = event.target instanceof Element
168
+ ? event.target.closest('[data-git-section-toggle]')
169
+ : null;
170
+ if (toggleButton) {
171
+ this.toggleSection(toggleButton.getAttribute('data-git-section-toggle'));
172
+ return true;
173
+ }
174
+ return false;
175
+ }
173
176
 
174
- const historyButton = event.target instanceof Element
175
- ? event.target.closest('[data-git-commit-hash]')
176
- : null;
177
- if (historyButton && !event.target.closest('[data-git-history-load-more]')) {
178
- const hash = historyButton.getAttribute('data-git-commit-hash');
179
- if (!hash) {
180
- return;
181
- }
177
+ _handleCommitHistoryClick(event) {
178
+ const historyButton = event.target instanceof Element
179
+ ? event.target.closest('[data-git-commit-hash]')
180
+ : null;
181
+ if (historyButton && !event.target.closest('[data-git-history-load-more]')) {
182
+ const hash = historyButton.getAttribute('data-git-commit-hash');
183
+ if (hash) {
182
184
  this.onSelectCommit(hash, { path: this.selection.commitHash === hash ? this.selection.path : null });
183
- return;
184
185
  }
186
+ return true;
187
+ }
188
+ return false;
189
+ }
185
190
 
186
- const loadMoreButton = event.target instanceof Element
187
- ? event.target.closest('[data-git-history-load-more]')
188
- : null;
189
- if (loadMoreButton) {
190
- void this.loadMoreHistory();
191
- return;
191
+ _handleFileSelectionClick(event) {
192
+ const fileButton = event.target instanceof Element
193
+ ? event.target.closest('[data-git-path]')
194
+ : null;
195
+ if (fileButton && !event.target.closest('[data-git-file-action]')) {
196
+ const filePath = fileButton.getAttribute('data-git-path');
197
+ if (filePath) {
198
+ this.onSelectDiff(filePath, { scope: fileButton.getAttribute('data-git-scope') || 'working-tree' });
192
199
  }
200
+ return true;
201
+ }
202
+ return false;
203
+ }
193
204
 
194
- const fileButton = event.target instanceof Element
195
- ? event.target.closest('[data-git-path]')
196
- : null;
197
- if (fileButton && !event.target.closest('[data-git-file-action]')) {
198
- const filePath = fileButton.getAttribute('data-git-path');
199
- const scope = fileButton.getAttribute('data-git-scope') || 'working-tree';
200
- if (!filePath) {
201
- return;
202
- }
203
- this.onSelectDiff(filePath, { scope });
204
- return;
205
+ _handleFileActionClick(event) {
206
+ const actionButton = event.target instanceof Element
207
+ ? event.target.closest('[data-git-file-action]')
208
+ : null;
209
+ if (actionButton) {
210
+ const action = actionButton.getAttribute('data-git-file-action');
211
+ const filePath = actionButton.getAttribute('data-git-path');
212
+ if (action && filePath) {
213
+ event.preventDefault();
214
+ event.stopPropagation();
215
+ void this.handleFileAction(action, filePath, actionButton.getAttribute('data-git-scope') || 'working-tree');
205
216
  }
217
+ return true;
218
+ }
219
+ return false;
220
+ }
206
221
 
207
- const pullBackupButton = event.target instanceof Element
208
- ? event.target.closest('[data-git-pull-backup-path]')
209
- : null;
210
- if (pullBackupButton) {
211
- const summaryPath = pullBackupButton.getAttribute('data-git-pull-backup-path');
212
- if (!summaryPath) {
213
- return;
214
- }
222
+ _handlePanelClick(event) {
223
+ if (this._handlePanelModeClick(event)) return;
224
+ if (this._handleSectionToggleClick(event)) return;
225
+ if (this._handleCommitHistoryClick(event)) return;
226
+
227
+ if (event.target instanceof Element && event.target.closest('[data-git-history-load-more]')) {
228
+ void this.loadMoreHistory();
229
+ return;
230
+ }
231
+
232
+ if (this._handleFileSelectionClick(event)) return;
233
+
234
+ const pullBackupButton = event.target instanceof Element
235
+ ? event.target.closest('[data-git-pull-backup-path]')
236
+ : null;
237
+ if (pullBackupButton) {
238
+ const summaryPath = pullBackupButton.getAttribute('data-git-pull-backup-path');
239
+ if (summaryPath) {
215
240
  this.onOpenPullBackup(summaryPath);
216
- return;
217
241
  }
242
+ return;
243
+ }
218
244
 
219
- const actionButton = event.target instanceof Element
220
- ? event.target.closest('[data-git-file-action]')
221
- : null;
222
- if (actionButton) {
223
- const action = actionButton.getAttribute('data-git-file-action');
224
- const filePath = actionButton.getAttribute('data-git-path');
225
- const scope = actionButton.getAttribute('data-git-scope') || 'working-tree';
226
- if (!action || !filePath) {
227
- return;
228
- }
245
+ if (this._handleFileActionClick(event)) return;
229
246
 
230
- event.preventDefault();
231
- event.stopPropagation();
232
- void this.handleFileAction(action, filePath, scope);
233
- return;
234
- }
247
+ if (event.target instanceof Element && event.target.closest('[data-git-view-all]')) {
248
+ this.onViewAllDiff();
249
+ return;
250
+ }
235
251
 
236
- const viewAllButton = event.target instanceof Element
237
- ? event.target.closest('[data-git-view-all]')
238
- : null;
239
- if (viewAllButton) {
240
- this.onViewAllDiff();
241
- return;
242
- }
252
+ if (event.target instanceof Element && event.target.closest('[data-git-commit-staged]')) {
253
+ void this.handleCommitStaged();
254
+ return;
255
+ }
243
256
 
244
- const commitStagedButton = event.target instanceof Element
245
- ? event.target.closest('[data-git-commit-staged]')
246
- : null;
247
- if (commitStagedButton) {
248
- void this.handleCommitStaged();
249
- return;
257
+ const syncButton = event.target instanceof Element
258
+ ? event.target.closest('[data-git-sync-action]')
259
+ : null;
260
+ if (syncButton) {
261
+ const action = syncButton.getAttribute('data-git-sync-action');
262
+ if (action === 'pull' || action === 'push') {
263
+ void this.handleSyncAction(action);
250
264
  }
265
+ }
266
+ }
251
267
 
252
- const syncButton = event.target instanceof Element
253
- ? event.target.closest('[data-git-sync-action]')
254
- : null;
255
- if (syncButton) {
256
- const action = syncButton.getAttribute('data-git-sync-action');
257
- if (action === 'pull' || action === 'push') {
258
- void this.handleSyncAction(action);
259
- }
260
- }
268
+ initialize() {
269
+ this.panel?.addEventListener('click', (event) => {
270
+ this._handlePanelClick(event);
261
271
  });
262
272
 
263
273
  this.searchInput?.addEventListener('input', (event) => {
@@ -5,6 +5,7 @@
5
5
  .file-tree,
6
6
  .git-panel,
7
7
  .diff-scroll,
8
+ .backlinks-body,
8
9
  .comments-drawer-list,
9
10
  .comments-drawer-item-quote,
10
11
  .comment-card-quote,
@@ -26,6 +27,7 @@
26
27
  .file-tree::-webkit-scrollbar,
27
28
  .git-panel::-webkit-scrollbar,
28
29
  .diff-scroll::-webkit-scrollbar,
30
+ .backlinks-body::-webkit-scrollbar,
29
31
  .comments-drawer-list::-webkit-scrollbar,
30
32
  .comments-drawer-item-quote::-webkit-scrollbar,
31
33
  .comment-card-quote::-webkit-scrollbar,
@@ -47,6 +49,7 @@
47
49
  .file-tree::-webkit-scrollbar-track,
48
50
  .git-panel::-webkit-scrollbar-track,
49
51
  .diff-scroll::-webkit-scrollbar-track,
52
+ .backlinks-body::-webkit-scrollbar-track,
50
53
  .comments-drawer-list::-webkit-scrollbar-track,
51
54
  .comments-drawer-item-quote::-webkit-scrollbar-track,
52
55
  .comment-card-quote::-webkit-scrollbar-track,
@@ -67,6 +70,7 @@
67
70
  .file-tree::-webkit-scrollbar-thumb,
68
71
  .git-panel::-webkit-scrollbar-thumb,
69
72
  .diff-scroll::-webkit-scrollbar-thumb,
73
+ .backlinks-body::-webkit-scrollbar-thumb,
70
74
  .comments-drawer-list::-webkit-scrollbar-thumb,
71
75
  .comments-drawer-item-quote::-webkit-scrollbar-thumb,
72
76
  .comment-card-quote::-webkit-scrollbar-thumb,
@@ -88,6 +92,7 @@
88
92
  .file-tree::-webkit-scrollbar-thumb:hover,
89
93
  .git-panel::-webkit-scrollbar-thumb:hover,
90
94
  .diff-scroll::-webkit-scrollbar-thumb:hover,
95
+ .backlinks-body::-webkit-scrollbar-thumb:hover,
91
96
  .comments-drawer-list::-webkit-scrollbar-thumb:hover,
92
97
  .comments-drawer-item-quote::-webkit-scrollbar-thumb:hover,
93
98
  .comment-card-quote::-webkit-scrollbar-thumb:hover,