collabmd 0.1.47 → 0.1.48

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 (100) hide show
  1. package/README.md +9 -6
  2. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  3. package/dist/client/assets/babel-CY5KOv-I.js +16 -0
  4. package/dist/client/assets/comment-ui-shared-DuoyznXW.js +7 -0
  5. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  6. package/dist/client/assets/document-formatter-Dxl_a8XO.js +2 -0
  7. package/dist/client/assets/{drawio-editor-BjrxTe3l.js → drawio-editor-BVaNNtYp.js} +2 -2
  8. package/dist/client/assets/{drawioEditor-DUVZjSSr.js → drawioEditor-BkIBlXx0.js} +2 -2
  9. package/dist/client/assets/editor-session-DMyVdHLj.js +54 -0
  10. package/dist/client/assets/embedded-editor-base-B3l6Xy65.css +1 -0
  11. package/dist/client/assets/estree-CuT_pB8K.js +44 -0
  12. package/dist/client/assets/{excalidraw-editor-BlcN6umJ.css → excalidraw-editor-BbSbI80a.css} +1 -1
  13. package/dist/client/assets/excalidraw-editor-BjYaNEMY.js +9 -0
  14. package/dist/client/assets/{excalidrawEditor-B0jOK3lv.js → excalidrawEditor-ChqF_d82.js} +2 -2
  15. package/dist/client/assets/export-page-Dyu0XCbu.js +57 -0
  16. package/dist/client/assets/exportDocument-CJBx4Xjx.css +1 -0
  17. package/dist/client/assets/{exportDocument-Dg9MjkIk.js → exportDocument-Dgvfa2Sd.js} +2 -2
  18. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  19. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  20. package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
  21. package/dist/client/assets/html-Cdnr2qzU.js +25 -0
  22. package/dist/client/assets/index-CSNIMyOP.css +1 -0
  23. package/dist/client/assets/index-DG3N2Ps-.js +2 -0
  24. package/dist/client/assets/main-D0wCxpVn.js +903 -0
  25. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  26. package/dist/client/assets/mermaid.core-BJRLUQZE.js +11 -0
  27. package/dist/client/assets/{preview-render-compiler-BApvlLLo.js → preview-render-compiler-CrJIooVD.js} +2 -2
  28. package/dist/client/assets/preview-render-compiler-O0JxBVNF.js +1 -0
  29. package/dist/client/assets/{preview-render-profile-CE-5rVTC.js → preview-render-profile-B4RWYWHt.js} +1 -1
  30. package/dist/client/assets/{preview-render-worker-BmN79bVc.js → preview-render-worker-_QTxqKvP.js} +22 -22
  31. package/dist/client/assets/quick-switcher-controller-CaEjEtq_.js +1 -0
  32. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  33. package/dist/client/assets/{vault-api-client-_LpV1aIn.js → vault-api-client-BkwdFjx0.js} +2 -2
  34. package/dist/client/assets/{es-CAsWR6hG.js → vault-utils-DvoSftJT.js} +1 -1
  35. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  36. package/dist/client/drawio-editor.html +1 -1
  37. package/dist/client/excalidraw-editor.html +1 -1
  38. package/dist/client/export-document.html +2 -2
  39. package/dist/client/index.html +41 -19
  40. package/package.json +4 -1
  41. package/src/client/app/index.html +39 -17
  42. package/src/client/application/app-shell/export-feature.js +17 -1
  43. package/src/client/application/app-shell/git-feature.js +28 -9
  44. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  45. package/src/client/application/app-shell/ui-feature-shell.js +164 -9
  46. package/src/client/application/app-shell-elements.js +4 -0
  47. package/src/client/application/workspace-coordinator.js +21 -4
  48. package/src/client/application/workspace-preview-controller.js +120 -2
  49. package/src/client/application/workspace-route-controller.js +4 -1
  50. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  51. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  52. package/src/client/bootstrap/collabmd-app-shell.js +21 -5
  53. package/src/client/domain/document-formatter.js +193 -0
  54. package/src/client/domain/file-search.js +73 -0
  55. package/src/client/domain/git-labels.js +5 -0
  56. package/src/client/domain/vault-paths.js +6 -4
  57. package/src/client/domain/wiki-link-completions.js +20 -20
  58. package/src/client/drawio-editor.js +8 -3
  59. package/src/client/excalidraw-editor.js +152 -6
  60. package/src/client/export/export-host.js +56 -35
  61. package/src/client/export/export-page.js +14 -4
  62. package/src/client/export/export-pipeline.js +266 -25
  63. package/src/client/infrastructure/editor-session.js +4 -0
  64. package/src/client/infrastructure/editor-view-adapter.js +60 -14
  65. package/src/client/presentation/bases-preview-controller.js +35 -11
  66. package/src/client/presentation/file-action-controller.js +15 -2
  67. package/src/client/presentation/file-explorer-controller.js +3 -0
  68. package/src/client/presentation/file-explorer-view.js +2 -1
  69. package/src/client/presentation/file-history-view-controller.js +6 -1
  70. package/src/client/presentation/file-tree-state.js +1 -11
  71. package/src/client/presentation/git-diff-view-controller.js +36 -11
  72. package/src/client/presentation/git-panel-controller.js +10 -6
  73. package/src/client/presentation/quick-switcher-controller.js +54 -154
  74. package/src/client/styles/components/scrollbars.css +10 -0
  75. package/src/client/styles/export-document.css +44 -29
  76. package/src/client/styles/features/collaboration-presence.css +13 -1
  77. package/src/client/styles/features/preview-markdown.css +54 -0
  78. package/src/client/styles/foundation/tokens.css +8 -0
  79. package/src/client/styles/layout/shell.css +76 -24
  80. package/src/client/styles/layout/view-modes.css +2 -0
  81. package/src/client/styles/overrides/codemirror.css +31 -19
  82. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  83. package/src/domain/file-kind.js +12 -1
  84. package/src/server/domain/ripgrep-search-service.js +110 -37
  85. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  86. package/src/server/infrastructure/persistence/vault-file-store.js +2 -0
  87. package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +0 -7
  88. package/dist/client/assets/editor-session-DfZuRgB_.js +0 -54
  89. package/dist/client/assets/embedded-editor-base-DvMynhKu.css +0 -1
  90. package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +0 -9
  91. package/dist/client/assets/export-page-Bs3xE1fg.js +0 -57
  92. package/dist/client/assets/exportDocument-PnYU3v1s.css +0 -1
  93. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  94. package/dist/client/assets/index-BIpgxyGZ.css +0 -1
  95. package/dist/client/assets/index-Z5sI0Bj-.js +0 -2
  96. package/dist/client/assets/main-Dwnj9Gaq.js +0 -903
  97. package/dist/client/assets/mermaid.core-CLZBlpIW.js +0 -11
  98. package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +0 -1
  99. package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +0 -12
  100. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
