collabmd 0.1.52 → 0.1.54

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 (105) hide show
  1. package/bin/collabmd.js +3 -2
  2. package/dist/client/assets/{CodeMirrorEditor-UCMRAMCV-BEz2BKN1.js → CodeMirrorEditor-UCMRAMCV-kJfwukbn.js} +1 -1
  3. package/dist/client/assets/{browser-BKLM0ThC-IxjwuOIz-C0mP17s1.js → browser-BKLM0ThC-BCLQGPD6-K2seJgRL.js} +1 -1
  4. package/dist/client/assets/{browser-utils-vlHL9goC.js → browser-utils-S4QYY31r.js} +1 -1
  5. package/dist/client/assets/{chunk-RUX4WMZV-BGnd3s_C.js → chunk-ISPTBLVO-BMsDq9Ha.js} +2 -2
  6. package/dist/client/assets/{chunk-4SOEAONT-C3fufoVl.js → chunk-T5WUORNS-DD7KlyY6.js} +9 -9
  7. package/dist/client/assets/{direct-engine-DdrQKpgq-4QysbH_V.js → direct-engine-DVLMOJWp-C-YL5NFb.js} +1 -1
  8. package/dist/client/assets/{dist-CXOUa90Q.js → dist-9Eodxf4G.js} +1 -1
  9. package/dist/client/assets/{dist-jVKfryIL.js → dist-B2ZNQifU.js} +1 -1
  10. package/dist/client/assets/dist-B6QCMNze.js +1 -0
  11. package/dist/client/assets/dist-BIAv19LO.js +1 -0
  12. package/dist/client/assets/{dist-Bog8M5U3.js → dist-BKYp1-dl.js} +4 -4
  13. package/dist/client/assets/{dist-DI6c02Da.js → dist-BSAZ-Hek.js} +1 -1
  14. package/dist/client/assets/{dist-DNONgI1P.js → dist-BSTbcioM.js} +1 -1
  15. package/dist/client/assets/{dist-BfpAc922.js → dist-BT9gJXCb.js} +1 -1
  16. package/dist/client/assets/{dist-CAG5sIOj.js → dist-BTVg1m-V.js} +1 -1
  17. package/dist/client/assets/{dist-GTGhtpyW.js → dist-Bi9ZIYXq.js} +1 -1
  18. package/dist/client/assets/{dist-CvVoyTRF.js → dist-BpAUe9sL.js} +1 -1
  19. package/dist/client/assets/{dist-B7fBbwrO.js → dist-Bqo-XRVG.js} +1 -1
  20. package/dist/client/assets/{dist-CUsXdnU4.js → dist-BrKhPlc9.js} +1 -1
  21. package/dist/client/assets/{dist-CYMCikZo.js → dist-CBsvKPcT.js} +1 -1
  22. package/dist/client/assets/{dist-W0FoeIxk.js → dist-CJBYh9Wy.js} +1 -1
  23. package/dist/client/assets/{dist-Ck-B8onw.js → dist-CRwfjtwz.js} +1 -1
  24. package/dist/client/assets/{dist-Dx37MHWs.js → dist-CX7Kc61C.js} +1 -1
  25. package/dist/client/assets/{dist-zHDLm1oE.js → dist-CoXN2D3R.js} +1 -1
  26. package/dist/client/assets/{dist-D4kDR0g8.js → dist-CucpLxUY.js} +1 -1
  27. package/dist/client/assets/{dist-Cu5Ze2cs.js → dist-DHBMeCxC.js} +1 -1
  28. package/dist/client/assets/dist-DLEg9d4u.js +1 -0
  29. package/dist/client/assets/{dist-Y6zYRI-A.js → dist-DOHWxIty.js} +1 -1
  30. package/dist/client/assets/{dist-DVBYH83t.js → dist-DYFBD-wW.js} +1 -1
  31. package/dist/client/assets/{dist-CsQTfQFi.js → dist-DeCV9FU4.js} +1 -1
  32. package/dist/client/assets/dist-DugWGOjQ.js +1 -0
  33. package/dist/client/assets/{dist-BVCNc-Es.js → dist-KrdpB_GB.js} +1 -1
  34. package/dist/client/assets/{dist-B_1hMrc6.js → dist-NNrRnB-f.js} +1 -1
  35. package/dist/client/assets/{dist-Da-jLrCf.js → dist-PUxZqhFe.js} +1 -1
  36. package/dist/client/assets/dist-cYiJMWHJ.js +1 -0
  37. package/dist/client/assets/dist-iKC7go4m.js +1 -0
  38. package/dist/client/assets/{dist-CKf7LlAo.js → dist-lw5TGjI6.js} +1 -1
  39. package/dist/client/assets/{dist-DGa-2MNX.js → dist-uk11ih8u.js} +1 -1
  40. package/dist/client/assets/{document-formatter-gO8qM2qY.js → document-formatter-6I63vstk.js} +1 -1
  41. package/dist/client/assets/{drawioEditor-DCkWweiy.js → drawioEditor-DNTjsjzJ.js} +1 -1
  42. package/dist/client/assets/{editor-session-DxRyr4EM.js → editor-session-D8GGkJak.js} +3 -3
  43. package/dist/client/assets/{embedpdf-DyOonRvx.js → embedpdf-TkbBYowM.js} +1 -1
  44. package/dist/client/assets/{embedpdf-DA4bA119-DsoNiPlQ.js → embedpdf-cl9qiF45-itpgqvGY.js} +2 -2
  45. package/dist/client/assets/{en-GNC3RJIS-CEy_oGTd.js → en-JTHJ2SIP-DBz9bRq4.js} +1 -1
  46. package/dist/client/assets/{estree-DCT3Jm65.js → estree-DGmLqopC.js} +17 -17
  47. package/dist/client/assets/{excalidraw-element-link-CGZ5934B.js → excalidraw-element-link-BRZOTIeI.js} +2 -2
  48. package/dist/client/assets/{excalidrawEditor-BVfSxjSO.js → excalidrawEditor-D0orhhku.js} +3 -3
  49. package/dist/client/assets/{export-page-BCo_e15t.js → export-page-CVJNMERq.js} +3 -3
  50. package/dist/client/assets/{exportDocument-DP6oE_or.js → exportDocument-DSshnmnZ.js} +2 -2
  51. package/dist/client/assets/{file-search-3hYF4cYa.js → file-search-BA8o_gs0.js} +1 -1
  52. package/dist/client/assets/{html-CBXMuS97.js → html-BryNQ_yx.js} +6 -6
  53. package/dist/client/assets/image-I42VHMUI-B2-c_ifV.js +1 -0
  54. package/dist/client/assets/index-BCgNcVNs.css +1 -0
  55. package/dist/client/assets/index-BpmR5Qqk.js +1140 -0
  56. package/dist/client/assets/{lobby-presence-Cz5jGOPg.js → lobby-presence-KJrYhMdN.js} +1 -1
  57. package/dist/client/assets/{markdown-Bo71X2X2.js → markdown-CZ1tYEW8.js} +17 -17
  58. package/dist/client/assets/{pdfium-RAgkpwfK.wasm → pdfium-BJ2Yip8Q.wasm} +0 -0
  59. package/dist/client/assets/{preview-render-compiler-ktU0SXxY.js → preview-render-compiler-BmWQStaL.js} +1 -1
  60. package/dist/client/assets/{preview-render-worker-BI2bFVqM.js → preview-render-worker-BA4VK_GY.js} +2 -2
  61. package/dist/client/assets/prod-CA0ie9yD.js +145 -0
  62. package/dist/client/assets/prod-yKJZ3E4s.js +1 -0
  63. package/dist/client/assets/{quick-switcher-controller-DSlOwOsU.js → quick-switcher-controller-B5vT9Vj5.js} +1 -1
  64. package/dist/client/assets/runtime-config-CvrkHb0n.js +1 -0
  65. package/dist/client/assets/runtime-paths-qQGhzNx4.js +1 -0
  66. package/dist/client/assets/{standalone-6z5x8CWe.js → standalone-DbyFjvTU.js} +1 -1
  67. package/dist/client/assets/{vault-api-client-BQxLVPeF.js → vault-api-client-NS_EVHJ3.js} +2 -2
  68. package/dist/client/assets/{worker-engine-m0rlHn8R-5h66k403.js → worker-engine-BCCrxWYm-CX0qJ_cG.js} +1 -1
  69. package/dist/client/assets/{workspace-sync-client-C2pdaQUN.js → workspace-sync-client-BrMtf6zN.js} +1 -1
  70. package/dist/client/drawio-editor.html +5 -5
  71. package/dist/client/excalidraw-editor.html +9 -9
  72. package/dist/client/export-document.html +1 -1
  73. package/dist/client/index.html +13 -2
  74. package/package.json +14 -14
  75. package/src/client/app/index.html +11 -0
  76. package/src/client/app/main-entry.js +30 -4
  77. package/src/client/bootstrap/collabmd-app-shell.js +3 -1
  78. package/src/client/browser-utils.js +21 -0
  79. package/src/client/domain/file-search.js +1 -1
  80. package/src/client/domain/runtime-paths.js +4 -0
  81. package/src/client/infrastructure/runtime-config.js +14 -0
  82. package/src/client/presentation/drawio-embed-controller.js +3 -1
  83. package/src/client/presentation/excalidraw-embed-controller.js +3 -1
  84. package/src/client/presentation/file-action-controller.js +21 -1
  85. package/src/client/presentation/file-explorer-view.js +24 -6
  86. package/src/client/presentation/vault-dashboard.js +63 -0
  87. package/src/client/styles/foundation/utilities.css +2 -0
  88. package/src/client/styles/layout/overlays.css +5 -0
  89. package/src/client/styles/layout/shell.css +152 -0
  90. package/src/server/config/env.js +39 -6
  91. package/src/server/infrastructure/http/create-static-handler.js +3 -1
  92. package/src/server/infrastructure/http/create-vault-api-query-handler.js +1 -1
  93. package/dist/client/assets/dist-BYBF4LtZ.js +0 -1
  94. package/dist/client/assets/dist-BnVaoRgZ.js +0 -1
  95. package/dist/client/assets/dist-CrkYYcOU.js +0 -1
  96. package/dist/client/assets/dist-D8LnhZzy.js +0 -1
  97. package/dist/client/assets/dist-DX6qtdxv.js +0 -1
  98. package/dist/client/assets/dist-DofvCYzJ.js +0 -1
  99. package/dist/client/assets/image-EJJKRVD3-BQfi5nWJ.js +0 -1
  100. package/dist/client/assets/index-BxAuGDoN.css +0 -1
  101. package/dist/client/assets/index-uw9O0z1c.js +0 -1112
  102. package/dist/client/assets/prod-DU-0VfI7.js +0 -145
  103. package/dist/client/assets/prod-cXUNdmYV.js +0 -1
  104. package/dist/client/assets/runtime-config-BFVdhnJu.js +0 -1
  105. package/dist/client/assets/runtime-paths-Cn0snt_9.js +0 -1