@@ -30,6 +30,7 @@
30
30
  .editor-layout[data-view="preview"] .preview-content.is-base-file-preview,
31
31
  .editor-layout[data-view="preview"] .preview-content.is-image-file-preview,
32
32
  .editor-layout[data-view="preview"] .preview-content.is-pdf-file-preview,
33
+ .editor-layout[data-view="preview"] .preview-content.is-html-file-preview,
33
34
  .editor-layout[data-view="preview"] .preview-content.is-mermaid-file-preview,
34
35
  .editor-layout[data-view="preview"] .preview-content.is-plantuml-file-preview {
35
36
  max-width: none;
@@ -40,6 +41,7 @@
40
41
  }
41
42
 
42
43
  .editor-layout[data-view="preview"] .preview-content p:has(> .excalidraw-embed-placeholder:only-child),
44
+ .editor-layout[data-view="preview"] .preview-content p:has(> .drawio-embed-placeholder:only-child),
43
45
  .editor-layout[data-view="preview"] .preview-content p:has(> .bases-embed-placeholder:only-child),
44
46
  .editor-layout[data-view="preview"] .preview-content p:has(> .video-embed-placeholder:only-child),
45
47
  .editor-layout[data-view="preview"] .preview-content p:has(> .mermaid-shell:only-child),
@@ -1,55 +1,67 @@
1
1
  /* ===== OVERRIDES / CODEMIRROR ===== */
2
2
 
3
+ .editor-container .cm-content {
4
+ font-variant-ligatures: none;
5
+ }
6
+
3
7
  .cm-tooltip-autocomplete {
8
+ width: min(420px, calc(100vw - 24px));
9
+ max-width: calc(100vw - 24px);
4
10
  overflow: hidden;
5
- border: 1px solid var(--color-divider) !important;
6
- border-radius: var(--radius-md) !important;
7
- background: var(--color-surface) !important;
8
- box-shadow: var(--shadow-md) !important;
11
+ border: 1px solid var(--color-border) !important;
12
+ border-radius: var(--radius-lg) !important;
13
+ background: oklch(from var(--color-surface) l c h / 0.98) !important;
14
+ box-shadow: var(--shadow-lg) !important;
15
+ backdrop-filter: blur(12px);
9
16
  font-family: var(--font-body);
10
- font-size: var(--text-sm);
17
+ font-size: var(--text-ui-sm);
11
18
  }
12
19
 
13
20
  .cm-tooltip-autocomplete > ul {
14
21
  max-height: 240px !important;
15
- padding: var(--space-1) 0;
22
+ padding: var(--space-1);
16
23
  }
17
24
 
18
25
  .cm-tooltip-autocomplete > ul > li {
19
26
  display: flex;
20
27
  align-items: center;
21
28
  gap: var(--space-2);
29
+ min-height: 36px;
22
30
  padding: var(--space-2) var(--space-3) !important;
31
+ border-radius: var(--radius-md);
23
32
  line-height: 1.4;
24
33
  }
25
34
 
26
35
  .cm-tooltip-autocomplete > ul > li[aria-selected] {
27
- background: var(--color-primary) !important;
28
- color: var(--color-text-inverse) !important;
36
+ background: var(--color-surface-offset) !important;
37
+ color: var(--color-text) !important;
38
+ }
39
+
40
+ .cm-tooltip-autocomplete .cm-completionIcon {
41
+ display: none;
29
42
  }
30
43
 
31
44
  .cm-tooltip-autocomplete .cm-completionLabel {
32
45
  flex: 1;
33
46
  overflow: hidden;
47
+ font-family: var(--font-body) !important;
48
+ font-weight: 600;
34
49
  text-overflow: ellipsis;
35
50
  white-space: nowrap;
36
51
  }
37
52
 
38
53
  .cm-tooltip-autocomplete .cm-completionDetail {
54
+ max-width: 45%;
39
55
  margin-left: auto;
40
- color: inherit;
41
- opacity: 0.6;
56
+ overflow: hidden;
57
+ color: var(--color-text-faint);
58
+ direction: rtl;
59
+ text-align: left;
60
+ text-overflow: ellipsis;
42
61
  white-space: nowrap;
62
+ font-family: var(--font-body) !important;
43
63
  font-size: var(--text-xs);
44
- }
45
-
46
- .cm-tooltip-autocomplete > ul > li[aria-selected] .cm-completionDetail {
47
- opacity: 0.85;
48
- }
49
-
50
- .cm-completionIcon-text::after {
51
- content: '📄';
52
- font-size: var(--text-ui-sm);
64
+ font-style: normal;
53
65
  }
54
66
 
55
67
  .cm-panels-top {
@@ -38,6 +38,7 @@ body[data-surface='excalidraw-editor'][data-document-mode='preview'] .disable-vi
38
38
  pointer-events: none;
39
39
  }
40
40
 
41
+ .excalidraw-editor-toolbar,
41
42
  .diagram-comments-toolbar {
42
43
  display: inline-flex;
43
44
  align-items: center;
@@ -45,6 +46,47 @@ body[data-surface='excalidraw-editor'][data-document-mode='preview'] .disable-vi
45
46
  pointer-events: auto;
46
47
  }
47
48
 
49
+ .excalidraw-presentation-toggle {
50
+ width: var(--control-h-md);
51
+ min-width: var(--control-h-md);
52
+ height: var(--control-h-md);
53
+ }
54
+
55
+ .excalidraw-presentation-navigation {
56
+ position: absolute;
57
+ z-index: 30;
58
+ bottom: var(--space-4);
59
+ left: 50%;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: var(--space-2);
63
+ padding: var(--space-2);
64
+ border: 1px solid var(--color-border);
65
+ border-radius: var(--radius-lg);
66
+ background: var(--color-surface-raised);
67
+ box-shadow: var(--shadow-md);
68
+ color: var(--color-text);
69
+ transform: translateX(-50%);
70
+ }
71
+
72
+ .excalidraw-presentation-navigation .diagram-comment-button {
73
+ width: var(--control-h-sm);
74
+ min-width: var(--control-h-sm);
75
+ height: var(--control-h-sm);
76
+ padding: 0;
77
+ }
78
+
79
+ .excalidraw-presentation-status {
80
+ min-width: 7rem;
81
+ max-width: min(20rem, 55vw);
82
+ color: var(--color-text-muted);
83
+ font-size: var(--text-ui-sm);
84
+ text-align: center;
85
+ overflow: hidden;
86
+ text-overflow: ellipsis;
87
+ white-space: nowrap;
88
+ }
89
+
48
90
  .diagram-comment-button,
49
91
  .diagram-comment-close,
50
92
  .diagram-comment-back,
@@ -1,4 +1,5 @@
1
1
  const MARKDOWN_FILE_EXTENSIONS = Object.freeze(['.md', '.markdown', '.mdx']);
2
+ const HTML_FILE_EXTENSIONS = Object.freeze(['.html', '.htm']);
2
3
  const BASE_FILE_EXTENSION = '.base';
3
4
  const EXCALIDRAW_FILE_EXTENSION = '.excalidraw';
4
5
  const DRAWIO_FILE_EXTENSION = '.drawio';
@@ -17,12 +18,13 @@ const DIAGRAM_FILE_EXTENSIONS = Object.freeze([
17
18
  ]);
18
19
  const VAULT_FILE_EXTENSIONS = Object.freeze([
19
20
  ...MARKDOWN_FILE_EXTENSIONS,
21
+ ...HTML_FILE_EXTENSIONS,
20
22
  BASE_FILE_EXTENSION,
21
23
  ...DIAGRAM_FILE_EXTENSIONS,
22
24
  PDF_FILE_EXTENSION,
23
25
  ...IMAGE_ATTACHMENT_EXTENSIONS,
24
26
  ]);