@@ -4,7 +4,7 @@ function createRangeIndices(start, length) {
4
4
  return Array.from({ length }, (_, index) => start + index);
5
5
  }
6
6
 
7
- function findFuzzyMatch(text, query) {
7
+ export function findFuzzyMatch(text, query) {
8
8
  const indices = [];
9
9
  let queryIndex = 0;
10
10
  let score = 0;
@@ -76,6 +76,10 @@ export function getActiveVaultId(config = getClientRuntimeConfig()) {
76
76
  if (vaults.length === 0) {
77
77
  return null;
78
78
  }
79
+ const requested = new URLSearchParams(globalThis.window?.location?.search ?? '').get('vault');
80
+ if (requested && vaults.includes(requested)) {
81
+ return requested;
82
+ }
79
83
  try {
80
84
  const stored = globalThis.window?.localStorage?.getItem(ACTIVE_VAULT_STORAGE_KEY);
81
85
  if (stored && vaults.includes(stored)) {
@@ -21,6 +21,20 @@ export function getRuntimeConfig() {
21
21
  export { getActiveVaultId, resolveApiUrl, resolveAppPath, resolveAppUrl, resolveWsBaseUrl, setActiveVaultId };
22
22
  export { createFileRouteHash, isCollabMdHashRoute };
23
23
 
24
+ export function initializeWorkspaceUrl() {
25
+ const url = new URL(window.location.href);
26
+ const vaultId = getActiveVaultId();
27
+ if (vaultId) {
28
+ url.searchParams.set('vault', vaultId);
29
+ }
30
+ const filePath = url.searchParams.get('file');
31
+ if (filePath && !url.hash) {
32
+ url.hash = createFileRouteHash(filePath);
33
+ }
34
+ url.searchParams.delete('file');
35
+ window.history.replaceState(window.history.state, '', url);
36
+ }
37
+
24
38
  function getHashParams() {
25
39
  return getHashParamsFromRaw(window.location.hash);
26
40
  }
@@ -1,6 +1,6 @@
1
1
  import { createDrawioLeaseRoomName } from '../../domain/drawio-room.js';
2
2
  import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
3
- import { resolveAppUrl } from '../domain/runtime-paths.js';
3
+ import { getActiveVaultId, resolveAppUrl } from '../domain/runtime-paths.js';
4
4
  import { renderDrawioViewer } from './drawio-viewer.js';
5
5
  import {
6
6
  cancelIdleRender,
@@ -363,6 +363,8 @@ export class DrawioEmbedController {
363
363
  buildIframeUrl(entry) {
364
364
  const url = new URL(resolveAppUrl('/drawio-editor.html'));
365
365
  const localUser = this.getLocalUser?.() ?? {};
366
+ const vaultId = getActiveVaultId();
367
+ if (vaultId) url.searchParams.set('vault', vaultId);
366
368
  url.searchParams.set('file', entry.filePath);
367
369
  url.searchParams.set('hostMode', entry.mode === 'edit' ? 'file-preview' : 'embed');
368
370
  url.searchParams.set('instanceId', entry.instanceId);
@@ -1,7 +1,7 @@
1
1
  import { setDiagramActionButtonIcon } from '../domain/diagram-action-icons.js';
2
2
  import { parseExcalidrawElementLink } from '../domain/excalidraw-element-link.js';
3
3
  import { reconcileEmbedEntries } from './excalidraw-embed-reconciler.js';
4
- import { resolveAppPath, resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
4
+ import { getActiveVaultId, resolveAppPath, resolveAppUrl, resolveWsServerOverride } from '../domain/runtime-paths.js';
5
5
  import {
6
6
  cancelIdleRender,
7
7
  isNearViewport,
@@ -762,6 +762,8 @@ export class ExcalidrawEmbedController {
762
762
  }
763
763
  const theme = this.getTheme?.() || 'dark';
764
764
 
765
+ const vaultId = getActiveVaultId();
766
+ if (vaultId) iframeUrl.searchParams.set('vault', vaultId);
765
767
  iframeUrl.searchParams.set('file', entry.filePath);
766
768
  iframeUrl.searchParams.set('theme', theme);
767
769
  iframeUrl.searchParams.set('boot', entry.instanceId || '');
@@ -3,7 +3,7 @@ import {
3
3
  isVaultFilePath,
4
4
  VAULT_FILE_EXTENSIONS,
5
5
  } from '../../domain/file-kind.js';
6
- import { pickFiles } from '../browser-utils.js';
6
+ import { copyTextToClipboard, pickFiles } from '../browser-utils.js';
7
7
  import {
8
8
  createBaseStarter,
9
9
  composeVaultChildPath,
@@ -17,6 +17,8 @@ import {
17
17
  getVaultPathParent,
18
18
  normalizeVaultPathInput,
19
19
  } from '../domain/vault-paths.js';
20
+ import { createFileRouteHash } from '../domain/hash-routes.js';
21
+ import { getActiveVaultId } from '../domain/runtime-paths.js';
20
22
  import { buttonClassNames } from './components/ui/button.js';
21
23
  import { CreateMenuPresenter } from './create-menu-presenter.js';
22
24
 
@@ -258,6 +260,10 @@ export class FileActionController {
258
260
 
259
261
  getFileContextMenuItems(filePath) {
260
262
  return [
263
+ {
264
+ label: 'Copy as URL',
265
+ onSelect: () => this.copyFileUrl(filePath),
266
+ },
261
267
  {
262
268
  label: 'Rename / move',
263
269
  onSelect: () => this.handleRenameFile(filePath),
@@ -274,6 +280,20 @@ export class FileActionController {
274
280
  ];
275
281
  }
276
282
 
283
+ async copyFileUrl(filePath) {
284
+ const url = new URL(window.location.href);
285
+ url.search = '';
286
+ const vaultId = getActiveVaultId();
287
+ if (vaultId) url.searchParams.set('vault', vaultId);
288
+ url.hash = createFileRouteHash(filePath);
289
+ try {
290
+ await copyTextToClipboard(url.toString());
291
+ this.showToast('Link copied');
292
+ } catch {
293
+ this.showToast('Failed to copy link');
294
+ }
295
+ }
296
+
277
297
  showToast(message) {
278
298
  if (message) {
279
299
  this.toastController?.show(String(message));
@@ -4,6 +4,7 @@ import {
4
4
  stripVaultFileExtension,
5
5
  } from '../../domain/file-kind.js';
6
6
  import { escapeHtml } from '../domain/vault-utils.js';
7
+ import { findFuzzyMatch } from '../domain/file-search.js';
7
8
  import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
8
9
  import { buttonClassNames } from './components/ui/button.js';
9
10
 
@@ -43,9 +44,17 @@ function createMenuFilterInput({ container, itemSelector, placeholder, skipLabel
43
44
  if (skipLabels.includes(button.textContent)) {
44
45
  return;
45
46
  }
46
- button.hidden = query.length > 0 && !(button.textContent ?? '').toLowerCase().includes(query);
47
+ const text = (button.dataset.searchText ?? button.textContent ?? '').toLowerCase();
48
+ button.hidden = query.length > 0 && !text.includes(query) && !findFuzzyMatch(text, query);
47
49
  });
48
50
  });
51
+ searchInput.addEventListener('keydown', (event) => {
52
+ if (event.key === 'Enter') {
53
+ event.preventDefault();
54
+ Array.from(container.querySelectorAll(itemSelector))
55
+ .find((button) => !button.hidden && !skipLabels.includes(button.textContent))?.click();
56
+ }
57
+ });
49
58
  container.appendChild(searchInput);
50
59
  return searchInput;
51
60
  }
@@ -117,17 +126,17 @@ export class FileExplorerView {
117
126
  button.className = 'vault-switcher-button';
118
127
  button.id = 'vaultSwitcher';
119
128
  button.setAttribute('aria-haspopup', 'menu');
120
- button.setAttribute('aria-label', `Switch vault (current: ${active.id})`);
129
+ button.setAttribute('aria-label', `Switch vault (current: ${active.name || active.id})`);
121
130
  button.title = 'Switch vault';
122
- button.innerHTML = `<span class="vault-switcher-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg></span><span class="vault-switcher-name">${escapeHtml(active.id)}</span><span class="vault-switcher-chevron" aria-hidden="true"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg></span>`;
131
+ button.innerHTML = `<span class="vault-switcher-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg></span><span class="vault-switcher-name">${escapeHtml(active.name || active.id)}</span><span class="vault-switcher-chevron" aria-hidden="true"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg></span>`;
123
132
  button.addEventListener('click', (event) => {
124
133
  this.showContextMenu(event, entries.map((vault) => ({
125
- label: vault.id,
134
+ label: vault.name || vault.id,
135
+ searchText: `${vault.name || vault.id} ${vault.id}`,
126
136
  onSelect: () => (onVaultSelect ?? this.onVaultSelect)?.(vault.id),
127
137
  })), {
128
138
  alignToAnchor: true,
129
- // ponytail: filter only pays off for long vault lists
130
- searchPlaceholder: entries.length > 8 ? 'Filter vaults…' : '',
139
+ searchPlaceholder: 'Filter vaults…',
131
140
  });
132
141
  });
133
142
  wrapper.appendChild(button);
@@ -1093,6 +1102,7 @@ export class FileExplorerView {
1093
1102
  button.className = `file-context-item${item.danger ? ' file-context-danger' : ''}`;
1094
1103
  button.setAttribute('role', 'menuitem');
1095
1104
  button.textContent = item.label;
1105
+ if (item.searchText) button.dataset.searchText = item.searchText;
1096
1106
  button.addEventListener('click', () => {
1097
1107
  this.removeContextMenu();
1098
1108
  item.onSelect?.({ anchor: contextAnchor });
@@ -1184,6 +1194,7 @@ export class FileExplorerView {
1184
1194
  extra: ['file-action-sheet-item', item.danger ? 'file-context-danger' : ''],
1185
1195
  });
1186
1196
  button.textContent = item.label;
1197
+ if (item.searchText) button.dataset.searchText = item.searchText;
1187
1198
  button.addEventListener('click', () => {
1188
1199
  this.removeContextMenu();
1189
1200
  item.onSelect?.();
@@ -1203,6 +1214,13 @@ export class FileExplorerView {
1203
1214
  });
1204
1215
  sheet.appendChild(cancelButton);
1205
1216
 
1217
+ sheet.addEventListener('keydown', (event) => {
1218
+ if (event.key === 'Escape') {
1219
+ // A search input otherwise consumes Escape before the dialog can cancel.
1220
+ event.preventDefault();
1221
+ this.removeContextMenu();
1222
+ }
1223
+ });
1206
1224
  sheet.addEventListener('cancel', (event) => {
1207
1225
  event.preventDefault();
1208
1226
  this.removeContextMenu();
@@ -0,0 +1,63 @@
1
+ import { findFuzzyMatch } from '../domain/file-search.js';
2
+
3
+ export function renderVaultDashboard({ container, vaults, getVaultUrl, onSelect }) {
4
+ container.removeAttribute('role');
5
+ container.classList.add('vault-dashboard');
6
+ container.innerHTML = `
7
+ <main class="vault-dashboard-card" aria-labelledby="vaultDashboardTitle">
8
+ <header class="vault-dashboard-header">
9
+ <div class="sidebar-brand">
10
+ <svg width="28" height="28" viewBox="0 0 32 32" fill="none" aria-hidden="true">
11
+ <rect x="3" y="4" width="17" height="24" rx="3" stroke="var(--color-primary)" stroke-width="2"></rect>
12
+ <rect x="12" y="4" width="17" height="24" rx="3" fill="var(--color-primary)"></rect>
13
+ <path d="M17 12h7m-7 4h7m-7 4h4" stroke="var(--color-primary-contrast)" stroke-width="1.5"></path>
14
+ </svg>
15
+ <span class="sidebar-title">CollabMD</span>
16
+ </div>
17
+ <h1 id="vaultDashboardTitle">Choose a vault</h1>
18
+ <p>Open a project to start collaborating.</p>
19
+ </header>
20
+ <div class="vault-dashboard-search">
21
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"></circle><path d="m16 16 4 4"></path></svg>
22
+ <input type="search" class="ui-input" aria-label="Find a vault" placeholder="Find a vault…">
23
+ </div>
24
+ <div class="vault-dashboard-list-heading">
25
+ <h2>Your vaults</h2>
26
+ <span class="ui-pill-badge ui-pill-badge--count ui-pill-badge--muted"></span>
27
+ </div>
28
+ <nav aria-label="Vaults"></nav>
29
+ <p class="ui-empty-state" role="status" hidden>No matching vaults.</p>
30
+ <footer class="vault-dashboard-footer">Press <kbd>Enter</kbd> to open the first match</footer>
31
+ </main>`;
32
+ container.querySelector('.ui-pill-badge').textContent = String(vaults.length);
33
+ const list = container.querySelector('nav');
34
+ const empty = container.querySelector('[role="status"]');
35
+ const entries = vaults.map((vault) => {
36
+ const link = document.createElement('a');
37
+ link.className = 'ui-record-surface vault-dashboard-link';
38
+ link.href = getVaultUrl(vault.id);
39
+ link.innerHTML = `
40
+ <span class="vault-dashboard-icon" aria-hidden="true"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg></span>
41
+ <span class="vault-dashboard-name"></span>
42
+ <svg class="vault-dashboard-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14m-5-5 5 5-5 5"></path></svg>`;
43
+ link.querySelector('.vault-dashboard-name').textContent = vault.name || vault.id;
44
+ link.addEventListener('click', () => onSelect?.(vault.id));
45
+ list.append(link);
46
+ return { link, text: `${vault.name || vault.id} ${vault.id}`.toLowerCase() };
47
+ });
48
+ const input = container.querySelector('input');
49
+ input.addEventListener('input', () => {
50
+ const query = input.value.trim().toLowerCase();
51
+ entries.forEach(({ link, text }) => {
52
+ link.hidden = Boolean(query && !text.includes(query) && !findFuzzyMatch(text, query));
53
+ });
54
+ empty.hidden = entries.some(({ link }) => !link.hidden);
55
+ });
56
+ input.addEventListener('keydown', (event) => {
57
+ if (event.key === 'Enter') {
58
+ event.preventDefault();
59
+ entries.find(({ link }) => !link.hidden)?.link.click();
60
+ }
61
+ });
62
+ input.focus();
63
+ }
@@ -34,3 +34,5 @@ html[data-initial-file-requested="true"] .editor-page.hidden {
34
34
  clip: auto;
35
35
  transform: translateY(0);
36
36
  }
37
+
38
+ .clipboard-fallback { position: fixed; left: -9999px; opacity: 0; }
@@ -99,6 +99,11 @@
99
99
  font-size: var(--text-ui-sm);
100
100
  }
101
101
 
102
+ .file-context-item[hidden],
103
+ .file-action-sheet-item[hidden] {
104
+ display: none;
105
+ }
106
+
102
107
  .file-context-item:hover {
103
108
  background: var(--color-surface-offset);
104
109
  }
@@ -1,5 +1,157 @@
1
1
  /* ===== APP SHELL ===== */
2
2
 
3
+ html[data-workspace-loading] .app-shell,
4
+ html[data-workspace-loading] .skip-link {
5
+ visibility: hidden;
6
+ }
7
+
8
+ .workspace-loading {
9
+ position: fixed;
10
+ inset: 0;
11
+ display: grid;
12
+ place-content: center;
13
+ padding: var(--space-6);
14
+ overflow: auto;
15
+ background: var(--color-bg);
16
+ color: var(--color-text-muted);
17
+ }
18
+
19
+ .vault-dashboard {
20
+ place-items: center;
21
+ place-content: normal;
22
+ }
23
+
24
+ .vault-dashboard-card {
25
+ width: min(100%, 520px);
26
+ padding: clamp(var(--space-5), 4vw, var(--space-8));
27
+ border: 1px solid var(--border-accent-soft);
28
+ border-radius: var(--radius-xl);
29
+ background: var(--color-surface);
30
+ color: var(--color-text);
31
+ box-shadow: var(--shadow-card-strong);
32
+ }
33
+
34
+ .vault-dashboard-header .sidebar-brand {
35
+ margin-bottom: var(--space-6);
36
+ }
37
+
38
+ .vault-dashboard-header h1 {
39
+ margin: 0 0 var(--space-2);
40
+ font-family: var(--font-display);
41
+ font-size: var(--text-xl);
42
+ line-height: 1.15;
43
+ letter-spacing: -0.035em;
44
+ }
45
+
46
+ .vault-dashboard-header p {
47
+ margin: 0 0 var(--space-6);
48
+ color: var(--color-text-muted);
49
+ font-size: var(--text-ui-md);
50
+ line-height: 1.5;
51
+ }
52
+
53
+ .vault-dashboard-search {
54
+ position: relative;
55
+ }
56
+
57
+ .vault-dashboard-search > svg {
58
+ position: absolute;
59
+ top: 50%;
60
+ left: var(--space-3);
61
+ color: var(--color-text-faint);
62
+ transform: translateY(-50%);
63
+ pointer-events: none;
64
+ }
65
+
66
+ .vault-dashboard-search .ui-input {
67
+ min-height: var(--control-h-lg);
68
+ padding-left: var(--space-10);
69
+ background: var(--color-surface-offset);
70
+ }
71
+
72
+ .vault-dashboard-list-heading {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: space-between;
76
+ margin: var(--space-6) 0 var(--space-3);
77
+ }
78
+
79
+ .vault-dashboard-list-heading h2 {
80
+ margin: 0;
81
+ color: var(--color-text-muted);
82
+ font-size: var(--text-ui-sm);
83
+ font-weight: 600;
84
+ }
85
+
86
+ .vault-dashboard-card nav {
87
+ display: grid;
88
+ gap: var(--space-2);
89
+ max-height: 40dvh;
90
+ padding: var(--space-1);
91
+ margin: calc(-1 * var(--space-1));
92
+ overflow: auto;
93
+ }
94
+
95
+ .vault-dashboard-link.ui-record-surface {
96
+ --ui-record-gap: var(--space-3);
97
+ --ui-record-border-hover: var(--border-accent-soft);
98
+ flex-direction: row;
99
+ align-items: center;
100
+ color: var(--color-text);
101
+ text-decoration: none;
102
+ }
103
+
104
+ .vault-dashboard-icon {
105
+ display: grid;
106
+ place-items: center;
107
+ flex-shrink: 0;
108
+ width: 40px;
109
+ height: 40px;
110
+ border-radius: var(--radius-md);
111
+ background: var(--color-primary-highlight);
112
+ color: var(--color-primary);
113
+ }
114
+
115
+ .vault-dashboard-name {
116
+ flex: 1;
117
+ min-width: 0;
118
+ overflow-wrap: anywhere;
119
+ font-size: var(--text-ui-md);
120
+ font-weight: 600;
121
+ line-height: 1.4;
122
+ }
123
+
124
+ .vault-dashboard-arrow {
125
+ flex-shrink: 0;
126
+ color: var(--color-text-faint);
127
+ }
128
+
129
+ .vault-dashboard-footer {
130
+ display: flex;
131
+ flex-wrap: wrap;
132
+ align-items: center;
133
+ justify-content: center;
134
+ gap: var(--space-1);
135
+ margin-top: var(--space-6);
136
+ padding-top: var(--space-4);
137
+ border-top: 1px solid var(--color-divider);
138
+ color: var(--color-text-faint);
139
+ font-size: var(--text-caption);
140
+ }
141
+
142
+ .vault-dashboard-footer kbd {
143
+ padding: 1px var(--space-1);
144
+ border: 1px solid var(--color-divider);
145
+ border-radius: var(--radius-xs);
146
+ background: var(--color-surface-offset);
147
+ font: inherit;
148
+ }
149
+
150
+ .vault-dashboard-link[hidden],
151
+ .vault-dashboard-card [role='status'][hidden] {
152
+ display: none;
153
+ }
154
+
3
155
  .app-shell {
4
156
  position: fixed;
5
157
  top: var(--app-viewport-offset-top, 0px);
@@ -1,5 +1,5 @@
1
1
  import { basename, isAbsolute, relative, resolve } from 'path';
2
- import { readFileSync } from 'node:fs';
2
+ import { lstatSync, readdirSync, readFileSync } from 'node:fs';
3
3
  import { fileURLToPath } from 'url';
4
4
 
5
5
  import {
@@ -222,11 +222,11 @@ export function parseVaultList(rawValue) {
222
222
 
223
223
  export function resolveConfiguredVaults(overrides = {}, env = process.env) {
224
224
  if (Array.isArray(overrides.vaults)) {
225
- return overrides.vaults.map(({ id, dir, repoUrl }) => {
225
+ return overrides.vaults.map(({ id, dir, name, repoUrl }) => {
226
226
  if (!VAULT_ID_PATTERN.test(String(id ?? ''))) {
227
227
  throw new Error(`Invalid vault id "${id}". ${VAULT_ID_HINT}`);
228
228
  }
229
- return { id, dir: resolve(dir), repoUrl: normalizeOptionalString(repoUrl) };
229
+ return { id, dir: resolve(dir), ...(name ? { name: normalizeOptionalString(name) } : {}), repoUrl: normalizeOptionalString(repoUrl) };
230
230
  });
231
231
  }
232
232
  // ponytail: explicit directory (CLI positional) wins over the operator list
@@ -235,13 +235,34 @@ export function resolveConfiguredVaults(overrides = {}, env = process.env) {
235
235
  return [{ id: basename(dir), dir }];
236
236
  }
237
237
  const rawList = normalizeOptionalString(env.COLLABMD_VAULTS);
238
- if (!rawList) {
238
+ const discovery = normalizeOptionalString(env.COLLABMD_VAULT_DISCOVERY);
239
+ if (!rawList && !discovery) {
239
240
  const dir = resolveConfiguredVaultDir(overrides, env);
240
241
  return [{ id: basename(dir), dir }];
241
242
  }
242
- const vaults = parseVaultList(rawList);
243
+ let vaults;
244
+ if (rawList) {
245
+ vaults = parseVaultList(rawList);
246
+ } else {
247
+ if (!['all', 'marked'].includes(discovery)) {
248
+ throw new Error('COLLABMD_VAULT_DISCOVERY must be "all" or "marked".');
249
+ }
250
+ const root = resolve(env.COLLABMD_VAULT_DIR || '.');
251
+ // ponytail: discover immediate real directories at startup; restart to pick up additions.
252
+ vaults = readdirSync(root, { withFileTypes: true })
253
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.')
254
+ && (discovery === 'all' || lstatSync(resolve(root, entry.name, '.collabmd'), { throwIfNoEntry: false })?.isDirectory()))
255
+ .sort((a, b) => a.name.localeCompare(b.name, 'en'))
256
+ .map((entry) => ({ id: entry.name, dir: resolve(root, entry.name) }));
257
+ if (vaults.length === 0) {
258
+ throw new Error('No vaults found. Check COLLABMD_VAULT_DIR and COLLABMD_VAULT_DISCOVERY.');
259
+ }
260
+ }
243
261
  const seenIds = new Set();
244
262
  for (const vault of vaults) {
263
+ if (!VAULT_ID_PATTERN.test(vault.id)) {
264
+ throw new Error(`Invalid vault id "${vault.id}". ${VAULT_ID_HINT}`);
265
+ }
245
266
  if (seenIds.has(vault.id)) {
246
267
  throw new Error(`Duplicate vault id "${vault.id}" in COLLABMD_VAULTS.`);
247
268
  }
@@ -262,6 +283,17 @@ export function mangleVaultIdForEnv(vaultId) {
262
283
  return String(vaultId ?? '').toUpperCase().replace(/[^A-Z0-9]/g, '_');
263
284
  }
264
285
 
286
+ export function resolveVaultNames(vaults, env = process.env) {
287
+ return vaults.map((vault) => {
288
+ const suffix = mangleVaultIdForEnv(vault.id);
289
+ const name = normalizeOptionalString(env[`COLLABMD_VAULT_NAME_${suffix}`]);
290
+ if (name && vaults.filter((entry) => mangleVaultIdForEnv(entry.id) === suffix).length > 1) {
291
+ throw new Error(`Ambiguous vault name: multiple vaults share COLLABMD_VAULT_NAME_${suffix}.`);
292
+ }
293
+ return { ...vault, name: vault.name || name || vault.id };
294
+ });
295
+ }
296
+
265
297
  // ponytail: explicit repoUrl wins, then COLLABMD_GIT_REPO_URL_<ID>, then the shared URL for the primary
266
298
  export function resolveVaultRepoUrls(vaultDefs = [], overrides = {}, env = process.env) {
267
299
  const globalRepoUrl = normalizeOptionalString(overrides.remote?.repoUrl ?? overrides.git?.remote?.repoUrl ?? env.COLLABMD_GIT_REPO_URL);
@@ -540,7 +572,7 @@ function resolvePublicDir(nodeEnv) {
540
572
 
541
573
  export function loadConfig(overrides = {}) {
542
574
  const nodeEnv = process.env.NODE_ENV || 'development';
543
- const vaults = resolveVaultRepoUrls(resolveConfiguredVaults(overrides), overrides);
575
+ const vaults = resolveVaultNames(resolveVaultRepoUrls(resolveConfiguredVaults(overrides), overrides));
544
576
  // ponytail: vaultDir stays the primary vault; other vaults resolve through the registry
545
577
  const vaultDir = overrides.vaultDir || vaults[0].dir;
546
578
  const basePath = normalizeAppBasePath(process.env.BASE_PATH || '');
@@ -622,6 +654,7 @@ export function loadConfig(overrides = {}) {
622
654
  publicDir,
623
655
  vaultDir,
624
656
  vaults,
657
+ vaultDashboard: overrides.vaultDashboard ?? parseBooleanFlag(process.env.COLLABMD_VAULT_DASHBOARD),
625
658
  publicWsBaseUrl: process.env.PUBLIC_WS_BASE_URL || '',
626
659
  testWsRoomHydrateDelayMs: parsePositiveInt(process.env.TEST_WS_ROOM_HYDRATE_DELAY_MS, 0),
627
660
  wikiLinkAutoCreate: process.env.COLLABMD_WIKI_LINK_AUTO_CREATE !== 'false',
@@ -59,6 +59,7 @@ function buildRuntimeConfig({
59
59
  wikiLinkAutoCreate,
60
60
  wsBasePath,
61
61
  vaults,
62
+ vaultDashboard,
62
63
  }) {
63
64
  return `window.__COLLABMD_CONFIG__ = ${JSON.stringify({
64
65
  activeVault: vaults?.[0]?.id ?? '',
@@ -81,7 +82,8 @@ function buildRuntimeConfig({
81
82
  structurizrEnabled: Boolean(structurizr?.enabled ?? structurizr?.serverUrl),
82
83
  wikiLinkAutoCreate,
83
84
  wsBasePath,
84
- vaults: (vaults ?? []).map(({ id }) => ({ id })),
85
+ vaults: (vaults ?? []).map(({ id, name }) => ({ id, name: name || id })),
86
+ vaultDashboard: Boolean(vaultDashboard && !hosted?.enabled),
85
87
  })};\n`;
86
88
  }
87
89
 
@@ -454,7 +454,7 @@ async function handleSearch(req, res, requestUrl, { searchService }) {
454
454
 
455
455
  async function handleVaultList(req, res, _requestUrl, { config }) {
456
456
  // ponytail: ids only — absolute vault dirs are operator internals
457
- const vaults = (config?.vaults ?? []).map(({ id }) => ({ id }));
457
+ const vaults = (config?.vaults ?? []).map(({ id, name }) => ({ id, name: name || id }));
458
458
  jsonResponse(req, res, 200, { activeVault: vaults[0]?.id ?? null, vaults });
459
459
  }
460
460
 
@@ -1 +0,0 @@
1
- import{A as e,At as t,B as n,Ct as r,E as i,F as a,M as o,Ot as s,Pt as c,St as l,T as u,Tt as d,V as f,dt as p,gt as m,k as h,kt as g,lt as ee,mt as _,n as te,ut as ne,wt as v}from"./dist-Bog8M5U3.js";var re=e=>{let{state:t}=e,n=t.doc.lineAt(t.selection.main.from),r=b(e.state,n.from);return r.line?ie(e):r.block?oe(e):!1};function y(e,t){return({state:n,dispatch:r})=>{if(n.readOnly)return!1;let i=e(t,n);return i?(r(n.update(i)),!0):!1}}var ie=y(le,0),ae=y(S,0),oe=y((e,t)=>S(e,t,ce(t)),0);function b(e,t){let n=e.languageDataAt(`commentTokens`,t,1);return n.length?n[0]:{}}var x=50;function se(e,{open:t,close:n},r,i){let a=e.sliceDoc(r-x,r),o=e.sliceDoc(i,i+x),s=/\s*$/.exec(a)[0].length,c=/^\s*/.exec(o)[0].length,l=a.length-s;if(a.slice(l-t.length,l)==t&&o.slice(c,c+n.length)==n)return{open:{pos:r-s,margin:s&&1},close:{pos:i+c,margin:c&&1}};let u,d;i-r<=100?u=d=e.sliceDoc(r,i):(u=e.sliceDoc(r,r+x),d=e.sliceDoc(i-x,i));let f=/^\s*/.exec(u)[0].length,p=/\s*$/.exec(d)[0].length,m=d.length-p-n.length;return u.slice(f,f+t.length)==t&&d.slice(m,m+n.length)==n?{open:{pos:r+f+t.length,margin:+!!/\s/.test(u.charAt(f+t.length))},close:{pos:i-p-n.length,margin:+!!/\s/.test(d.charAt(m-1))}}:null}function ce(e){let t=[];for(let n of e.selection.ranges){let r=e.doc.lineAt(n.from),i=n.to<=r.to?r:e.doc.lineAt(n.to);i.from>r.from&&i.from==n.to&&(i=n.to==r.to+1?r:e.doc.lineAt(n.to-1));let a=t.length-1;a>=0&&t[a].to>r.from?t[a].to=i.to:t.push({from:r.from+/^\s*/.exec(r.text)[0].length,to:i.to})}return t}function S(e,t,n=t.selection.ranges){let r=n.map(e=>b(t,e.from).block);if(!r.every(e=>e))return null;let i=n.map((e,n)=>se(t,r[n],e.from,e.to));if(e!=2&&!i.every(e=>e))return{changes:t.changes(n.map((e,t)=>i[t]?[]:[{from:e.from,insert:r[t].open+` `},{from:e.to,insert:` `+r[t].close}]))};if(e!=1&&i.some(e=>e)){let e=[];for(let t=0,n;t<i.length;t++)if(n=i[t]){let i=r[t],{open:a,close:o}=n;e.push({from:a.pos-i.open.length,to:a.pos+a.margin},{from:o.pos-o.margin,to:o.pos+i.close.length})}return{changes:e}}return null}function le(e,t,n=t.selection.ranges){let r=[],i=-1;ranges:for(let{from:e,to:a}of n){let n=r.length,o=1e9,s;for(let n=e;n<=a;){let c=t.doc.lineAt(n);if(s==null&&(s=b(t,c.from).line,!s))continue ranges;if(c.from>i&&(e==a||a>c.from)){i=c.from;let e=/^\s*/.exec(c.text)[0].length,t=e==c.length,n=c.text.slice(e,e+s.length)==s?e:-1;e<c.text.length&&e<o&&(o=e),r.push({line:c,comment:n,token:s,indent:e,empty:t,single:!1})}n=c.to+1}if(o<1e9)for(let e=n;e<r.length;e++)r[e].indent<r[e].line.text.length&&(r[e].indent=o);r.length==n+1&&(r[n].single=!0)}if(e!=2&&r.some(e=>e.comment<0&&(!e.empty||e.single))){let e=[];for(let{line:t,token:n,indent:i,empty:a,single:o}of r)(o||!a)&&e.push({from:t.from+i,insert:n+` `});let n=t.changes(e);return{changes:n,selection:t.selection.map(n,1)}}if(e!=1&&r.some(e=>e.comment>=0)){let e=[];for(let{line:t,comment:n,token:i}of r)if(n>=0){let r=t.from+n,a=r+i.length;t.text[a-t.from]==` `&&a++,e.push({from:r,to:a})}return{changes:e}}return null}var C=ee.define(),ue=ee.define(),de=m.define(),w=m.define({combine(e){return s(e,{minDepth:100,newGroupDelay:500,joinToEvent:(e,t)=>t},{minDepth:Math.max,newGroupDelay:Math.min,joinToEvent:(e,t)=>(n,r)=>e(n,r)||t(n,r)})}}),fe=r.define({create(){return M.empty},update(e,t){let n=t.state.facet(w),r=t.annotation(C);if(r){let i=O.fromTransaction(t,r.selection),a=r.side,o=a==0?e.undone:e.done;return o=i?k(o,o.length,n.minDepth,i):be(o,t.startState.selection),new M(a==0?r.rest:o,a==0?o:r.rest)}let i=t.annotation(ue);if((i==`full`||i==`before`)&&(e=e.isolate()),t.annotation(d.addToHistory)===!1)return t.changes.empty?e:e.addMapping(t.changes.desc);let a=O.fromTransaction(t),o=t.annotation(d.time),s=t.annotation(d.userEvent);return a?e=e.addChanges(a,o,s,n,t):t.selection&&(e=e.addSelection(t.startState.selection,o,s,n.newGroupDelay)),(i==`full`||i==`after`)&&(e=e.isolate()),e},toJSON(e){return{done:e.done.map(e=>e.toJSON()),undone:e.undone.map(e=>e.toJSON())}},fromJSON(e){return new M(e.done.map(O.fromJSON),e.undone.map(O.fromJSON))}});function pe(e={}){return[fe,w.of(e),f.domEventHandlers({beforeinput(e,t){let n=e.inputType==`historyUndo`?E:e.inputType==`historyRedo`?D:null;return n?(e.preventDefault(),n(t)):!1}})]}function T(e,t){return function({state:n,dispatch:r}){if(!t&&n.readOnly)return!1;let i=n.field(fe,!1);if(!i)return!1;let a=i.pop(e,n,t);return a?(r(a),!0):!1}}var E=T(0,!1),D=T(1,!1),me=T(0,!0),he=T(1,!0),O=class e{constructor(e,t,n,r,i){this.changes=e,this.effects=t,this.mapped=n,this.startSelection=r,this.selectionsAfter=i}setSelAfter(t){return new e(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){return{changes:this.changes?.toJSON(),mapped:this.mapped?.toJSON(),startSelection:this.startSelection?.toJSON(),selectionsAfter:this.selectionsAfter.map(e=>e.toJSON())}}static fromJSON(t){return new e(t.changes&&p.fromJSON(t.changes),[],t.mapped&&ne.fromJSON(t.mapped),t.startSelection&&_.fromJSON(t.startSelection),t.selectionsAfter.map(_.fromJSON))}static fromTransaction(t,n){let r=A;for(let e of t.startState.facet(de)){let n=e(t);n.length&&(r=r.concat(n))}return!r.length&&t.changes.empty?null:new e(t.changes.invert(t.startState.doc),r,void 0,n||t.startState.selection,A)}static selection(t){return new e(void 0,A,void 0,void 0,t)}};function k(e,t,n,r){let i=t+1>n+20?t-n-1:0,a=e.slice(i,t);return a.push(r),a}function ge(e,t){let n=[],r=!1;return e.iterChangedRanges((e,t)=>n.push(e,t)),t.iterChangedRanges((e,t,i,a)=>{for(let e=0;e<n.length;){let t=n[e++],o=n[e++];a>=t&&i<=o&&(r=!0)}}),r}function _e(e,t){return e.ranges.length==t.ranges.length&&e.ranges.filter((e,n)=>e.empty!=t.ranges[n].empty).length===0}function ve(e,t){return e.length?t.length?e.concat(t):e:t}var A=[],ye=200;function be(e,t){if(e.length){let n=e[e.length-1],r=n.selectionsAfter.slice(Math.max(0,n.selectionsAfter.length-ye));return r.length&&r[r.length-1].eq(t)?e:(r.push(t),k(e,e.length-1,1e9,n.setSelAfter(r)))}return[O.selection([t])]}function xe(e){let t=e[e.length-1],n=e.slice();return n[e.length-1]=t.setSelAfter(t.selectionsAfter.slice(0,t.selectionsAfter.length-1)),n}function j(e,t){if(!e.length)return e;let n=e.length,r=A;for(;n;){let i=Se(e[n-1],t,r);if(i.changes&&!i.changes.empty||i.effects.length){let t=e.slice(0,n);return t[n-1]=i,t}t=i.mapped,n--,r=i.selectionsAfter}return r.length?[O.selection(r)]:A}function Se(e,t,n){let r=ve(e.selectionsAfter.length?e.selectionsAfter.map(e=>e.map(t)):A,n);if(!e.changes)return O.selection(r);let i=e.changes.map(t),a=t.mapDesc(e.changes,!0),o=e.mapped?e.mapped.composeDesc(a):a;return new O(i,l.mapEffects(e.effects,t),o,e.startSelection.map(a),r)}var Ce=/^(input\.type|delete)($|\.)/,M=class e{constructor(e,t,n=0,r=void 0){this.done=e,this.undone=t,this.prevTime=n,this.prevUserEvent=r}isolate(){return this.prevTime?new e(this.done,this.undone):this}addChanges(t,n,r,i,a){let o=this.done,s=o[o.length-1];return o=s&&s.changes&&!s.changes.empty&&t.changes&&(!r||Ce.test(r))&&(!s.selectionsAfter.length&&n-this.prevTime<i.newGroupDelay&&i.joinToEvent(a,ge(s.changes,t.changes))||r==`input.type.compose`)?k(o,o.length-1,i.minDepth,new O(t.changes.compose(s.changes),ve(l.mapEffects(t.effects,s.changes),s.effects),s.mapped,s.startSelection,A)):k(o,o.length,i.minDepth,t),new e(o,A,n,r)}addSelection(t,n,r,i){let a=this.done.length?this.done[this.done.length-1].selectionsAfter:A;return a.length>0&&n-this.prevTime<i&&r==this.prevUserEvent&&r&&/^select($|\.)/.test(r)&&_e(a[a.length-1],t)?this:new e(be(this.done,t),this.undone,n,r)}addMapping(t){return new e(j(this.done,t),j(this.undone,t),this.prevTime,this.prevUserEvent)}pop(e,t,n){let r=e==0?this.done:this.undone;if(r.length==0)return null;let i=r[r.length-1],a=i.selectionsAfter[0]||(i.startSelection?i.startSelection.map(i.changes.invertedDesc,1):t.selection);if(n&&i.selectionsAfter.length)return t.update({selection:i.selectionsAfter[i.selectionsAfter.length-1],annotations:C.of({side:e,rest:xe(r),selection:a}),userEvent:e==0?`select.undo`:`select.redo`,scrollIntoView:!0});if(i.changes){let n=r.length==1?A:r.slice(0,r.length-1);return i.mapped&&(n=j(n,i.mapped)),t.update({changes:i.changes,selection:i.startSelection,effects:i.effects,annotations:C.of({side:e,rest:n,selection:a}),filter:!1,userEvent:e==0?`undo`:`redo`,scrollIntoView:!0})}return null}};M.empty=new M(A,A);var we=[{key:`Mod-z`,run:E,preventDefault:!0},{key:`Mod-y`,mac:`Mod-Shift-z`,run:D,preventDefault:!0},{linux:`Ctrl-Shift-z`,run:D,preventDefault:!0},{key:`Mod-u`,run:me,preventDefault:!0},{key:`Alt-u`,mac:`Mod-Shift-u`,run:he,preventDefault:!0}];function N(e,t){return _.create(e.ranges.map(t),e.mainIndex)}function P(e,t){return e.update({selection:t,scrollIntoView:!0,userEvent:`select`})}function F({state:e,dispatch:t},n){let r=N(e.selection,n);return!r.eq(e.selection,!0)&&(t(P(e,r)),!0)}function I(e,t){return _.cursor(t?e.to:e.from)}function L(e,t){return F(e,n=>n.empty?e.moveByChar(n,t):I(n,t))}function R(e){return e.textDirectionAt(e.state.selection.main.head)==n.LTR}var z=e=>L(e,!R(e)),B=e=>L(e,R(e)),Te=e=>L(e,!1);function Ee(e,t){return F(e,n=>n.empty?e.moveByGroup(n,t):I(n,t))}var De=e=>Ee(e,!R(e)),Oe=e=>Ee(e,R(e));typeof Intl<`u`&&Intl.Segmenter;function ke(e,t,n){if(t.type.prop(n))return!0;let r=t.to-t.from;return r&&(r>2||/[^\s,.;:]/.test(e.sliceDoc(t.from,t.to)))||t.firstChild}function V(e,t,n){let r=a(e).resolveInner(t.head),i=n?c.closedBy:c.openedBy;for(let a=t.head;;){let t=n?r.childAfter(a):r.childBefore(a);if(!t)break;ke(e,t,i)?r=t:a=n?t.to:t.from}let s=r.type.prop(i),l,u;return u=s&&(l=n?o(e,r.from,1):o(e,r.to,-1))&&l.matched?n?l.end.to:l.end.from:n?r.to:r.from,_.cursor(u,n?-1:1)}var Ae=e=>F(e,t=>V(e.state,t,!R(e))),je=e=>F(e,t=>V(e.state,t,R(e)));function Me(e,t){return F(e,n=>{if(!n.empty)return I(n,t);let r=e.moveVertically(n,t);return r.head==n.head?e.moveToLineBoundary(n,t):r})}var Ne=e=>Me(e,!1),H=e=>Me(e,!0);function Pe(e){let t=e.scrollDOM.clientHeight<e.scrollDOM.scrollHeight-2,n=0,r=0,i;if(t){for(let t of e.state.facet(f.scrollMargins)){let i=t(e);i?.top&&(n=Math.max(i?.top,n)),i?.bottom&&(r=Math.max(i?.bottom,r))}i=e.scrollDOM.clientHeight-n-r}else i=(e.dom.ownerDocument.defaultView||window).innerHeight;return{marginTop:n,marginBottom:r,selfScroll:t,height:Math.max(e.defaultLineHeight,i-5)}}function Fe(e,t){let n=Pe(e),{state:r}=e,i=N(r.selection,r=>r.empty?e.moveVertically(r,t,n.height):I(r,t));if(i.eq(r.selection))return!1;let a;if(n.selfScroll){let t=e.coordsAtPos(r.selection.main.head),o=e.scrollDOM.getBoundingClientRect(),s=o.top+n.marginTop,c=o.bottom-n.marginBottom;t&&t.top>s&&t.bottom<c&&(a=f.scrollIntoView(i.main.head,{y:`start`,yMargin:t.top-s}))}return e.dispatch(P(r,i),{effects:a}),!0}var Ie=e=>Fe(e,!1),U=e=>Fe(e,!0);function W(e,t,n){let r=e.lineBlockAt(t.head),i=e.moveToLineBoundary(t,n);if(i.head==t.head&&i.head!=(n?r.to:r.from)&&(i=e.moveToLineBoundary(t,n,!1)),!n&&i.head==r.from&&r.length){let n=/^\s*/.exec(e.state.sliceDoc(r.from,Math.min(r.from+100,r.to)))[0].length;n&&t.head!=r.from+n&&(i=_.cursor(r.from+n))}return i}var Le=e=>F(e,t=>W(e,t,!0)),Re=e=>F(e,t=>W(e,t,!1)),ze=e=>F(e,t=>W(e,t,!R(e))),Be=e=>F(e,t=>W(e,t,R(e))),Ve=e=>F(e,t=>_.cursor(e.lineBlockAt(t.head).from,1)),He=e=>F(e,t=>_.cursor(e.lineBlockAt(t.head).to,-1));function Ue(e,t,n){let r=!1,i=N(e.selection,t=>{let i=o(e,t.head,-1)||o(e,t.head,1)||t.head>0&&o(e,t.head-1,1)||t.head<e.doc.length&&o(e,t.head+1,-1);if(!i||!i.end)return t;r=!0;let a=i.start.from==t.head?i.end.to:i.end.from;return n?_.range(t.anchor,a):_.cursor(a)});return r?(t(P(e,i)),!0):!1}var We=({state:e,dispatch:t})=>Ue(e,t,!1);function G(e,t,n){let r=N(e.state.selection,e=>{e.undirectional&&e.head>=e.anchor!=t&&(e=_.range(e.head,e.anchor));let r=n(e);return _.range(e.anchor,r.head,r.goalColumn,r.bidiLevel||void 0,r.assoc)});return!r.eq(e.state.selection)&&(e.dispatch(P(e.state,r)),!0)}function Ge(e,t){return G(e,t,n=>e.moveByChar(n,t))}var Ke=e=>Ge(e,!R(e)),qe=e=>Ge(e,R(e));function Je(e,t){return G(e,t,n=>e.moveByGroup(n,t))}var Ye=e=>Je(e,!R(e)),Xe=e=>Je(e,R(e)),Ze=e=>{let t=!R(e);return G(e,t,n=>V(e.state,n,t))},Qe=e=>{let t=R(e);return G(e,t,n=>V(e.state,n,t))};function $e(e,t){return G(e,t,n=>e.moveVertically(n,t))}var et=e=>$e(e,!1),tt=e=>$e(e,!0);function nt(e,t){return G(e,t,n=>e.moveVertically(n,t,Pe(e).height))}var rt=e=>nt(e,!1),it=e=>nt(e,!0),at=e=>G(e,!0,t=>W(e,t,!0)),ot=e=>G(e,!1,t=>W(e,t,!1)),st=e=>{let t=!R(e);return G(e,t,n=>W(e,n,t))},ct=e=>{let t=R(e);return G(e,t,n=>W(e,n,t))},lt=e=>G(e,!1,t=>_.cursor(e.lineBlockAt(t.head).from)),ut=e=>G(e,!0,t=>_.cursor(e.lineBlockAt(t.head).to)),dt=({state:e,dispatch:t})=>(t(P(e,{anchor:0})),!0),ft=({state:e,dispatch:t})=>(t(P(e,{anchor:e.doc.length})),!0),pt=({state:e,dispatch:t})=>(t(P(e,{anchor:e.selection.main.anchor,head:0})),!0),mt=({state:e,dispatch:t})=>(t(P(e,{anchor:e.selection.main.anchor,head:e.doc.length})),!0),ht=({state:e,dispatch:t})=>(t(e.update({selection:{anchor:0,head:e.doc.length},userEvent:`select`})),!0),gt=({state:e,dispatch:t})=>{let n=Y(e).map(({from:t,to:n})=>_.undirectionalRange(t,Math.min(n+1,e.doc.length)));return t(e.update({selection:_.create(n),userEvent:`select`})),!0},_t=({state:e,dispatch:t})=>{let n=N(e.selection,t=>{let n=a(e),r=n.resolveStack(t.from,1);if(t.empty){let e=n.resolveStack(t.from,-1);e.node.from>=r.node.from&&e.node.to<=r.node.to&&(r=e)}for(let e=r;e;e=e.next){let{node:n}=e;if((n.from<t.from&&n.to>=t.to||n.to>t.to&&n.from<=t.from)&&e.next)return _.undirectionalRange(n.from,n.to)}return t});return!n.eq(e.selection)&&(t(P(e,n)),!0)};function vt(e,t){let{state:n}=e,r=n.selection,i=n.selection.ranges.slice();for(let r of n.selection.ranges){let a=n.doc.lineAt(r.head);if(t?a.to<e.state.doc.length:a.from>0)for(let n=r;;){let r=e.moveVertically(n,t);if(r.head<a.from||r.head>a.to){i.some(e=>e.head==r.head)||i.push(r);break}if(r.head==n.head)break;n=r}}return i.length!=r.ranges.length&&(e.dispatch(P(n,_.create(i,i.length-1))),!0)}var yt=e=>vt(e,!1),bt=e=>vt(e,!0),xt=({state:e,dispatch:t})=>{let n=e.selection,r=null;return n.ranges.length>1?r=_.create([n.main]):n.main.empty||(r=_.create([_.cursor(n.main.head)])),r?(t(P(e,r)),!0):!1};function K(e,t){if(e.state.readOnly)return!1;let n=`delete.selection`,{state:r}=e,i=r.changeByRange(r=>{let{from:i,to:a}=r;if(i==a){let o=t(r);o<i?(n=`delete.backward`,o=q(e,o,!1)):o>i&&(n=`delete.forward`,o=q(e,o,!0)),i=Math.min(i,o),a=Math.max(a,o)}else i=q(e,i,!1),a=q(e,a,!0);return i==a?{range:r}:{changes:{from:i,to:a},range:_.cursor(i,i<r.head?-1:1)}});return!i.changes.empty&&(e.dispatch(r.update(i,{scrollIntoView:!0,userEvent:n,effects:n==`delete.selection`?f.announce.of(r.phrase(`Selection deleted`)):void 0})),!0)}function q(e,t,n){if(e instanceof f)for(let r of e.state.facet(f.atomicRanges).map(t=>t(e)))r.between(t,t,(e,r)=>{e<t&&r>t&&(t=n?r:e)});return t}var St=(e,n,r)=>K(e,i=>{let a=i.from,{state:o}=e,s=o.doc.lineAt(a),c,l;if(r&&!n&&a>s.from&&a<s.from+200&&!/[^ \t]/.test(c=s.text.slice(0,a-s.from))){if(c[c.length-1]==` `)return a-1;let e=g(c,o.tabSize)%u(o)||u(o);for(let t=0;t<e&&c[c.length-1-t]==` `;t++)a--;l=a}else l=t(s.text,a-s.from,n,n)+s.from,l==a&&s.number!=(n?o.doc.lines:1)?l+=n?1:-1:!n&&/[\ufe00-\ufe0f]/.test(s.text.slice(l-s.from,a-s.from))&&(l=t(s.text,l-s.from,!1,!1)+s.from);return l}),J=e=>St(e,!1,!0),Ct=e=>St(e,!0,!1),wt=(e,n)=>K(e,r=>{let i=r.head,{state:a}=e,o=a.doc.lineAt(i),s=a.charCategorizer(i);for(let e=null;;){if(i==(n?o.to:o.from)){i==r.head&&o.number!=(n?a.doc.lines:1)&&(i+=n?1:-1);break}let c=t(o.text,i-o.from,n)+o.from,l=o.text.slice(Math.min(i,c)-o.from,Math.max(i,c)-o.from),u=s(l);if(e!=null&&u!=e)break;(l!=` `||i!=r.head)&&(e=u),i=c}return i}),Tt=e=>wt(e,!1),Et=e=>wt(e,!0),Dt=e=>K(e,t=>{let n=e.lineBlockAt(t.head).to;return t.head<n?n:Math.min(e.state.doc.length,t.head+1)}),Ot=e=>K(e,t=>{let n=e.moveToLineBoundary(t,!1).head;return t.head>n?n:Math.max(0,t.head-1)}),kt=e=>K(e,t=>{let n=e.moveToLineBoundary(t,!0).head;return t.head<n?n:Math.min(e.state.doc.length,t.head+1)}),At=({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=e.changeByRange(e=>({changes:{from:e.from,to:e.to,insert:v.of([``,``])},range:_.cursor(e.from)}));return t(e.update(n,{scrollIntoView:!0,userEvent:`input`})),!0},jt=({state:e,dispatch:n})=>{if(e.readOnly)return!1;let r=e.changeByRange(n=>{if(!n.empty||n.from==0||n.from==e.doc.length)return{range:n};let r=n.from,i=e.doc.lineAt(r),a=r==i.from?r-1:t(i.text,r-i.from,!1)+i.from,o=r==i.to?r+1:t(i.text,r-i.from,!0)+i.from;return{changes:{from:a,to:o,insert:e.doc.slice(r,o).append(e.doc.slice(a,r))},range:_.cursor(o)}});return!r.changes.empty&&(n(e.update(r,{scrollIntoView:!0,userEvent:`move.character`})),!0)};function Y(e){let t=[],n=-1;for(let r of e.selection.ranges){let i=e.doc.lineAt(r.from),a=e.doc.lineAt(r.to);if(!r.empty&&r.to==a.from&&(a=e.doc.lineAt(r.to-1)),n>=i.number){let e=t[t.length-1];e.to=a.to,e.ranges.push(r)}else t.push({from:i.from,to:a.to,ranges:[r]});n=a.number+1}return t}function Mt(e,t,n){if(e.readOnly)return!1;let r=[],i=[];for(let t of Y(e)){if(n?t.to==e.doc.length:t.from==0)continue;let a=e.doc.lineAt(n?t.to+1:t.from-1),o=a.length+1;if(n){r.push({from:t.to,to:a.to},{from:t.from,insert:a.text+e.lineBreak});for(let n of t.ranges)i.push(_.range(Math.min(e.doc.length,n.anchor+o),Math.min(e.doc.length,n.head+o)))}else{r.push({from:a.from,to:t.from},{from:t.to,insert:e.lineBreak+a.text});for(let e of t.ranges)i.push(_.range(e.anchor-o,e.head-o))}}return r.length?(t(e.update({changes:r,scrollIntoView:!0,selection:_.create(i,e.selection.mainIndex),userEvent:`move.line`})),!0):!1}var Nt=({state:e,dispatch:t})=>Mt(e,t,!1),Pt=({state:e,dispatch:t})=>Mt(e,t,!0);function Ft(e,t,n){if(e.readOnly)return!1;let r=[];for(let t of Y(e))n?r.push({from:t.from,insert:e.doc.slice(t.from,t.to)+e.lineBreak}):r.push({from:t.to,insert:e.lineBreak+e.doc.slice(t.from,t.to)});let i=e.changes(r);return t(e.update({changes:i,selection:e.selection.map(i,n?1:-1),scrollIntoView:!0,userEvent:`input.copyline`})),!0}var It=({state:e,dispatch:t})=>Ft(e,t,!1),Lt=({state:e,dispatch:t})=>Ft(e,t,!0),Rt=e=>{if(e.state.readOnly)return!1;let{state:t}=e,n=t.changes(Y(t).map(({from:e,to:n})=>(e>0?e--:n<t.doc.length&&n++,{from:e,to:n}))),r=N(t.selection,t=>{let n;if(e.lineWrapping){let r=e.lineBlockAt(t.head),i=e.coordsAtPos(t.head,t.assoc||1);i&&(n=r.bottom+e.documentTop-i.bottom+e.defaultLineHeight/2)}return e.moveVertically(t,!0,n)}).map(n);return e.dispatch({changes:n,selection:r,scrollIntoView:!0,userEvent:`delete.line`}),!0};function zt(e,t){if(/\(\)|\[\]|\{\}/.test(e.sliceDoc(t-1,t+1)))return{from:t,to:t};let n=a(e).resolveInner(t),r=n.childBefore(t),i=n.childAfter(t),o;return r&&i&&r.to<=t&&i.from>=t&&(o=r.type.prop(c.closedBy))&&o.indexOf(i.name)>-1&&e.doc.lineAt(r.to).from==e.doc.lineAt(i.from).from&&!/\S/.test(e.sliceDoc(r.to,i.from))?{from:r.to,to:i.from}:null}var X=Vt(!1),Bt=Vt(!0);function Vt(e){return({state:t,dispatch:n})=>{if(t.readOnly)return!1;let r=t.changeByRange(n=>{let{from:r,to:a}=n,o=t.doc.lineAt(r),s=!e&&r==a&&zt(t,r);e&&(r=a=(a<=o.to?o:t.doc.lineAt(a)).to);let c=new te(t,{simulateBreak:r,simulateDoubleBreak:!!s}),l=i(c,r);for(l??=g(/^\s*/.exec(t.doc.lineAt(r).text)[0],t.tabSize);a<o.to&&/\s/.test(o.text[a-o.from]);)a++;s?{from:r,to:a}=s:r>o.from&&r<o.from+100&&!/\S/.test(o.text.slice(0,r))&&(r=o.from);let u=[``,h(t,l)];return s&&u.push(h(t,c.lineIndent(o.from,-1))),{changes:{from:r,to:a,insert:v.of(u)},range:_.cursor(r+1+u[1].length)}});return n(t.update(r,{scrollIntoView:!0,userEvent:`input`})),!0}}function Z(e,t){let n=-1;return e.changeByRange(r=>{let i=[];for(let a=r.from;a<=r.to;){let o=e.doc.lineAt(a);o.number>n&&(r.empty||r.to>o.from)&&(t(o,i,r),n=o.number),a=o.to+1}let a=e.changes(i);return{changes:i,range:_.range(a.mapPos(r.anchor,1),a.mapPos(r.head,1))}})}var Ht=({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=Object.create(null),r=new te(e,{overrideIndentation:e=>n[e]??-1}),a=Z(e,(t,a,o)=>{let s=i(r,t.from);if(s==null)return;/\S/.test(t.text)||(s=0);let c=/^\s*/.exec(t.text)[0],l=h(e,s);(c!=l||o.from<t.from+c.length)&&(n[t.from]=s,a.push({from:t.from,to:t.from+c.length,insert:l}))});return a.changes.empty||t(e.update(a,{userEvent:`indent`})),!0},Q=({state:t,dispatch:n})=>!t.readOnly&&(n(t.update(Z(t,(n,r)=>{r.push({from:n.from,insert:t.facet(e)})}),{userEvent:`input.indent`})),!0),$=({state:e,dispatch:t})=>!e.readOnly&&(t(e.update(Z(e,(t,n)=>{let r=/^\s*/.exec(t.text)[0];if(!r)return;let i=g(r,e.tabSize),a=0,o=h(e,Math.max(0,i-u(e)));for(;a<r.length&&a<o.length&&r.charCodeAt(a)==o.charCodeAt(a);)a++;n.push({from:t.from+a,to:t.from+r.length,insert:o.slice(a)})}),{userEvent:`delete.dedent`})),!0),Ut=e=>(e.setTabFocusMode(),!0),Wt=[{key:`Ctrl-b`,run:z,shift:Ke,preventDefault:!0},{key:`Ctrl-f`,run:B,shift:qe},{key:`Ctrl-p`,run:Ne,shift:et},{key:`Ctrl-n`,run:H,shift:tt},{key:`Ctrl-a`,run:Ve,shift:lt},{key:`Ctrl-e`,run:He,shift:ut},{key:`Ctrl-d`,run:Ct},{key:`Ctrl-h`,run:J},{key:`Ctrl-k`,run:Dt},{key:`Ctrl-Alt-h`,run:Tt},{key:`Ctrl-o`,run:At},{key:`Ctrl-t`,run:jt},{key:`Ctrl-v`,run:U}],Gt=[{key:`ArrowLeft`,run:z,shift:Ke,preventDefault:!0},{key:`Mod-ArrowLeft`,mac:`Alt-ArrowLeft`,run:De,shift:Ye,preventDefault:!0},{mac:`Cmd-ArrowLeft`,run:ze,shift:st,preventDefault:!0},{key:`ArrowRight`,run:B,shift:qe,preventDefault:!0},{key:`Mod-ArrowRight`,mac:`Alt-ArrowRight`,run:Oe,shift:Xe,preventDefault:!0},{mac:`Cmd-ArrowRight`,run:Be,shift:ct,preventDefault:!0},{key:`ArrowUp`,run:Ne,shift:et,preventDefault:!0},{mac:`Cmd-ArrowUp`,run:dt,shift:pt},{mac:`Ctrl-ArrowUp`,run:Ie,shift:rt},{key:`ArrowDown`,run:H,shift:tt,preventDefault:!0},{mac:`Cmd-ArrowDown`,run:ft,shift:mt},{mac:`Ctrl-ArrowDown`,run:U,shift:it},{key:`PageUp`,run:Ie,shift:rt},{key:`PageDown`,run:U,shift:it},{key:`Home`,run:Re,shift:ot,preventDefault:!0},{key:`Mod-Home`,run:dt,shift:pt},{key:`End`,run:Le,shift:at,preventDefault:!0},{key:`Mod-End`,run:ft,shift:mt},{key:`Enter`,run:X,shift:X},{key:`Mod-a`,run:ht},{key:`Backspace`,run:J,shift:J,preventDefault:!0},{key:`Delete`,run:Ct,preventDefault:!0},{key:`Mod-Backspace`,mac:`Alt-Backspace`,run:Tt,preventDefault:!0},{key:`Mod-Delete`,mac:`Alt-Delete`,run:Et,preventDefault:!0},{mac:`Mod-Backspace`,run:Ot,preventDefault:!0},{mac:`Mod-Delete`,run:kt,preventDefault:!0}].concat(Wt.map(e=>({mac:e.key,run:e.run,shift:e.shift}))),Kt=[{key:`Alt-ArrowLeft`,mac:`Ctrl-ArrowLeft`,run:Ae,shift:Ze},{key:`Alt-ArrowRight`,mac:`Ctrl-ArrowRight`,run:je,shift:Qe},{key:`Alt-ArrowUp`,run:Nt},{key:`Shift-Alt-ArrowUp`,run:It},{key:`Alt-ArrowDown`,run:Pt},{key:`Shift-Alt-ArrowDown`,run:Lt},{key:`Mod-Alt-ArrowUp`,run:yt},{key:`Mod-Alt-ArrowDown`,run:bt},{key:`Escape`,run:xt},{key:`Mod-Enter`,run:Bt},{key:`Alt-l`,mac:`Ctrl-l`,run:gt},{key:`Mod-i`,run:_t,preventDefault:!0},{key:`Mod-[`,run:$},{key:`Mod-]`,run:Q},{key:`Mod-Alt-\\`,run:Ht},{key:`Shift-Mod-k`,run:Rt},{key:`Shift-Mod-\\`,run:We},{key:`Mod-/`,run:re},{key:`Alt-A`,mac:`Ctrl-A`,run:ae},{key:`Ctrl-m`,mac:`Shift-Alt-m`,run:Ut}].concat(Gt),qt={key:`Tab`,run:Q,shift:$};export{Kt as a,$ as c,qt as d,X as f,E as h,Le as i,Q as l,ie as m,z as n,pe as o,D as p,Re as r,we as s,Te as t,Ht as u};
@@ -1 +0,0 @@
1
- import{a as e}from"./dist-Cu5Ze2cs.js";export{e as markdown};
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-jVKfryIL.js";export{e as css};
@@ -1 +0,0 @@
1
- import{n as e}from"./dist-W0FoeIxk.js";export{e as javascript};
@@ -1 +0,0 @@
1
- import{t as e}from"./dist-zHDLm1oE.js";export{e as yaml};
@@ -1 +0,0 @@
1
- import{n as e}from"./dist-B_1hMrc6.js";export{e as html};
@@ -1 +0,0 @@
1
- import{i as e,y as t}from"./chunk-4SOEAONT-C3fufoVl.js";import"./chunk-SRAX5OIU-KFQikY-4.js";export{t as decodePngMetadata,e as encodePngMetadata};