25
- const STRIP_VAULT_EXTENSION_PATTERN = /\.(?:md|markdown|mdx|base|excalidraw|drawio|mmd|mermaid|puml|plantuml|dsl|pdf|png|jpe?g|webp|gif|svg)$/i;
27
+ const STRIP_VAULT_EXTENSION_PATTERN = /\.(?:md|markdown|mdx|html?|base|excalidraw|drawio|mmd|mermaid|puml|plantuml|dsl|pdf|png|jpe?g|webp|gif|svg)$/i;
26
28
 
27
29
  function normalizeFilePath(filePath) {
28
30
  return String(filePath ?? '').trim().toLowerCase();
@@ -38,6 +40,7 @@ export {
38
40
  DIAGRAM_FILE_EXTENSIONS,
39
41
  DRAWIO_FILE_EXTENSION,
40
42
  EXCALIDRAW_FILE_EXTENSION,
43
+ HTML_FILE_EXTENSIONS,
41
44
  IMAGE_ATTACHMENT_EXTENSIONS,
42
45
  MARKDOWN_FILE_EXTENSIONS,
43
46
  MERMAID_FILE_EXTENSIONS,
@@ -53,6 +56,10 @@ export function getVaultFileKind(filePath) {
53
56
  return 'markdown';
54
57
  }
55
58
 
59
+ if (hasFileExtension(filePath, HTML_FILE_EXTENSIONS)) {
60
+ return 'html';
61
+ }
62
+
56
63
  if (hasFileExtension(filePath, [BASE_FILE_EXTENSION])) {
57
64
  return 'base';
58
65
  }
@@ -114,6 +121,10 @@ export function isMarkdownFilePath(filePath) {
114
121
  return getVaultFileKind(filePath) === 'markdown';
115
122
  }
116
123
 
124
+ export function isHtmlFilePath(filePath) {
125
+ return getVaultFileKind(filePath) === 'html';
126
+ }
127
+
117
128
  export function isExcalidrawFilePath(filePath) {
118
129
  return getVaultFileKind(filePath) === 'excalidraw';
119
130
  }
@@ -1,4 +1,6 @@
1
1
  import { execFile as execFileCallback } from 'node:child_process';
2
+ import { glob, readFile, stat } from 'node:fs/promises';
3
+ import { join } from 'node:path';
2
4
  import { promisify } from 'node:util';
3
5
 
4
6
  import { getVaultFileKind } from '../../domain/file-kind.js';
@@ -17,6 +19,8 @@ const TEXT_SEARCH_GLOBS = Object.freeze([
17
19
  '*.md',
18
20
  '*.markdown',
19
21
  '*.mdx',
22
+ '*.html',
23
+ '*.htm',
20
24
  '*.base',
21
25
  '*.mmd',
22
26
  '*.mermaid',
@@ -24,9 +28,15 @@ const TEXT_SEARCH_GLOBS = Object.freeze([
24
28
  '*.plantuml',
25
29
  '*.dsl',
26
30
  '*.drawio',
27
- '*.excalidraw',
28
31
  ]);
29
32
 
33
+ function parseByteLimit(value) {
34
+ const match = String(value ?? '').trim().match(/^(\d+)([KMG])?$/iu);
35
+ if (!match) return 1024 * 1024;
36
+ const multipliers = { K: 1024, M: 1024 ** 2, G: 1024 ** 3 };
37
+ return Number(match[1]) * (multipliers[match[2]?.toUpperCase()] ?? 1);
38
+ }
39
+
30
40
  function normalizePositiveInt(value, fallback, { min = 1, max = Number.MAX_SAFE_INTEGER } = {}) {
31
41
  const parsed = Number.parseInt(value ?? '', 10);
32
42
  if (!Number.isInteger(parsed) || parsed < min) {
@@ -216,6 +226,72 @@ export function parseRipgrepJson(stdout, {
216
226
  };
217
227
  }
218
228
 
229
+ async function searchExcalidrawText({ maxFileSize, maxFiles, maxSnippetsPerFile, query, signal, vaultDir }) {
230
+ const files = [];
231
+ let matchCount = 0;
232
+ let truncated = false;
233
+ const normalizedQuery = query.toLocaleLowerCase();
234
+ const paths = glob('**/*.excalidraw', {
235
+ cwd: vaultDir,
236
+ exclude: ['**/.*', '**/node_modules/**'],
237
+ });
238
+
239
+ for await (const filePath of paths) {
240
+ signal?.throwIfAborted?.();
241
+ const absolutePath = join(vaultDir, filePath);
242
+ if ((await stat(absolutePath)).size > parseByteLimit(maxFileSize)) continue;
243
+
244
+ let scene;
245
+ try {
246
+ scene = JSON.parse(await readFile(absolutePath, 'utf8'));
247
+ } catch {
248
+ continue;
249
+ }
250
+
251
+ const snippets = [];
252
+ let fileMatchCount = 0;
253
+ for (const element of Array.isArray(scene?.elements) ? scene.elements : []) {
254
+ if (element?.isDeleted || element?.type !== 'text' || typeof element.text !== 'string') continue;
255
+ const text = element.text;
256
+ const normalizedText = text.toLocaleLowerCase();
257
+ let matchStart = normalizedText.indexOf(normalizedQuery);
258
+ while (matchStart >= 0) {
259
+ fileMatchCount += 1;
260
+ matchCount += 1;
261
+ if (snippets.length < maxSnippetsPerFile) {
262
+ const snippet = createSnippet(text, matchStart, matchStart + query.length);
263
+ snippets.push({
264
+ column: matchStart + 1,
265
+ line: 1,
266
+ matchEnd: snippet.matchEnd,
267
+ matchStart: snippet.matchStart,
268
+ text: snippet.text,
269
+ });
270
+ } else {
271
+ truncated = true;
272
+ }
273
+ matchStart = normalizedText.indexOf(normalizedQuery, matchStart + Math.max(query.length, 1));
274
+ }
275
+ }
276
+
277
+ if (fileMatchCount > 0) {
278
+ if (files.length >= maxFiles) {
279
+ truncated = true;
280
+ continue;
281
+ }
282
+ files.push({
283
+ file: filePath.replaceAll('\\', '/'),
284
+ kind: 'excalidraw',
285
+ matchCount: fileMatchCount,
286
+ snippets,
287
+ truncated: fileMatchCount > snippets.length,
288
+ });
289
+ }
290
+ }
291
+
292
+ return { files, matchCount, truncated };
293
+ }
294
+
219
295
  export class RipgrepSearchService {
220
296
  constructor({
221
297
  execFileImpl = execFile,
@@ -307,7 +383,7 @@ export class RipgrepSearchService {
307
383
  maxFileSize: this.maxFileSize,
308
384
  maxSnippetsPerFile: this.maxSnippetsPerFile,
309
385
  });
310
-
386
+ let parsed;
311
387
  try {
312
388
  const result = await this.execFileImpl('rg', args, {
313
389
  cwd: this.vaultDir,
@@ -316,56 +392,53 @@ export class RipgrepSearchService {
316
392
  ...(signal ? { signal } : {}),
317
393
  timeout: this.timeoutMs,
318
394
  });
319
- const parsed = parseRipgrepJson(result.stdout, {
395
+ parsed = parseRipgrepJson(result.stdout, {
320
396
  maxFiles,
321
397
  maxSnippetsPerFile: this.maxSnippetsPerFile,
322
398
  query: normalizedQuery,
323
399
  });
324
- parsed.search = this.getClientConfig();
325
-
326
- logPerfEvent(this.perfLoggingEnabled, 'search-query', {
327
- durationMs: Date.now() - startedAt,
328
- fileCount: parsed.files.length,
329
- matchCount: parsed.matchCount,
330
- truncated: parsed.truncated,
331
- });
332
- return parsed;
333
400
  } catch (error) {
334
401
  if (error?.code === 1) {
335
- const empty = createEmptySearchResult({
402
+ parsed = createEmptySearchResult({
336
403
  query: normalizedQuery,
337
404
  searchAvailable: this.available,
338
405
  });
339
- empty.search = this.getClientConfig();
340
- logPerfEvent(this.perfLoggingEnabled, 'search-query', {
341
- durationMs: Date.now() - startedAt,
342
- fileCount: 0,
343
- matchCount: 0,
344
- truncated: false,
406
+ } else {
407
+ const isMaxBuffer = error?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
408
+ parsed = parseRipgrepJson(error?.stdout ?? '', {
409
+ maxFiles,
410
+ maxSnippetsPerFile: this.maxSnippetsPerFile,
411
+ query: normalizedQuery,
345
412
  });
346
- return empty;
413
+ if (isMaxBuffer && parsed.files.length > 0) {
414
+ parsed.truncated = true;
415
+ } else {
416
+ error.statusCode = error?.signal === 'SIGTERM' ? 504 : 500;
417
+ throw error;
418
+ }
347
419
  }
420
+ }
348
421
 
349
- const isMaxBuffer = error?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';
350
- const parsed = parseRipgrepJson(error?.stdout ?? '', {
351
- maxFiles,
422
+ if (parsed.files.length < maxFiles) {
423
+ const excalidraw = await searchExcalidrawText({
424
+ maxFileSize: this.maxFileSize,
425
+ maxFiles: maxFiles - parsed.files.length,
352
426
  maxSnippetsPerFile: this.maxSnippetsPerFile,
353
427
  query: normalizedQuery,
428
+ signal,
429
+ vaultDir: this.vaultDir,
354
430
  });
355
- if (isMaxBuffer && parsed.files.length > 0) {
356
- parsed.search = this.getClientConfig();
357
- parsed.truncated = true;
358
- logPerfEvent(this.perfLoggingEnabled, 'search-query', {
359
- durationMs: Date.now() - startedAt,
360
- fileCount: parsed.files.length,
361
- matchCount: parsed.matchCount,
362
- truncated: true,
363
- });
364
- return parsed;
365
- }
366
-
367
- error.statusCode = error?.signal === 'SIGTERM' ? 504 : 500;
368
- throw error;
431
+ parsed.files.push(...excalidraw.files);
432
+ parsed.matchCount += excalidraw.matchCount;
433
+ parsed.truncated ||= excalidraw.truncated;
369
434
  }
435
+ parsed.search = this.getClientConfig();
436
+ logPerfEvent(this.perfLoggingEnabled, 'search-query', {
437
+ durationMs: Date.now() - startedAt,
438
+ fileCount: parsed.files.length,
439
+ matchCount: parsed.matchCount,
440
+ truncated: parsed.truncated,
441
+ });
442
+ return parsed;
370
443
  }
371
444
  }
@@ -3,7 +3,7 @@ import { isAbsolute, normalize, relative, resolve } from 'path';
3
3
  import { isVaultFilePath } from '../../../domain/file-kind.js';
4
4
 
5
5
  export const IGNORED_DIRECTORIES = new Set(['.git', '.obsidian', '.trash', 'node_modules', '.DS_Store']);
6
- export const VAULT_FILE_PATH_REQUIREMENT = '.md, .markdown, .mdx, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .dsl, .png, .jpg, .jpeg, .webp, .gif, or .svg';
6
+ export const VAULT_FILE_PATH_REQUIREMENT = '.md, .markdown, .mdx, .html, .htm, .base, .excalidraw, .drawio, .mmd, .mermaid, .puml, .plantuml, .dsl, .pdf, .png, .jpg, .jpeg, .webp, .gif, or .svg';
7
7
  export const INVALID_VAULT_FILE_PATH_ERROR = `Invalid file path — must end in ${VAULT_FILE_PATH_REQUIREMENT}`;
8
8
  export const INVALID_DIRECTORY_PATH_ERROR = 'Invalid directory path';
9
9
 
@@ -12,12 +12,18 @@ export function isIgnoredVaultEntry(name) {
12
12
  }
13
13
 
14
14
  function normalizeRequestedPath(requestedPath) {
15
- const normalized = normalize(String(requestedPath ?? '').trim().replace(/\\/g, '/'));
16
- if (!normalized || normalized === '.') {
15
+ const value = String(requestedPath ?? '').trim().replace(/\\/g, '/');
16
+ const segments = value.split('/');
17
+ if (
18
+ !value
19
+ || isAbsolute(value)
20
+ || segments.some((segment) => segment === '.' || segment === '..')
21
+ ) {
17
22
  return '';
18
23
  }
19
24
 
20
- return normalized;
25
+ const normalized = normalize(value);
26
+ return normalized === '.' ? '' : normalized;
21
27
  }
22
28
 
23
29
  export function sanitizeVaultPath(vaultDir, requestedPath) {
@@ -28,6 +28,7 @@ const EDITABLE_VAULT_CONTENT_PATH_ERRORS = {
28
28
  base: 'Invalid file path — must end in .base',
29
29
  drawio: 'Invalid file path — must end in .drawio',
30
30
  excalidraw: 'Invalid file path — must end in .excalidraw',
31
+ html: 'Invalid file path — must end in .html or .htm',
31
32
  markdown: 'Invalid file path',
32
33
  mermaid: 'Invalid file path — must end in .mmd or .mermaid',
33
34
  plantuml: 'Invalid file path — must end in .puml or .plantuml',
@@ -66,6 +67,7 @@ const TEXT_FILE_MIME_TYPES = Object.freeze({
66
67
  base: 'text/yaml; charset=utf-8',
67
68
  drawio: 'application/xml; charset=utf-8',
68
69
  excalidraw: 'application/json; charset=utf-8',
70
+ html: 'text/html; charset=utf-8',
69
71
  markdown: 'text/markdown; charset=utf-8',
70
72
  mermaid: 'text/plain; charset=utf-8',
71
73
  pdf: 'application/pdf',
@@ -1,7 +0,0 @@
1
- import{n as e,t}from"./es-CAsWR6hG.js";import{n}from"./vault-utils-wdQ0RNR9.js";import{o as r,s as i}from"./comment-threads-9J6Em-_y.js";function a(e,t,n,r,i,a){return e?.(t,n,r,i,a)??a.renderToken(t,n,r)}function o(e=``){return`<p>${n(String(e??``)).replace(/\n/g,`<br>`)}</p>`}function s(){let r=e({breaks:!0,highlight(e,r){try{return r&&t.getLanguage(r)?t.highlight(e,{ignoreIllegals:!0,language:r}).value:t.highlightAuto(e).value}catch{return n(e)}},html:!1,linkify:!0,typographer:!0}),i=r.renderer.rules.link_open,o=r.renderer.rules.table_open,s=r.renderer.rules.table_close;return r.renderer.rules.link_open=(e,t,n,r,o)=>(e[t].attrSet(`target`,`_blank`),e[t].attrSet(`rel`,`noopener noreferrer`),a(i,e,t,n,r,o)),r.renderer.rules.table_open=(e,t,n,r,i)=>`<div class="comment-markdown-table">${a(o,e,t,n,r,i)}`,r.renderer.rules.table_close=(e,t,n,r,i)=>`${a(s,e,t,n,r,i)}</div>`,r}var c=s();function l(e=``){let t=String(e??``);try{return c.render(t)}catch{return o(t)}}var u=Object.freeze([`👍`,`❤️`,`🎉`,`👀`,`🚀`]),d=Object.freeze([`😂`,`🔥`,`✅`,`🙏`,`💡`,`🤔`,`👏`,`😄`,`🎯`,`🙌`]);function f(e=[]){return[...e].sort((e,t)=>(e.anchor?.startLine??0)-(t.anchor?.startLine??0)||e.createdAt-t.createdAt)}function p(e){return e?.anchorKind||e?.kind||`line`}function m(e){return p(e)===`text`&&Number.isFinite(e?.startIndex)&&Number.isFinite(e?.endIndex)&&e.endIndex>e.startIndex}function h(e,t){return e===t?!0:!e||!t?!1:p(e)===p(t)&&(e.startIndex??null)===(t.startIndex??null)&&(e.endIndex??null)===(t.endIndex??null)&&(e.startLine??null)===(t.startLine??null)&&(e.endLine??null)===(t.endLine??null)&&(e.anchorQuote??e.quote??``)===(t.anchorQuote??t.quote??``)}function g(e){if(p(e)===`diagram-element`)return`Diagram element`;let t=Number(e?.startLine||0),n=Number(e?.endLine||t);return!Number.isFinite(t)||t<=0?`No source anchor`:t===n?`Line ${t}`:`Lines ${t}-${n}`}function _(e={}){return[e.kind||e.anchorKind||`line`,e.startLine??0,e.endLine??0,e.quote||``].join(`::`)}function v(e){return e&&!e.querySelector(`[data-source-line]`)}function y(e){let t=Number.parseInt(e||``,10);return Number.isFinite(t)?t:null}function b(e=[]){return e.reduce((e,t)=>e?(t?.createdAt??0)>=(e?.createdAt??0)?t:e:t,null)}function x(e){return e?.threads?.reduce((e,t)=>{let n=b(t?.messages??[]);return n&&(n.createdAt??0)>=(e?.createdAt??0)?n:e},null)}function S(e,t=`comment-markdown`){let n=document.createElement(`div`);return n.className=t,n.innerHTML=l(e),n}function C({authorName:e=`Anonymous`,buttonClassName:t=`comment-overview-thread`,footerClassName:n=`comment-overview-thread-footer`,headerClassName:r=`comment-overview-thread-header`,lineClassName:i=`comment-overview-thread-line`,lineLabel:a=``,messageCount:o=0,previewBody:s=``,previewClassName:c=`comment-markdown comment-overview-thread-preview`,quote:l=`Source anchored comment`,quoteClassName:u=`comment-overview-thread-quote`,timestamp:d=``}={}){let f=document.createElement(`button`);f.type=`button`,f.className=t;let p=document.createElement(`div`);p.className=r;let m=document.createElement(`span`);m.className=i,m.textContent=a;let h=document.createElement(`span`);h.className=`comment-overview-thread-meta`,h.textContent=d,p.append(m,h);let g=document.createElement(`div`);g.className=`comment-overview-thread-body`;let _=S(s,c),v=document.createElement(`div`);v.className=`comment-overview-thread-section comment-overview-thread-reference`;let y=document.createElement(`span`);y.className=`comment-overview-thread-section-label`,y.textContent=`Reference`;let b=document.createElement(`span`);b.className=u,b.textContent=l,v.append(y,b),g.append(_,v);let x=document.createElement(`div`);x.className=n;let C=document.createElement(`span`);C.className=`comment-overview-thread-author`,C.textContent=e;let w=document.createElement(`span`);return w.className=`comment-overview-thread-message-count`,w.textContent=`${o} message${o===1?``:`s`}`,x.append(C,w),f.append(p,g,x),f}function w(e,t){return!!(t&&e?.users?.some(e=>e?.userId===t))}function T(e){return String(Array.isArray(e?.users)?e.users.length:0)}function E(e,t,n){return e?.threadId===t&&e?.messageId===n}function D(e){let t=e?.querySelector?.(`.comment-reaction-picker`),n=t?.closest?.(`.comment-reaction-picker-wrap`),r=e?.querySelector?.(`.comment-card-scroll`);return!(t instanceof HTMLElement)||!(n instanceof HTMLElement)||!(r instanceof HTMLElement)?null:{picker:t,scroll:r,wrap:n}}function O(e,t){let n=y(e?.getAttribute?.(`data-source-line`)),r=y(e?.getAttribute?.(`data-source-line-end`))??n;return!n||!r||!t?!1:t.startLine<=r&&t.endLine>=n}function k(e,t,n=``){if(!e||e.isCollapsed||e.rangeCount===0||!t?.contains(e.anchorNode)||!t.contains(e.focusNode))return null;let i=e.getRangeAt(0),a=String(e.toString()).trim(),o=r(a),s=i.commonAncestorContainer.nodeType===Node.ELEMENT_NODE?i.commonAncestorContainer:i.commonAncestorContainer.parentElement;if(!o||s?.closest?.(`.diagram-preview-shell`))return null;let c=Array.from(t.querySelectorAll(`[data-source-line]`)).filter(e=>v(e)&&i.intersectsNode(e));if(c.length===0)return null;let l=String(n).split(`
2
- `),u=Math.min(Math.max(Math.min(...c.map(e=>y(e.getAttribute(`data-source-line`))??l.length)),1),l.length),d=Math.min(Math.max(...c.map(e=>y(e.getAttribute(`data-source-line-end`))??y(e.getAttribute(`data-source-line`))??1)),l.length),f=l.slice(0,u-1).reduce((e,t)=>e+t.length+1,0),p=l.slice(u-1,d).reduce((e,t,n)=>e+t.length+(n===0?0:1),f),m=`line`,h=f,g=p,_=!0;if(c.length===1){let e=String(n).slice(f,p),t=e.indexOf(a);t>=0&&t===e.lastIndexOf(a)&&(m=`text`,h=f+t,g=h+a.length,_=!1)}let b=e=>String(n).slice(0,e).split(`
3
- `).length,x=_?u:b(h),S=_?d:b(Math.max(g-1,h)),C=N(Array.from(i.getClientRects()))||N(c.map(e=>e.getBoundingClientRect()));return{anchor:{anchorKind:m,anchorQuote:o,endIndex:g,endLine:S,fallbackToLines:_,kind:m,quote:o,startIndex:h,startLine:x},range:i.cloneRange(),rect:C}}function A(e){let t=document.createTreeWalker(e,NodeFilter.SHOW_TEXT),n=``,r=[],i=!0;for(;t.nextNode();){let e=t.currentNode,a=e.textContent||``;for(let t=0;t<a.length;t+=1){let o=a[t];if(/\s/.test(o)){if(i)continue;n+=` `,r.push({node:e,offset:t}),i=!0;continue}n+=o,r.push({node:e,offset:t}),i=!1}}for(;n.endsWith(` `);)n=n.slice(0,-1),r.pop();return{map:r,normalized:n}}function j(e,t){let n=i(t);if(!e||!n)return null;let r=A(e);if(!r.normalized)return null;let a=r.normalized.indexOf(n);if(a<0||r.normalized.indexOf(n,a+1)>=0)return null;let o=r.map[a],s=r.map[a+n.length-1];if(!o||!s)return null;let c=document.createRange();return c.setStart(o.node,o.offset),c.setEnd(s.node,s.offset+1),c}function M(e,t){return{bottom:e.bottom-t.top,height:e.height,left:e.left-t.left,right:e.right-t.left,top:e.top-t.top,width:e.width}}function N(e=[]){if(!Array.isArray(e)||e.length===0)return null;let t=Math.min(...e.map(e=>e.left)),n=Math.min(...e.map(e=>e.top)),r=Math.max(...e.map(e=>e.right)),i=Math.max(...e.map(e=>e.bottom));return{bottom:i,height:i-n,left:t,right:r,top:n,width:r-t}}function P(e,t,n){return n?e>=n.left&&e<=n.right&&t>=n.top&&t<=n.bottom:!1}function F(e=[]){return[...new Set(e.filter(Boolean))].sort()}function I(e=[]){return F(e).join(` `)}function L(e){let t=document.createDocumentFragment(),n=document.createElement(`span`);if(n.className=`comment-marker-icon`,n.setAttribute(`aria-hidden`,`true`),n.innerHTML=`
4
- <svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
5
- <path d="M3 4.75A1.75 1.75 0 0 1 4.75 3h6.5A1.75 1.75 0 0 1 13 4.75v4.5A1.75 1.75 0 0 1 11.25 11H8.9L6.5 13v-2H4.75A1.75 1.75 0 0 1 3 9.25v-4.5Z" stroke="currentColor" stroke-width="1.35" stroke-linejoin="round"/>
6
- </svg>
7
- `,t.appendChild(n),e>1){let n=document.createElement(`span`);n.className=`comment-marker-count`,n.textContent=String(e),t.appendChild(n)}return t}export{I as C,P as S,M as T,v as _,C as a,F as b,S as c,T as d,_ as f,w as g,D as h,L as i,j as l,b as m,u as n,k as o,x as p,h as r,N as s,d as t,g as u,E as v,f as w,O as x,m as y};