@bendyline/docblocks-react 2.7.3 → 2.7.5

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 (37) hide show
  1. package/THIRD_PARTY_NOTICES.txt +56 -25
  2. package/dist/{AiDiagrams-QE3LVYFK.js → AiDiagrams-5P657GFC.js} +3 -3
  3. package/dist/{AiEditorAssistant-YLJ5OZK2.js → AiEditorAssistant-V4MZRC6Q.js} +5 -5
  4. package/dist/{AiStudyDialog-4TQZZGU3.js → AiStudyDialog-GSCFTG3G.js} +2 -2
  5. package/dist/{AudioExportDialog-F23QXNPB.js → AudioExportDialog-IOYIEAER.js} +1 -1
  6. package/dist/{DocumentConflictDialog-5LRVIGXS.js → DocumentConflictDialog-DZ2BV2IH.js} +1 -1
  7. package/dist/ExportDialog-LGWBXIWN.js +9 -0
  8. package/dist/{ExportToolbarControls-A2YBKUUD.js → ExportToolbarControls-HOV424TO.js} +84 -13
  9. package/dist/{GitUI-RCWUZVS6.js → GitUI-F3CNBURE.js} +1 -1
  10. package/dist/{KeyboardShortcutsDialog-G5OBDTNR.js → KeyboardShortcutsDialog-HVKDFP5Z.js} +1 -1
  11. package/dist/{PresentationMaker-P2JP6UUX.js → PresentationMaker-H5B44Q7P.js} +3 -3
  12. package/dist/{QuickOpenDialog-2KK4SEBU.js → QuickOpenDialog-ENVFGIHS.js} +1 -1
  13. package/dist/{Settings-ByNImjk5.d.ts → Settings-CfhY489G.d.ts} +1 -1
  14. package/dist/{ShareDialog-XUA2OVGI.js → ShareDialog-PF7D7ORP.js} +1 -1
  15. package/dist/{SpeechToolbarControl-CLU7RB2G.js → SpeechToolbarControl-KNI2C4JJ.js} +1 -1
  16. package/dist/{TextFromNarration-NITJQ4IB.js → TextFromNarration-EYOMW2IW.js} +1 -1
  17. package/dist/{chunk-6PA7QBZT.js → chunk-7CSBRAMT.js} +20 -4
  18. package/dist/{chunk-YPIVCCTB.js → chunk-CUCWCZRO.js} +8 -14
  19. package/dist/{chunk-ZMCQO57Q.js → chunk-FBPCURCS.js} +8 -1
  20. package/dist/{chunk-Y5OE3HJC.js → chunk-GFK4TCKN.js} +11 -5
  21. package/dist/chunk-H3BK5MK6.js +34 -0
  22. package/dist/{chunk-3CE5RH2B.js → chunk-I4JURPYS.js} +2 -2
  23. package/dist/{chunk-6B7G33D5.js → chunk-KBUHWZCF.js} +1 -1
  24. package/dist/{chunk-EOU2JWAQ.js → chunk-USKX66SA.js} +91 -14
  25. package/dist/{chunk-56KO2LYN.js → chunk-VJPGZCFS.js} +4 -0
  26. package/dist/editor.js +3 -3
  27. package/dist/export/index.d.ts +9 -2
  28. package/dist/export/index.js +4 -4
  29. package/dist/index.d.ts +13 -4
  30. package/dist/index.js +1292 -869
  31. package/dist/{run-export-OHRNT2TP.js → run-export-SPNNH32M.js} +1 -1
  32. package/dist/settings/index.d.ts +1 -1
  33. package/dist/settings/index.js +2 -2
  34. package/package.json +8 -8
  35. package/src/styles/docblocks.css +73 -14
  36. package/dist/ExportDialog-JRVZCG3J.js +0 -9
  37. package/dist/chunk-YBEYTVU2.js +0 -12
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-DICE7VBJ.js";
18
18
  import {
19
19
  useConfirmDialog
20
- } from "./chunk-6B7G33D5.js";
20
+ } from "./chunk-KBUHWZCF.js";
21
21
  import {
22
22
  createSpeechInputProvider
23
23
  } from "./chunk-KBDZDFPU.js";
@@ -51,7 +51,7 @@ import {
51
51
  } from "./chunk-KWG6UP5O.js";
52
52
  import {
53
53
  runExport
54
- } from "./chunk-6PA7QBZT.js";
54
+ } from "./chunk-7CSBRAMT.js";
55
55
  import {
56
56
  createImageSaveOutput
57
57
  } from "./chunk-UONFWKJ7.js";
@@ -87,7 +87,7 @@ import {
87
87
  saveProofingPreferences,
88
88
  saveThemePreference,
89
89
  saveWriteCanvasPreferences
90
- } from "./chunk-ZMCQO57Q.js";
90
+ } from "./chunk-FBPCURCS.js";
91
91
  import {
92
92
  FindButton,
93
93
  focusFindInput,
@@ -100,18 +100,18 @@ import {
100
100
  matchShortcut,
101
101
  shortcutLabel
102
102
  } from "./chunk-ACQF63SB.js";
103
- import "./chunk-EOU2JWAQ.js";
103
+ import "./chunk-USKX66SA.js";
104
104
  import {
105
105
  buildIssueReportUrl,
106
106
  displayBuildDate,
107
107
  localDateStamp,
108
108
  openExternalUrl
109
- } from "./chunk-YPIVCCTB.js";
109
+ } from "./chunk-CUCWCZRO.js";
110
110
  import "./chunk-K4I3NJFL.js";
111
111
  import {
112
112
  ExportDialog
113
- } from "./chunk-Y5OE3HJC.js";
114
- import "./chunk-YBEYTVU2.js";
113
+ } from "./chunk-GFK4TCKN.js";
114
+ import "./chunk-H3BK5MK6.js";
115
115
  import {
116
116
  DEFAULT_OPTIONS,
117
117
  loadLastExportOptions
@@ -119,13 +119,14 @@ import {
119
119
  import {
120
120
  Dialog,
121
121
  dismissTopmostDialog
122
- } from "./chunk-56KO2LYN.js";
122
+ } from "./chunk-VJPGZCFS.js";
123
123
 
124
124
  // src/FileExplorer/FileExplorer.tsx
125
125
  import { useCallback as useCallback5, useEffect as useEffect5, useMemo, useRef as useRef5, useState as useState4 } from "react";
126
126
  import {
127
127
  isFileSystemMoveStateError,
128
- parseWorkspacePath as parseWorkspacePath4
128
+ parseWorkspacePath as parseWorkspacePath4,
129
+ tryParseWorkspacePath as tryParseWorkspacePath2
129
130
  } from "@bendyline/docblocks/filesystem";
130
131
 
131
132
  // src/FileExplorer/useFileTree.ts
@@ -139,6 +140,7 @@ import {
139
140
  workspacePathDirname
140
141
  } from "@bendyline/docblocks/filesystem";
141
142
  var DIRECTORY_NOT_FOUND_RETRY_DELAYS_MS = Object.freeze([40, 120]);
143
+ var AUTO_RETRY_DELAYS_MS = Object.freeze([1e3, 4e3, 15e3]);
142
144
  function normalisePath(path) {
143
145
  return parseWorkspacePath(path);
144
146
  }
@@ -167,10 +169,13 @@ function sameEntry(left, right) {
167
169
  if (left.kind !== right.kind || left.name !== right.name || left.path !== right.path)
168
170
  return false;
169
171
  if (left.kind === "file" && right.kind === "file") {
170
- return left.lastModified === right.lastModified;
172
+ return left.lastModified === right.lastModified && left.readIssue?.code === right.readIssue?.code && left.readIssue?.message === right.readIssue?.message;
171
173
  }
172
174
  return true;
173
175
  }
176
+ function hasUnreadableFile(entries) {
177
+ return entries.some((entry) => entry.kind === "file" && entry.readIssue !== void 0);
178
+ }
174
179
  function reconcileEntries(current, incoming) {
175
180
  const currentByPath = new Map(current.map((entry) => [entry.path, entry]));
176
181
  const reconciled = incoming.map((entry) => {
@@ -189,7 +194,8 @@ async function readProviderDirectory(provider, path) {
189
194
  kind: entry.kind,
190
195
  name: entry.name,
191
196
  path: entry.path,
192
- ...entry.kind === "file" ? { lastModified: entry.lastModified } : {}
197
+ ...entry.kind === "file" ? { lastModified: entry.lastModified } : {},
198
+ ...entry.kind === "file" && entry.readIssue ? { readIssue: entry.readIssue } : {}
193
199
  }));
194
200
  };
195
201
  for (let attempt = 0; ; attempt += 1) {
@@ -237,64 +243,111 @@ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
237
243
  providerRef.current = provider;
238
244
  const directoryRequestSequenceRef = useRef(0);
239
245
  const directoryRequestsRef = useRef(/* @__PURE__ */ new Map());
246
+ const autoRetriesRef = useRef(/* @__PURE__ */ new Map());
247
+ const autoRetryLoadRef = useRef(async () => void 0);
240
248
  const reportRootIssue = useCallback((caught) => {
241
249
  setRootIssue(readIssue(caught, ""));
242
250
  }, []);
243
- const loadRoot = useCallback(async (showLoading = true) => {
244
- const sourceProvider = providerRef.current;
245
- if (!sourceProvider) {
246
- setEntries([]);
247
- setLoading(false);
248
- return;
249
- }
250
- const requestId = ++directoryRequestSequenceRef.current;
251
- directoryRequestsRef.current.set("", requestId);
252
- const isCurrent = () => providerRef.current === sourceProvider && directoryRequestsRef.current.get("") === requestId;
253
- if (showLoading) setLoading(true);
254
- try {
255
- const root = await readProviderDirectory(sourceProvider, "");
256
- if (!isCurrent()) return;
257
- setEntries((current) => reconcileEntries(current, root));
258
- setRootIssue(null);
259
- } catch (caught) {
260
- if (isCurrent()) setRootIssue(readIssue(caught, ""));
261
- } finally {
262
- if (isCurrent()) setLoading(false);
263
- }
251
+ const cancelAutoRetry = useCallback((directoryPath) => {
252
+ const state = autoRetriesRef.current.get(directoryPath);
253
+ if (state?.timer) clearTimeout(state.timer);
254
+ autoRetriesRef.current.delete(directoryPath);
264
255
  }, []);
265
- const loadChildren = useCallback(async (dirPath) => {
266
- const sourceProvider = providerRef.current;
267
- if (!sourceProvider) return;
268
- const canonical = normalisePath(dirPath);
269
- const requestId = ++directoryRequestSequenceRef.current;
270
- directoryRequestsRef.current.set(canonical, requestId);
271
- const isCurrent = () => providerRef.current === sourceProvider && directoryRequestsRef.current.get(canonical) === requestId;
272
- try {
273
- const children = await readProviderDirectory(sourceProvider, dirPath);
274
- if (!isCurrent()) return;
275
- setChildEntries((prev) => {
276
- const current = prev.get(canonical) ?? [];
277
- const reconciled = reconcileEntries(current, children);
278
- if (prev.has(canonical) && reconciled === current) return prev;
279
- const next = new Map(prev);
280
- next.set(canonical, reconciled);
281
- return next;
282
- });
283
- setChildIssues((prev) => {
284
- if (!prev.has(canonical)) return prev;
285
- const next = new Map(prev);
286
- next.delete(canonical);
287
- return next;
288
- });
289
- } catch (caught) {
290
- if (!isCurrent()) return;
291
- setChildIssues((prev) => {
292
- const next = new Map(prev);
293
- next.set(canonical, readIssue(caught, dirPath));
294
- return next;
295
- });
256
+ const cancelAllAutoRetries = useCallback(() => {
257
+ for (const state of autoRetriesRef.current.values()) {
258
+ if (state.timer) clearTimeout(state.timer);
296
259
  }
260
+ autoRetriesRef.current.clear();
297
261
  }, []);
262
+ const settleAutoRetry = useCallback(
263
+ (directoryPath, needsRetry) => {
264
+ if (!needsRetry) {
265
+ cancelAutoRetry(directoryPath);
266
+ return;
267
+ }
268
+ const current = autoRetriesRef.current.get(directoryPath);
269
+ if (current?.timer) return;
270
+ const attempts = current?.attempts ?? 0;
271
+ const delay = AUTO_RETRY_DELAYS_MS[attempts];
272
+ if (delay === void 0) return;
273
+ const next = { attempts: attempts + 1, timer: null };
274
+ next.timer = setTimeout(() => {
275
+ next.timer = null;
276
+ void autoRetryLoadRef.current(directoryPath);
277
+ }, delay);
278
+ autoRetriesRef.current.set(directoryPath, next);
279
+ },
280
+ [cancelAutoRetry]
281
+ );
282
+ const loadRoot = useCallback(
283
+ async (showLoading = true) => {
284
+ const sourceProvider = providerRef.current;
285
+ if (!sourceProvider) {
286
+ setEntries([]);
287
+ setLoading(false);
288
+ return;
289
+ }
290
+ const requestId = ++directoryRequestSequenceRef.current;
291
+ directoryRequestsRef.current.set("", requestId);
292
+ const isCurrent = () => providerRef.current === sourceProvider && directoryRequestsRef.current.get("") === requestId;
293
+ if (showLoading) setLoading(true);
294
+ try {
295
+ const root = await readProviderDirectory(sourceProvider, "");
296
+ if (!isCurrent()) return;
297
+ setEntries((current) => reconcileEntries(current, root));
298
+ setRootIssue(null);
299
+ settleAutoRetry("", hasUnreadableFile(root));
300
+ } catch (caught) {
301
+ if (isCurrent()) {
302
+ const issue = readIssue(caught, "");
303
+ setRootIssue(issue);
304
+ settleAutoRetry("", issue.retryable);
305
+ }
306
+ } finally {
307
+ if (isCurrent()) setLoading(false);
308
+ }
309
+ },
310
+ [settleAutoRetry]
311
+ );
312
+ const loadChildren = useCallback(
313
+ async (dirPath) => {
314
+ const sourceProvider = providerRef.current;
315
+ if (!sourceProvider) return;
316
+ const canonical = normalisePath(dirPath);
317
+ const requestId = ++directoryRequestSequenceRef.current;
318
+ directoryRequestsRef.current.set(canonical, requestId);
319
+ const isCurrent = () => providerRef.current === sourceProvider && directoryRequestsRef.current.get(canonical) === requestId;
320
+ try {
321
+ const children = await readProviderDirectory(sourceProvider, dirPath);
322
+ if (!isCurrent()) return;
323
+ setChildEntries((prev) => {
324
+ const current = prev.get(canonical) ?? [];
325
+ const reconciled = reconcileEntries(current, children);
326
+ if (prev.has(canonical) && reconciled === current) return prev;
327
+ const next = new Map(prev);
328
+ next.set(canonical, reconciled);
329
+ return next;
330
+ });
331
+ setChildIssues((prev) => {
332
+ if (!prev.has(canonical)) return prev;
333
+ const next = new Map(prev);
334
+ next.delete(canonical);
335
+ return next;
336
+ });
337
+ settleAutoRetry(canonical, hasUnreadableFile(children));
338
+ } catch (caught) {
339
+ if (!isCurrent()) return;
340
+ const issue = readIssue(caught, dirPath);
341
+ setChildIssues((prev) => {
342
+ const next = new Map(prev);
343
+ next.set(canonical, issue);
344
+ return next;
345
+ });
346
+ settleAutoRetry(canonical, issue.retryable);
347
+ }
348
+ },
349
+ [settleAutoRetry]
350
+ );
298
351
  useEffect(() => {
299
352
  setEntries([]);
300
353
  setExpanded(/* @__PURE__ */ new Set());
@@ -304,8 +357,10 @@ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
304
357
  setSelectedKind(null);
305
358
  setRootIssue(null);
306
359
  directoryRequestsRef.current.clear();
360
+ cancelAllAutoRetries();
307
361
  void loadRoot();
308
- }, [provider, loadRoot]);
362
+ return cancelAllAutoRetries;
363
+ }, [provider, loadRoot, cancelAllAutoRetries]);
309
364
  const toggleExpand = useCallback(
310
365
  (path) => {
311
366
  setExpanded((prev) => {
@@ -352,10 +407,12 @@ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
352
407
  }, [loadRoot, loadChildren, expanded]);
353
408
  const retryDirectory = useCallback(
354
409
  async (path) => {
355
- if (normalisePath(path) === "") await loadRoot();
410
+ const canonical = normalisePath(path);
411
+ cancelAutoRetry(canonical);
412
+ if (canonical === "") await loadRoot();
356
413
  else await loadChildren(path);
357
414
  },
358
- [loadRoot, loadChildren]
415
+ [loadRoot, loadChildren, cancelAutoRetry]
359
416
  );
360
417
  const refreshRef = useRef(refresh);
361
418
  refreshRef.current = refresh;
@@ -366,6 +423,7 @@ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
366
423
  },
367
424
  [loadChildren, loadRoot]
368
425
  );
426
+ autoRetryLoadRef.current = refreshDirectory;
369
427
  const previousMetadataRefreshKeyRef = useRef(metadataRefreshKey);
370
428
  useEffect(() => {
371
429
  if (Object.is(previousMetadataRefreshKeyRef.current, metadataRefreshKey)) return;
@@ -542,12 +600,13 @@ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
542
600
  setExpanded(nextExpanded);
543
601
  setChildEntries(/* @__PURE__ */ new Map());
544
602
  setChildIssues(/* @__PURE__ */ new Map());
603
+ cancelAllAutoRetries();
545
604
  await loadRoot();
546
605
  for (const dirPath of nextExpanded) {
547
606
  await loadChildren(dirPath);
548
607
  }
549
608
  },
550
- [selectedPath, entries, childEntries, expanded, loadRoot, loadChildren]
609
+ [selectedPath, entries, childEntries, expanded, loadRoot, loadChildren, cancelAllAutoRetries]
551
610
  );
552
611
  return {
553
612
  entries,
@@ -788,6 +847,11 @@ function FileTreeNode({
788
847
  }) {
789
848
  const [renaming, setRenaming] = useState2(false);
790
849
  const [renameValue, setRenameValue] = useState2(entry.name);
850
+ const readIssue2 = entry.kind === "file" ? entry.readIssue : void 0;
851
+ const rowStatus = [
852
+ badge?.label,
853
+ readIssue2 ? `could not be read: ${readIssue2.message}` : void 0
854
+ ].filter((part) => part !== void 0);
791
855
  const [showContext, setShowContext] = useState2(false);
792
856
  const [contextPos, setContextPos] = useState2({ x: 0, y: 0 });
793
857
  const [actionError, setActionError] = useState2(null);
@@ -1034,7 +1098,7 @@ function FileTreeNode({
1034
1098
  "data-path": entry.path,
1035
1099
  "aria-expanded": isDir ? expanded : void 0,
1036
1100
  "aria-selected": selected,
1037
- "aria-label": badge ? `${entry.name}, ${badge.label}` : void 0,
1101
+ "aria-label": rowStatus.length > 0 ? `${entry.name}, ${rowStatus.join(", ")}` : void 0,
1038
1102
  "aria-level": depth + 1,
1039
1103
  "aria-posinset": posInSet,
1040
1104
  "aria-setsize": setSize,
@@ -1086,6 +1150,15 @@ function FileTreeNode({
1086
1150
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1087
1151
  /* @__PURE__ */ jsx2("span", { className: "db-tree-label", children: displayFileName(entry.name) }),
1088
1152
  badge && /* @__PURE__ */ jsx2("span", { className: `db-git-badge db-git-badge--${badge.kind}`, "aria-hidden": "true", children: badge.glyph }),
1153
+ readIssue2 && /* @__PURE__ */ jsx2(
1154
+ "span",
1155
+ {
1156
+ className: "db-tree-read-issue",
1157
+ title: `Could not read this file: ${readIssue2.message}`,
1158
+ "aria-hidden": "true",
1159
+ children: "!"
1160
+ }
1161
+ ),
1089
1162
  entry.kind === "file" && entry.lastModified && /* @__PURE__ */ jsx2(LastModifiedTime, { value: entry.lastModified }),
1090
1163
  /* @__PURE__ */ jsx2(
1091
1164
  "button",
@@ -1717,6 +1790,7 @@ import {
1717
1790
  FsError as FsError3,
1718
1791
  SQUISQ_RUNTIME_DIRECTORY_NAME,
1719
1792
  getFileSystemProviderV2 as getFileSystemProviderV23,
1793
+ findWikiArticle,
1720
1794
  parseWorkspacePath as parseWorkspacePath3
1721
1795
  } from "@bendyline/docblocks/filesystem";
1722
1796
  import {
@@ -2149,10 +2223,7 @@ async function writeRuntimeIfNeeded(provider, runtimePath) {
2149
2223
  if (current !== PLAYER_BUNDLE) await writeProviderText(provider, runtimePath, PLAYER_BUNDLE);
2150
2224
  }
2151
2225
  async function renderPlainOutsideInHtml(provider, layout, markdown, themeId) {
2152
- const [{ parseMarkdown }, { markdownDocToPlainHtml }] = await Promise.all([
2153
- import("@bendyline/squisq/markdown"),
2154
- import("@bendyline/squisq-formats/html")
2155
- ]);
2226
+ const { markdownDocToPlainHtml } = await import("@bendyline/squisq-formats/html");
2156
2227
  const container = new FileSystemContentContainer(provider, layout.companionDirectory);
2157
2228
  const images = /* @__PURE__ */ new Map();
2158
2229
  for (const entry of await container.listFiles()) {
@@ -2161,7 +2232,7 @@ async function renderPlainOutsideInHtml(provider, layout, markdown, themeId) {
2161
2232
  images.set(`./${entry.path}`, outputPath);
2162
2233
  }
2163
2234
  return new TextEncoder().encode(
2164
- markdownDocToPlainHtml(parseMarkdown(markdown), {
2235
+ markdownDocToPlainHtml(markdown, {
2165
2236
  title: layout.stem,
2166
2237
  images: images.size > 0 ? images : void 0,
2167
2238
  ...themeId ? { themeId } : {}
@@ -2171,9 +2242,22 @@ async function renderPlainOutsideInHtml(provider, layout, markdown, themeId) {
2171
2242
  async function prepareOutsideInRender(provider, layout, markdown, render) {
2172
2243
  const themeId = await resolveFallbackRenderTheme(markdown, render);
2173
2244
  const htmlOutput = layout.format === "html" ? await readOutsideInHtmlOutput(markdown) : null;
2245
+ let renderedSource = markdown;
2246
+ if (layout.format === "html") {
2247
+ const { parseMarkdown, resolveWikiLinks } = await import("@bendyline/squisq/markdown");
2248
+ renderedSource = await resolveWikiLinks(parseMarkdown(markdown), async (target) => {
2249
+ const article = await findWikiArticle(provider, layout.targetPath, target, ".html");
2250
+ return article.exists ? article.relativePath.split("/").map(encodeURIComponent).join("/") : null;
2251
+ });
2252
+ }
2174
2253
  if (htmlOutput === "static") {
2175
2254
  return {
2176
- bytes: await renderPlainOutsideInHtml(provider, layout, markdown, themeId),
2255
+ bytes: await renderPlainOutsideInHtml(
2256
+ provider,
2257
+ layout,
2258
+ renderedSource,
2259
+ themeId
2260
+ ),
2177
2261
  runtimePath: null
2178
2262
  };
2179
2263
  }
@@ -2181,7 +2265,7 @@ async function prepareOutsideInRender(provider, layout, markdown, render) {
2181
2265
  const outputDirectory = dirname(layout.targetPath);
2182
2266
  const rendered = await renderOutsideInDocument(
2183
2267
  {
2184
- markdown,
2268
+ markdown: renderedSource,
2185
2269
  targetPath: layout.targetPath,
2186
2270
  container: new FileSystemContentContainer(provider, layout.companionDirectory)
2187
2271
  },
@@ -2416,6 +2500,10 @@ var NEW_ITEM_HINT_ID = "db-new-item-hint";
2416
2500
  function normalisePath2(path) {
2417
2501
  return parseWorkspacePath4(path);
2418
2502
  }
2503
+ function readIssueText(issue) {
2504
+ const failingPath = tryParseWorkspacePath2(issue.path);
2505
+ return failingPath && failingPath !== issue.directoryPath ? `${failingPath}: ${issue.message}` : issue.message;
2506
+ }
2419
2507
  function hasEquivalentPath2(paths, path) {
2420
2508
  const canonical = normalisePath2(path);
2421
2509
  return paths.has(path) || paths.has(canonical) || canonical !== "" && paths.has(`/${canonical}`);
@@ -2880,7 +2968,7 @@ function FileExplorer({
2880
2968
  role: "alert",
2881
2969
  "data-directory-path": childIssue.directoryPath,
2882
2970
  children: [
2883
- /* @__PURE__ */ jsx5("span", { children: childIssue.message }),
2971
+ /* @__PURE__ */ jsx5("span", { children: readIssueText(childIssue) }),
2884
2972
  " ",
2885
2973
  /* @__PURE__ */ jsx5(
2886
2974
  "button",
@@ -3161,7 +3249,7 @@ function FileExplorer({
3161
3249
  role: "alert",
3162
3250
  "data-directory-path": tree.rootIssue.directoryPath,
3163
3251
  children: [
3164
- /* @__PURE__ */ jsx5("span", { children: tree.rootIssue.message }),
3252
+ /* @__PURE__ */ jsx5("span", { children: readIssueText(tree.rootIssue) }),
3165
3253
  " ",
3166
3254
  /* @__PURE__ */ jsx5(
3167
3255
  "button",
@@ -3670,11 +3758,11 @@ async function prepareHostLifecycle(session, request) {
3670
3758
 
3671
3759
  // src/DocBlocksShell/DocBlocksShell.tsx
3672
3760
  import {
3673
- useState as useState23,
3674
- useCallback as useCallback18,
3675
- useEffect as useEffect29,
3761
+ useState as useState24,
3762
+ useCallback as useCallback19,
3763
+ useEffect as useEffect30,
3676
3764
  useLayoutEffect as useLayoutEffect4,
3677
- useRef as useRef19,
3765
+ useRef as useRef20,
3678
3766
  useMemo as useMemo6,
3679
3767
  lazy as lazy2,
3680
3768
  Suspense as Suspense2
@@ -3686,13 +3774,15 @@ import {
3686
3774
  FileSystemContentContainer as FileSystemContentContainer2,
3687
3775
  createFileMediaProvider,
3688
3776
  decodeUtf8Text,
3689
- FsError as FsError5,
3690
- getFileSystemProviderV2 as getFileSystemProviderV210,
3777
+ FsError as FsError6,
3778
+ getFileSystemProviderV2 as getFileSystemProviderV211,
3691
3779
  isQuotaExceededError,
3692
3780
  moveFileSystemEntry as moveFileSystemEntry2,
3693
3781
  rewriteCompanionReferences,
3694
3782
  parseWorkspacePath as parseWorkspacePath8,
3695
- workspacePathContains
3783
+ workspacePathContains,
3784
+ findWikiArticle as findWikiArticle2,
3785
+ suggestWikiArticles
3696
3786
  } from "@bendyline/docblocks/filesystem";
3697
3787
  import {
3698
3788
  createFileSystemDocumentTarget as createFileSystemDocumentTarget2,
@@ -3715,16 +3805,18 @@ import {
3715
3805
  getWorkspace,
3716
3806
  listWorkspaces as listWorkspaces2,
3717
3807
  reconcileElectronWorkspaceDescriptors,
3718
- removeWorkspace,
3719
- saveWorkspace as saveWorkspace2,
3808
+ removeWorkspace as removeWorkspace2,
3809
+ saveWorkspace as saveWorkspace3,
3720
3810
  touchWorkspace as touchWorkspace2,
3721
3811
  registerTransientWorkspace,
3722
3812
  getTransientWorkspace,
3723
- unregisterTransientWorkspace
3813
+ unregisterTransientWorkspace,
3814
+ appendWorkspaceBackup,
3815
+ finishWorkspaceBackup
3724
3816
  } from "@bendyline/docblocks/workspace";
3725
3817
 
3726
3818
  // src/AppMenu/AppMenu.tsx
3727
- import { useState as useState7, useCallback as useCallback8, useRef as useRef8, useEffect as useEffect8 } from "react";
3819
+ import { useState as useState8, useCallback as useCallback9, useRef as useRef9, useEffect as useEffect9 } from "react";
3728
3820
  import {
3729
3821
  getHostEnvironment
3730
3822
  } from "@bendyline/docblocks/host";
@@ -4041,8 +4133,87 @@ function SpeechSettingsControls({ speech }) {
4041
4133
  ] });
4042
4134
  }
4043
4135
 
4136
+ // src/Settings/TraySettings.tsx
4137
+ import { useCallback as useCallback8, useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
4138
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4139
+ function trayPlace(platform) {
4140
+ switch (platform) {
4141
+ case "darwin":
4142
+ return { legend: "Menu bar", label: "Show DocBlocks in the menu bar" };
4143
+ case "win32":
4144
+ return {
4145
+ legend: "Notification area",
4146
+ label: "Show DocBlocks in the notification area"
4147
+ };
4148
+ default:
4149
+ return { legend: "System tray", label: "Show DocBlocks in the system tray" };
4150
+ }
4151
+ }
4152
+ function TraySettingsControls({ menu, platform }) {
4153
+ const [enabled, setEnabled] = useState7(null);
4154
+ const [saving, setSaving] = useState7(false);
4155
+ const [failure, setFailure] = useState7(null);
4156
+ const mounted = useRef8(true);
4157
+ useEffect8(() => {
4158
+ mounted.current = true;
4159
+ const read2 = menu.getTrayEnabled;
4160
+ if (read2) {
4161
+ read2.call(menu).then(
4162
+ (value) => {
4163
+ if (mounted.current) setEnabled(value);
4164
+ },
4165
+ () => {
4166
+ if (mounted.current) setFailure("DocBlocks could not read this setting.");
4167
+ }
4168
+ );
4169
+ }
4170
+ return () => {
4171
+ mounted.current = false;
4172
+ };
4173
+ }, [menu]);
4174
+ const change = useCallback8(
4175
+ async (next) => {
4176
+ const write2 = menu.setTrayEnabled;
4177
+ if (!write2) return;
4178
+ setSaving(true);
4179
+ setFailure(null);
4180
+ setEnabled(next);
4181
+ try {
4182
+ const saved = await write2.call(menu, next);
4183
+ if (mounted.current) setEnabled(saved);
4184
+ } catch {
4185
+ if (mounted.current) {
4186
+ setEnabled(!next);
4187
+ setFailure("DocBlocks could not change this setting. Try again.");
4188
+ }
4189
+ } finally {
4190
+ if (mounted.current) setSaving(false);
4191
+ }
4192
+ },
4193
+ [menu]
4194
+ );
4195
+ const place = trayPlace(platform);
4196
+ return /* @__PURE__ */ jsxs7("fieldset", { className: "db-settings-fieldset", children: [
4197
+ /* @__PURE__ */ jsx8("legend", { className: "db-settings-legend", children: place.legend }),
4198
+ /* @__PURE__ */ jsxs7("label", { className: "db-settings-checkbox", children: [
4199
+ /* @__PURE__ */ jsx8(
4200
+ "input",
4201
+ {
4202
+ type: "checkbox",
4203
+ checked: enabled === true,
4204
+ disabled: enabled === null || saving,
4205
+ onChange: (event) => void change(event.currentTarget.checked)
4206
+ }
4207
+ ),
4208
+ place.label
4209
+ ] }),
4210
+ /* @__PURE__ */ jsx8("p", { className: "db-settings-hint", children: "One click to a new document, a folder, or any document you have pinned." }),
4211
+ failure && /* @__PURE__ */ jsx8("p", { className: "db-settings-hint db-settings-ai-error", role: "alert", children: failure })
4212
+ ] });
4213
+ }
4214
+
4044
4215
  // src/AppMenu/AppMenu.tsx
4045
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4216
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4046
4217
  function formatBytes(bytes) {
4047
4218
  if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
4048
4219
  let value = bytes;
@@ -4077,6 +4248,7 @@ function AppMenu({
4077
4248
  onVersioningPreferenceChange,
4078
4249
  onOpenWorkspaceSettings,
4079
4250
  onDownloadAllWorkspaces,
4251
+ onRestoreBackup,
4080
4252
  onShowKeyboardShortcuts,
4081
4253
  onKeepBrowserData,
4082
4254
  onInstallApp,
@@ -4086,21 +4258,22 @@ function AppMenu({
4086
4258
  appBuildDate,
4087
4259
  ai,
4088
4260
  speech,
4261
+ tray,
4089
4262
  settingsRequest = 0
4090
4263
  }) {
4091
- const [isOpen, setIsOpen] = useState7(false);
4092
- const [showAbout, setShowAbout] = useState7(false);
4093
- const [showSettings, setShowSettings] = useState7(false);
4094
- useEffect8(() => {
4264
+ const [isOpen, setIsOpen] = useState8(false);
4265
+ const [showAbout, setShowAbout] = useState8(false);
4266
+ const [showSettings, setShowSettings] = useState8(false);
4267
+ useEffect9(() => {
4095
4268
  if (settingsRequest > 0) setShowSettings(true);
4096
4269
  }, [settingsRequest]);
4097
- const [requestingPersistentStorage, setRequestingPersistentStorage] = useState7(false);
4270
+ const [requestingPersistentStorage, setRequestingPersistentStorage] = useState8(false);
4098
4271
  const surface = getHostEnvironment()?.surface ?? "web";
4099
4272
  const moreInformationUrl = MORE_INFORMATION_URLS[surface] ?? MORE_INFORMATION_URLS.web;
4100
- const containerRef = useRef8(null);
4273
+ const containerRef = useRef9(null);
4101
4274
  const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } = useMenuKeyboard(isOpen, setIsOpen);
4102
- const [storageEstimate, setStorageEstimate] = useState7(null);
4103
- useEffect8(() => {
4275
+ const [storageEstimate, setStorageEstimate] = useState8(null);
4276
+ useEffect9(() => {
4104
4277
  if (!showSettings || !getStorageEstimate) return;
4105
4278
  let cancelled = false;
4106
4279
  getStorageEstimate().then((estimate) => {
@@ -4112,7 +4285,7 @@ function AppMenu({
4112
4285
  cancelled = true;
4113
4286
  };
4114
4287
  }, [showSettings, getStorageEstimate]);
4115
- useEffect8(() => {
4288
+ useEffect9(() => {
4116
4289
  if (!isOpen) return;
4117
4290
  function handleOutsideClick(e) {
4118
4291
  if (containerRef.current && !containerRef.current.contains(e.target)) {
@@ -4122,14 +4295,14 @@ function AppMenu({
4122
4295
  document.addEventListener("mousedown", handleOutsideClick);
4123
4296
  return () => document.removeEventListener("mousedown", handleOutsideClick);
4124
4297
  }, [closeMenu, isOpen]);
4125
- const handleAction = useCallback8(
4298
+ const handleAction = useCallback9(
4126
4299
  (action) => {
4127
4300
  closeMenu(true);
4128
4301
  action();
4129
4302
  },
4130
4303
  [closeMenu]
4131
4304
  );
4132
- const handleKeepBrowserDataFromSettings = useCallback8(async () => {
4305
+ const handleKeepBrowserDataFromSettings = useCallback9(async () => {
4133
4306
  if (!onKeepBrowserData || requestingPersistentStorage) return;
4134
4307
  setShowSettings(false);
4135
4308
  setRequestingPersistentStorage(true);
@@ -4139,9 +4312,9 @@ function AppMenu({
4139
4312
  setRequestingPersistentStorage(false);
4140
4313
  }
4141
4314
  }, [onKeepBrowserData, requestingPersistentStorage]);
4142
- return /* @__PURE__ */ jsxs7(Fragment5, { children: [
4143
- /* @__PURE__ */ jsxs7("div", { ref: containerRef, className: "db-app-menu", children: [
4144
- /* @__PURE__ */ jsxs7(
4315
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
4316
+ /* @__PURE__ */ jsxs8("div", { ref: containerRef, className: "db-app-menu", children: [
4317
+ /* @__PURE__ */ jsxs8(
4145
4318
  "button",
4146
4319
  {
4147
4320
  ref: triggerRef,
@@ -4151,8 +4324,8 @@ function AppMenu({
4151
4324
  "aria-expanded": isOpen,
4152
4325
  "aria-haspopup": "menu",
4153
4326
  children: [
4154
- logoUrl ? /* @__PURE__ */ jsxs7(Fragment5, { children: [
4155
- /* @__PURE__ */ jsx8(
4327
+ logoUrl ? /* @__PURE__ */ jsxs8(Fragment5, { children: [
4328
+ /* @__PURE__ */ jsx9(
4156
4329
  "img",
4157
4330
  {
4158
4331
  src: logoUrl,
@@ -4162,7 +4335,7 @@ function AppMenu({
4162
4335
  height: 242
4163
4336
  }
4164
4337
  ),
4165
- logoDarkUrl && /* @__PURE__ */ jsx8(
4338
+ logoDarkUrl && /* @__PURE__ */ jsx9(
4166
4339
  "img",
4167
4340
  {
4168
4341
  src: logoDarkUrl,
@@ -4172,8 +4345,8 @@ function AppMenu({
4172
4345
  height: 242
4173
4346
  }
4174
4347
  )
4175
- ] }) : /* @__PURE__ */ jsx8("span", { className: "db-app-menu-label", children: "docblocks" }),
4176
- /* @__PURE__ */ jsx8(
4348
+ ] }) : /* @__PURE__ */ jsx9("span", { className: "db-app-menu-label", children: "docblocks" }),
4349
+ /* @__PURE__ */ jsx9(
4177
4350
  "span",
4178
4351
  {
4179
4352
  className: `db-app-menu-caret${isOpen ? " db-app-menu-caret--open" : ""}`,
@@ -4183,7 +4356,7 @@ function AppMenu({
4183
4356
  ]
4184
4357
  }
4185
4358
  ),
4186
- isOpen && /* @__PURE__ */ jsxs7(
4359
+ isOpen && /* @__PURE__ */ jsxs8(
4187
4360
  "div",
4188
4361
  {
4189
4362
  ref: menuRef,
@@ -4191,7 +4364,7 @@ function AppMenu({
4191
4364
  role: "menu",
4192
4365
  onKeyDown: handleMenuKeyDown,
4193
4366
  children: [
4194
- /* @__PURE__ */ jsx8(
4367
+ /* @__PURE__ */ jsx9(
4195
4368
  "button",
4196
4369
  {
4197
4370
  className: "db-app-menu-item",
@@ -4201,7 +4374,7 @@ function AppMenu({
4201
4374
  children: "Settings"
4202
4375
  }
4203
4376
  ),
4204
- onInstallApp && /* @__PURE__ */ jsx8(
4377
+ onInstallApp && /* @__PURE__ */ jsx9(
4205
4378
  "button",
4206
4379
  {
4207
4380
  className: "db-app-menu-item",
@@ -4211,8 +4384,8 @@ function AppMenu({
4211
4384
  children: "Install DocBlocks\u2026"
4212
4385
  }
4213
4386
  ),
4214
- (onDownloadAllWorkspaces || onKeepBrowserData) && /* @__PURE__ */ jsx8("div", { className: "db-app-menu-divider" }),
4215
- onDownloadAllWorkspaces && /* @__PURE__ */ jsx8(
4387
+ (onDownloadAllWorkspaces || onRestoreBackup || onKeepBrowserData) && /* @__PURE__ */ jsx9("div", { className: "db-app-menu-divider" }),
4388
+ onDownloadAllWorkspaces && /* @__PURE__ */ jsx9(
4216
4389
  "button",
4217
4390
  {
4218
4391
  className: "db-app-menu-item",
@@ -4222,7 +4395,17 @@ function AppMenu({
4222
4395
  children: "Back up all workspaces"
4223
4396
  }
4224
4397
  ),
4225
- onKeepBrowserData && /* @__PURE__ */ jsx8(
4398
+ onRestoreBackup && /* @__PURE__ */ jsx9(
4399
+ "button",
4400
+ {
4401
+ className: "db-app-menu-item",
4402
+ role: "menuitem",
4403
+ tabIndex: -1,
4404
+ onClick: () => handleAction(onRestoreBackup),
4405
+ children: "Restore backup\u2026"
4406
+ }
4407
+ ),
4408
+ onKeepBrowserData && /* @__PURE__ */ jsx9(
4226
4409
  "button",
4227
4410
  {
4228
4411
  className: "db-app-menu-item",
@@ -4232,8 +4415,8 @@ function AppMenu({
4232
4415
  children: "Protect data from browser cleanup"
4233
4416
  }
4234
4417
  ),
4235
- /* @__PURE__ */ jsx8("div", { className: "db-app-menu-divider" }),
4236
- onShowKeyboardShortcuts && /* @__PURE__ */ jsxs7(
4418
+ /* @__PURE__ */ jsx9("div", { className: "db-app-menu-divider" }),
4419
+ onShowKeyboardShortcuts && /* @__PURE__ */ jsxs8(
4237
4420
  "button",
4238
4421
  {
4239
4422
  className: "db-app-menu-item db-app-menu-item--with-shortcut",
@@ -4243,11 +4426,11 @@ function AppMenu({
4243
4426
  onClick: () => handleAction(onShowKeyboardShortcuts),
4244
4427
  children: [
4245
4428
  "Keyboard shortcuts",
4246
- /* @__PURE__ */ jsx8("span", { className: "db-app-menu-shortcut", "aria-hidden": "true", children: "F1" })
4429
+ /* @__PURE__ */ jsx9("span", { className: "db-app-menu-shortcut", "aria-hidden": "true", children: "F1" })
4247
4430
  ]
4248
4431
  }
4249
4432
  ),
4250
- /* @__PURE__ */ jsx8(
4433
+ /* @__PURE__ */ jsx9(
4251
4434
  "a",
4252
4435
  {
4253
4436
  className: "db-app-menu-item",
@@ -4260,7 +4443,7 @@ function AppMenu({
4260
4443
  children: "Documentation"
4261
4444
  }
4262
4445
  ),
4263
- /* @__PURE__ */ jsx8(
4446
+ /* @__PURE__ */ jsx9(
4264
4447
  "button",
4265
4448
  {
4266
4449
  className: "db-app-menu-item",
@@ -4274,53 +4457,54 @@ function AppMenu({
4274
4457
  }
4275
4458
  )
4276
4459
  ] }),
4277
- showSettings && /* @__PURE__ */ jsxs7(SettingsDialog, { onClose: () => setShowSettings(false), children: [
4278
- /* @__PURE__ */ jsx8(
4460
+ showSettings && /* @__PURE__ */ jsxs8(SettingsDialog, { onClose: () => setShowSettings(false), children: [
4461
+ /* @__PURE__ */ jsx9(
4279
4462
  ThemeSettings,
4280
4463
  {
4281
4464
  value: themePreference,
4282
4465
  onChange: (preference) => onThemeChange?.(preference)
4283
4466
  }
4284
4467
  ),
4285
- /* @__PURE__ */ jsx8(
4468
+ /* @__PURE__ */ jsx9(
4286
4469
  AccentColorSettings,
4287
4470
  {
4288
4471
  value: accentColor,
4289
4472
  onChange: (color) => onAccentColorChange?.(color)
4290
4473
  }
4291
4474
  ),
4292
- /* @__PURE__ */ jsx8(
4475
+ /* @__PURE__ */ jsx9(
4293
4476
  InterfaceFontSettings,
4294
4477
  {
4295
4478
  value: interfaceFont,
4296
4479
  onChange: (font) => onInterfaceFontChange?.(font)
4297
4480
  }
4298
4481
  ),
4299
- /* @__PURE__ */ jsx8(
4482
+ /* @__PURE__ */ jsx9(
4300
4483
  WriteCanvasSettingsControls,
4301
4484
  {
4302
4485
  value: writeCanvasSettings,
4303
4486
  onChange: (settings) => onWriteCanvasSettingsChange?.(settings)
4304
4487
  }
4305
4488
  ),
4306
- /* @__PURE__ */ jsx8(
4489
+ /* @__PURE__ */ jsx9(
4307
4490
  ProofingSettingsControls,
4308
4491
  {
4309
4492
  value: proofingPreferences,
4310
4493
  onChange: (settings) => onProofingPreferencesChange?.(settings)
4311
4494
  }
4312
4495
  ),
4313
- ai && /* @__PURE__ */ jsx8(AiSettingsControls, { ai }),
4314
- speech && /* @__PURE__ */ jsx8(SpeechSettingsControls, { speech }),
4315
- getStorageEstimate && /* @__PURE__ */ jsxs7("fieldset", { className: "db-settings-fieldset", children: [
4316
- /* @__PURE__ */ jsx8("legend", { className: "db-settings-legend", children: "Storage" }),
4317
- /* @__PURE__ */ jsx8("p", { className: "db-settings-hint", children: storageEstimate ? `DocBlocks documents and app data are using ${formatBytes(
4496
+ ai && /* @__PURE__ */ jsx9(AiSettingsControls, { ai }),
4497
+ speech && /* @__PURE__ */ jsx9(SpeechSettingsControls, { speech }),
4498
+ tray && /* @__PURE__ */ jsx9(TraySettingsControls, { menu: tray, platform: getHostEnvironment()?.platform }),
4499
+ getStorageEstimate && /* @__PURE__ */ jsxs8("fieldset", { className: "db-settings-fieldset", children: [
4500
+ /* @__PURE__ */ jsx9("legend", { className: "db-settings-legend", children: "Storage" }),
4501
+ /* @__PURE__ */ jsx9("p", { className: "db-settings-hint", children: storageEstimate ? `DocBlocks documents and app data are using ${formatBytes(
4318
4502
  storageEstimate.usage
4319
4503
  )} of the ${formatBytes(
4320
4504
  storageEstimate.quota
4321
4505
  )} this browser allows for the site.` : "Storage usage is not available in this browser." }),
4322
- storagePersistent !== void 0 && /* @__PURE__ */ jsx8("p", { className: "db-settings-hint", children: storagePersistent ? "Protected from routine browser cleanup on this device." : "Browsers may clear site data under storage pressure unless protection is granted." }),
4323
- onKeepBrowserData && /* @__PURE__ */ jsx8(
4506
+ storagePersistent !== void 0 && /* @__PURE__ */ jsx9("p", { className: "db-settings-hint", children: storagePersistent ? "Protected from routine browser cleanup on this device." : "Browsers may clear site data under storage pressure unless protection is granted." }),
4507
+ onKeepBrowserData && /* @__PURE__ */ jsx9(
4324
4508
  "button",
4325
4509
  {
4326
4510
  type: "button",
@@ -4331,16 +4515,16 @@ function AppMenu({
4331
4515
  }
4332
4516
  )
4333
4517
  ] }),
4334
- onVersioningPreferenceChange && /* @__PURE__ */ jsxs7("fieldset", { className: "db-settings-fieldset", children: [
4335
- /* @__PURE__ */ jsx8("legend", { className: "db-settings-legend", children: "Version history" }),
4336
- /* @__PURE__ */ jsxs7("p", { className: "db-settings-hint", children: [
4518
+ onVersioningPreferenceChange && /* @__PURE__ */ jsxs8("fieldset", { className: "db-settings-fieldset", children: [
4519
+ /* @__PURE__ */ jsx9("legend", { className: "db-settings-legend", children: "Version history" }),
4520
+ /* @__PURE__ */ jsxs8("p", { className: "db-settings-hint", children: [
4337
4521
  "When on, DocBlocks keeps prior revisions of each document inside a sibling",
4338
4522
  " ",
4339
- /* @__PURE__ */ jsx8("code", { children: "<name>_files/.versions/" }),
4523
+ /* @__PURE__ */ jsx9("code", { children: "<name>_files/.versions/" }),
4340
4524
  " folder. Individual workspaces can override this default in their own settings."
4341
4525
  ] }),
4342
- /* @__PURE__ */ jsxs7("label", { className: "db-settings-radio", children: [
4343
- /* @__PURE__ */ jsx8(
4526
+ /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
4527
+ /* @__PURE__ */ jsx9(
4344
4528
  "input",
4345
4529
  {
4346
4530
  type: "radio",
@@ -4352,8 +4536,8 @@ function AppMenu({
4352
4536
  ),
4353
4537
  "On for all workspaces"
4354
4538
  ] }),
4355
- /* @__PURE__ */ jsxs7("label", { className: "db-settings-radio", children: [
4356
- /* @__PURE__ */ jsx8(
4539
+ /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
4540
+ /* @__PURE__ */ jsx9(
4357
4541
  "input",
4358
4542
  {
4359
4543
  type: "radio",
@@ -4365,8 +4549,8 @@ function AppMenu({
4365
4549
  ),
4366
4550
  "On in browser workspaces, off for local folders"
4367
4551
  ] }),
4368
- /* @__PURE__ */ jsxs7("label", { className: "db-settings-radio", children: [
4369
- /* @__PURE__ */ jsx8(
4552
+ /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
4553
+ /* @__PURE__ */ jsx9(
4370
4554
  "input",
4371
4555
  {
4372
4556
  type: "radio",
@@ -4378,7 +4562,7 @@ function AppMenu({
4378
4562
  ),
4379
4563
  "Off for all workspaces"
4380
4564
  ] }),
4381
- onOpenWorkspaceSettings && /* @__PURE__ */ jsx8(
4565
+ onOpenWorkspaceSettings && /* @__PURE__ */ jsx9(
4382
4566
  "button",
4383
4567
  {
4384
4568
  type: "button",
@@ -4392,38 +4576,38 @@ function AppMenu({
4392
4576
  )
4393
4577
  ] })
4394
4578
  ] }),
4395
- showAbout && /* @__PURE__ */ jsxs7(Dialog, { title: "About DocBlocks", onClose: () => setShowAbout(false), children: [
4396
- /* @__PURE__ */ jsxs7("p", { children: [
4397
- /* @__PURE__ */ jsx8("strong", { children: "DocBlocks" }),
4579
+ showAbout && /* @__PURE__ */ jsxs8(Dialog, { title: "About DocBlocks", onClose: () => setShowAbout(false), children: [
4580
+ /* @__PURE__ */ jsxs8("p", { children: [
4581
+ /* @__PURE__ */ jsx9("strong", { children: "DocBlocks" }),
4398
4582
  " is a local-first Markdown document editor. Your files stay under your control."
4399
4583
  ] }),
4400
- appVersion && /* @__PURE__ */ jsxs7("p", { className: "db-about-version", children: [
4401
- /* @__PURE__ */ jsx8("span", { children: "Version" }),
4402
- /* @__PURE__ */ jsx8("code", { "aria-label": `DocBlocks version ${appVersion}`, children: appVersion })
4584
+ appVersion && /* @__PURE__ */ jsxs8("p", { className: "db-about-version", children: [
4585
+ /* @__PURE__ */ jsx9("span", { children: "Version" }),
4586
+ /* @__PURE__ */ jsx9("code", { "aria-label": `DocBlocks version ${appVersion}`, children: appVersion })
4403
4587
  ] }),
4404
- appBuildDate && /* @__PURE__ */ jsxs7("p", { className: "db-about-version", children: [
4405
- /* @__PURE__ */ jsx8("span", { children: "Build" }),
4406
- /* @__PURE__ */ jsx8("time", { dateTime: appBuildDate, children: displayBuildDate(appBuildDate) })
4588
+ appBuildDate && /* @__PURE__ */ jsxs8("p", { className: "db-about-version", children: [
4589
+ /* @__PURE__ */ jsx9("span", { children: "Build" }),
4590
+ /* @__PURE__ */ jsx9("time", { dateTime: appBuildDate, children: displayBuildDate(appBuildDate) })
4407
4591
  ] }),
4408
- /* @__PURE__ */ jsxs7("p", { children: [
4592
+ /* @__PURE__ */ jsxs8("p", { children: [
4409
4593
  "Built with",
4410
4594
  " ",
4411
- /* @__PURE__ */ jsx8("a", { href: "https://github.com/bendyline/squisq", target: "_blank", rel: "noopener noreferrer", children: "squisq" }),
4595
+ /* @__PURE__ */ jsx9("a", { href: "https://github.com/bendyline/squisq", target: "_blank", rel: "noopener noreferrer", children: "squisq" }),
4412
4596
  " ",
4413
4597
  "by",
4414
4598
  " ",
4415
- /* @__PURE__ */ jsx8("a", { href: "https://bendyline.com", target: "_blank", rel: "noopener noreferrer", children: "Bendyline" }),
4599
+ /* @__PURE__ */ jsx9("a", { href: "https://bendyline.com", target: "_blank", rel: "noopener noreferrer", children: "Bendyline" }),
4416
4600
  "."
4417
4601
  ] }),
4418
- surface === "web" && /* @__PURE__ */ jsxs7("p", { children: [
4602
+ surface === "web" && /* @__PURE__ */ jsxs8("p", { children: [
4419
4603
  "See our free desktop apps at",
4420
4604
  " ",
4421
- /* @__PURE__ */ jsx8("a", { href: MORE_INFORMATION_URLS.electron, target: "_blank", rel: "noopener noreferrer", children: "docblocks.com/desktop" }),
4605
+ /* @__PURE__ */ jsx9("a", { href: MORE_INFORMATION_URLS.electron, target: "_blank", rel: "noopener noreferrer", children: "docblocks.com/desktop" }),
4422
4606
  "."
4423
4607
  ] }),
4424
- /* @__PURE__ */ jsxs7("p", { className: "db-dialog-links", children: [
4425
- /* @__PURE__ */ jsx8("a", { href: moreInformationUrl, target: "_blank", rel: "noopener noreferrer", children: "More information..." }),
4426
- /* @__PURE__ */ jsx8(
4608
+ /* @__PURE__ */ jsxs8("p", { className: "db-dialog-links", children: [
4609
+ /* @__PURE__ */ jsx9("a", { href: moreInformationUrl, target: "_blank", rel: "noopener noreferrer", children: "More information..." }),
4610
+ /* @__PURE__ */ jsx9(
4427
4611
  "a",
4428
4612
  {
4429
4613
  href: "https://github.com/bendyline/docblocks",
@@ -4432,7 +4616,7 @@ function AppMenu({
4432
4616
  children: "GitHub"
4433
4617
  }
4434
4618
  ),
4435
- /* @__PURE__ */ jsx8(
4619
+ /* @__PURE__ */ jsx9(
4436
4620
  "a",
4437
4621
  {
4438
4622
  href: "https://github.com/bendyline/docblocks/releases",
@@ -4441,7 +4625,7 @@ function AppMenu({
4441
4625
  children: "Release notes"
4442
4626
  }
4443
4627
  ),
4444
- /* @__PURE__ */ jsx8(
4628
+ /* @__PURE__ */ jsx9(
4445
4629
  "a",
4446
4630
  {
4447
4631
  href: "https://github.com/bendyline/docblocks/issues",
@@ -4450,7 +4634,7 @@ function AppMenu({
4450
4634
  children: "Support"
4451
4635
  }
4452
4636
  ),
4453
- /* @__PURE__ */ jsx8(
4637
+ /* @__PURE__ */ jsx9(
4454
4638
  "a",
4455
4639
  {
4456
4640
  href: "https://github.com/bendyline/docblocks/blob/main/LICENSE",
@@ -4459,7 +4643,7 @@ function AppMenu({
4459
4643
  children: "License (MIT)"
4460
4644
  }
4461
4645
  ),
4462
- /* @__PURE__ */ jsx8(
4646
+ /* @__PURE__ */ jsx9(
4463
4647
  "a",
4464
4648
  {
4465
4649
  href: "https://github.com/bendyline/docblocks/blob/main/NOTICE.md",
@@ -4473,19 +4657,72 @@ function AppMenu({
4473
4657
  ] });
4474
4658
  }
4475
4659
 
4660
+ // src/DocBlocksShell/restore-backup.ts
4661
+ import {
4662
+ readWorkspaceBackup,
4663
+ restoreWorkspaceBackup,
4664
+ WORKSPACE_BACKUP_LIMITS,
4665
+ saveWorkspace as saveWorkspace2,
4666
+ removeWorkspace
4667
+ } from "@bendyline/docblocks/workspace";
4668
+ async function restoreBrowserBackup(file) {
4669
+ if (file.size > 256 * 1024 * 1024) throw new Error("Backup ZIPs must be smaller than 256 MiB.");
4670
+ const { zipToContainer } = await import("@bendyline/squisq-formats/container");
4671
+ const container = await zipToContainer(await file.arrayBuffer(), WORKSPACE_BACKUP_LIMITS);
4672
+ const workspaces = await readWorkspaceBackup(
4673
+ container,
4674
+ file.name.replace(/\.zip$/i, "") || "Workspace"
4675
+ );
4676
+ const { IndexedDBFileSystemProvider, deleteIndexedDBWorkspaceData } = await import("@bendyline/docblocks/filesystem/indexeddb");
4677
+ const staged = [];
4678
+ try {
4679
+ for (const workspace of workspaces) {
4680
+ const descriptor = {
4681
+ id: `restored-${crypto.randomUUID()}`,
4682
+ name: `${workspace.name.slice(0, 1013)} (restored)`,
4683
+ type: "indexeddb",
4684
+ lastOpened: (/* @__PURE__ */ new Date()).toISOString()
4685
+ };
4686
+ const provider = new IndexedDBFileSystemProvider(descriptor.id, descriptor.name).v2;
4687
+ staged.push({ descriptor, provider });
4688
+ await restoreWorkspaceBackup(workspace, provider);
4689
+ }
4690
+ for (const { descriptor } of staged) await saveWorkspace2(descriptor);
4691
+ return staged.map(({ descriptor }) => descriptor);
4692
+ } catch (error) {
4693
+ const failures = [error];
4694
+ for (const { descriptor, provider } of staged) {
4695
+ try {
4696
+ await provider.dispose();
4697
+ await removeWorkspace(descriptor.id);
4698
+ await deleteIndexedDBWorkspaceData(descriptor.id);
4699
+ } catch (cleanupError) {
4700
+ failures.push(cleanupError);
4701
+ }
4702
+ }
4703
+ if (failures.length > 1)
4704
+ throw new Error(
4705
+ `Restore failed; temporary workspace cleanup also failed. ${failures.map((failure) => failure instanceof Error ? failure.message : String(failure)).join("; ")}`
4706
+ );
4707
+ throw error;
4708
+ } finally {
4709
+ for (const { provider } of staged) await provider.dispose();
4710
+ }
4711
+ }
4712
+
4476
4713
  // src/WorkspacePicker/WorkspaceSettingsButton.tsx
4477
- import { useState as useState8, useCallback as useCallback9, useRef as useRef9, useEffect as useEffect9 } from "react";
4478
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4714
+ import { useState as useState9, useCallback as useCallback10, useRef as useRef10, useEffect as useEffect10 } from "react";
4715
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4479
4716
  function WorkspaceSettingsButton({
4480
4717
  onSettings,
4481
4718
  onRename,
4482
4719
  onDownload,
4483
4720
  onRemove
4484
4721
  }) {
4485
- const [isOpen, setIsOpen] = useState8(false);
4486
- const containerRef = useRef9(null);
4722
+ const [isOpen, setIsOpen] = useState9(false);
4723
+ const containerRef = useRef10(null);
4487
4724
  const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } = useMenuKeyboard(isOpen, setIsOpen);
4488
- useEffect9(() => {
4725
+ useEffect10(() => {
4489
4726
  if (!isOpen) return;
4490
4727
  function handleOutsideClick(e) {
4491
4728
  if (containerRef.current && !containerRef.current.contains(e.target)) {
@@ -4495,15 +4732,15 @@ function WorkspaceSettingsButton({
4495
4732
  document.addEventListener("mousedown", handleOutsideClick);
4496
4733
  return () => document.removeEventListener("mousedown", handleOutsideClick);
4497
4734
  }, [closeMenu, isOpen]);
4498
- const handleAction = useCallback9(
4735
+ const handleAction = useCallback10(
4499
4736
  (action) => {
4500
4737
  closeMenu(true);
4501
4738
  action();
4502
4739
  },
4503
4740
  [closeMenu]
4504
4741
  );
4505
- return /* @__PURE__ */ jsxs8("div", { ref: containerRef, className: "db-ws-settings", children: [
4506
- /* @__PURE__ */ jsx9(
4742
+ return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "db-ws-settings", children: [
4743
+ /* @__PURE__ */ jsx10(
4507
4744
  "button",
4508
4745
  {
4509
4746
  ref: triggerRef,
@@ -4514,10 +4751,10 @@ function WorkspaceSettingsButton({
4514
4751
  "aria-haspopup": "menu",
4515
4752
  "aria-label": "Workspace settings",
4516
4753
  title: "Workspace settings",
4517
- children: /* @__PURE__ */ jsx9(WorkspaceIcon, {})
4754
+ children: /* @__PURE__ */ jsx10(WorkspaceIcon, {})
4518
4755
  }
4519
4756
  ),
4520
- isOpen && /* @__PURE__ */ jsxs8(
4757
+ isOpen && /* @__PURE__ */ jsxs9(
4521
4758
  "div",
4522
4759
  {
4523
4760
  ref: menuRef,
@@ -4525,7 +4762,7 @@ function WorkspaceSettingsButton({
4525
4762
  role: "menu",
4526
4763
  onKeyDown: handleMenuKeyDown,
4527
4764
  children: [
4528
- /* @__PURE__ */ jsx9(
4765
+ /* @__PURE__ */ jsx10(
4529
4766
  "button",
4530
4767
  {
4531
4768
  className: "db-ws-settings-item",
@@ -4535,7 +4772,7 @@ function WorkspaceSettingsButton({
4535
4772
  children: "Workspace settings\u2026"
4536
4773
  }
4537
4774
  ),
4538
- /* @__PURE__ */ jsx9(
4775
+ /* @__PURE__ */ jsx10(
4539
4776
  "button",
4540
4777
  {
4541
4778
  className: "db-ws-settings-item",
@@ -4545,7 +4782,7 @@ function WorkspaceSettingsButton({
4545
4782
  children: "Rename workspace"
4546
4783
  }
4547
4784
  ),
4548
- /* @__PURE__ */ jsx9(
4785
+ /* @__PURE__ */ jsx10(
4549
4786
  "button",
4550
4787
  {
4551
4788
  className: "db-ws-settings-item",
@@ -4555,8 +4792,8 @@ function WorkspaceSettingsButton({
4555
4792
  children: "Back up this workspace"
4556
4793
  }
4557
4794
  ),
4558
- /* @__PURE__ */ jsx9("div", { className: "db-ws-settings-divider" }),
4559
- /* @__PURE__ */ jsx9(
4795
+ /* @__PURE__ */ jsx10("div", { className: "db-ws-settings-divider" }),
4796
+ /* @__PURE__ */ jsx10(
4560
4797
  "button",
4561
4798
  {
4562
4799
  className: "db-ws-settings-item db-ws-settings-item--danger",
@@ -4573,7 +4810,7 @@ function WorkspaceSettingsButton({
4573
4810
  }
4574
4811
 
4575
4812
  // src/WorkspacePicker/WorkspaceSettingsDialog.tsx
4576
- import { useEffect as useEffect10, useMemo as useMemo2, useState as useState9 } from "react";
4813
+ import { useEffect as useEffect11, useMemo as useMemo2, useState as useState10 } from "react";
4577
4814
  import { getThemeSummaries } from "@bendyline/squisq/schemas";
4578
4815
  import {
4579
4816
  diffWorkspaceSettings,
@@ -4627,7 +4864,7 @@ function resolveVersioningEnabled(workspace, globalPref, workspaceSettings) {
4627
4864
  }
4628
4865
 
4629
4866
  // src/WorkspacePicker/WorkspaceSettingsDialog.tsx
4630
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
4867
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
4631
4868
  var VERSIONING_LABELS = {
4632
4869
  on: "on",
4633
4870
  "browser-only": "on for browser workspaces, off for local folders",
@@ -4676,15 +4913,15 @@ function WorkspaceSettingsDialog({
4676
4913
  }) {
4677
4914
  const writable = isWorkspaceSettingsWritable(settings);
4678
4915
  const base = settings.settings;
4679
- const [draft, setDraft] = useState9(
4916
+ const [draft, setDraft] = useState10(
4680
4917
  () => initialDraft(workspace, settings.settings)
4681
4918
  );
4682
- const [touched, setTouched] = useState9(false);
4683
- useEffect10(() => {
4919
+ const [touched, setTouched] = useState10(false);
4920
+ useEffect11(() => {
4684
4921
  if (!touched) setDraft(initialDraft(workspace, settings.settings));
4685
4922
  }, [settings.settings, touched, workspace]);
4686
- const [saving, setSaving] = useState9(false);
4687
- const [saveError, setSaveError] = useState9(null);
4923
+ const [saving, setSaving] = useState10(false);
4924
+ const [saveError, setSaveError] = useState10(null);
4688
4925
  const themes = useMemo2(() => getThemeSummaries(), []);
4689
4926
  const patch = useMemo2(() => diffWorkspaceSettings(base, draft), [base, draft]);
4690
4927
  const validation = useMemo2(() => tryApplyWorkspaceSettingsPatch(base, patch), [base, patch]);
@@ -4709,16 +4946,16 @@ function WorkspaceSettingsDialog({
4709
4946
  }
4710
4947
  };
4711
4948
  const notice = settings.status === "loading" ? "Loading workspace settings\u2026" : writable ? null : settings.message ?? "These workspace settings cannot be changed here.";
4712
- return /* @__PURE__ */ jsxs9(
4949
+ return /* @__PURE__ */ jsxs10(
4713
4950
  Dialog,
4714
4951
  {
4715
4952
  title: "Workspace settings",
4716
4953
  size: "wide",
4717
4954
  onClose,
4718
4955
  closeOnBackdrop: !changed,
4719
- footer: writable ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
4720
- /* @__PURE__ */ jsx10("button", { type: "button", className: "db-git-secondary-btn", onClick: onClose, children: "Cancel" }),
4721
- /* @__PURE__ */ jsx10(
4956
+ footer: writable ? /* @__PURE__ */ jsxs10(Fragment6, { children: [
4957
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "db-git-secondary-btn", onClick: onClose, children: "Cancel" }),
4958
+ /* @__PURE__ */ jsx11(
4722
4959
  "button",
4723
4960
  {
4724
4961
  type: "button",
@@ -4728,19 +4965,19 @@ function WorkspaceSettingsDialog({
4728
4965
  children: saving ? "Saving\u2026" : "Save"
4729
4966
  }
4730
4967
  )
4731
- ] }) : /* @__PURE__ */ jsx10("button", { type: "button", className: "db-git-secondary-btn", onClick: onClose, children: "Close" }),
4968
+ ] }) : /* @__PURE__ */ jsx11("button", { type: "button", className: "db-git-secondary-btn", onClick: onClose, children: "Close" }),
4732
4969
  children: [
4733
- /* @__PURE__ */ jsxs9("p", { className: "db-settings-hint", children: [
4734
- /* @__PURE__ */ jsx10("strong", { children: workspace.name }),
4970
+ /* @__PURE__ */ jsxs10("p", { className: "db-settings-hint", children: [
4971
+ /* @__PURE__ */ jsx11("strong", { children: workspace.name }),
4735
4972
  " \xB7",
4736
4973
  " ",
4737
4974
  isLocal ? "Local folder workspace" : "Browser workspace",
4738
4975
  " \xB7 saved in",
4739
4976
  " ",
4740
- /* @__PURE__ */ jsx10("code", { children: ".docblocks/workspace.json" })
4977
+ /* @__PURE__ */ jsx11("code", { children: ".docblocks/workspace.json" })
4741
4978
  ] }),
4742
- notice && /* @__PURE__ */ jsx10("p", { className: "db-settings-status db-settings-status--warning", role: "status", children: notice }),
4743
- /* @__PURE__ */ jsx10(
4979
+ notice && /* @__PURE__ */ jsx11("p", { className: "db-settings-status db-settings-status--warning", role: "status", children: notice }),
4980
+ /* @__PURE__ */ jsx11(
4744
4981
  WorkspaceSettingsControls,
4745
4982
  {
4746
4983
  value: draft,
@@ -4757,14 +4994,14 @@ function WorkspaceSettingsDialog({
4757
4994
  regenerating: outputsStatus.state === "running"
4758
4995
  }
4759
4996
  ),
4760
- (saveError || !validation.ok && changed) && /* @__PURE__ */ jsx10("p", { className: "db-settings-status db-settings-status--warning", role: "alert", children: saveError ?? (!validation.ok ? validation.message : null) })
4997
+ (saveError || !validation.ok && changed) && /* @__PURE__ */ jsx11("p", { className: "db-settings-status db-settings-status--warning", role: "alert", children: saveError ?? (!validation.ok ? validation.message : null) })
4761
4998
  ]
4762
4999
  }
4763
5000
  );
4764
5001
  }
4765
5002
 
4766
5003
  // src/WorkspaceSettings/useWorkspaceSettings.ts
4767
- import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef10, useState as useState10 } from "react";
5004
+ import { useCallback as useCallback11, useEffect as useEffect12, useRef as useRef11, useState as useState11 } from "react";
4768
5005
  import { maybeGetDocBlocksHost } from "@bendyline/docblocks/host";
4769
5006
  import { getFileSystemProviderV2 as getFileSystemProviderV24 } from "@bendyline/docblocks/filesystem";
4770
5007
  import {
@@ -4779,15 +5016,15 @@ function sameState(left, right) {
4779
5016
  }
4780
5017
  function useWorkspaceSettings(provider, enabled) {
4781
5018
  const providerV2 = provider && enabled ? getFileSystemProviderV24(provider) : null;
4782
- const [state, setState] = useState10(
5019
+ const [state, setState] = useState11(
4783
5020
  providerV2 ? LOADING_WORKSPACE_SETTINGS : UNAVAILABLE_WORKSPACE_SETTINGS
4784
5021
  );
4785
- const stateRef = useRef10(state);
5022
+ const stateRef = useRef11(state);
4786
5023
  stateRef.current = state;
4787
- const generationRef = useRef10(0);
4788
- const [refreshKey, setRefreshKey] = useState10(0);
4789
- const refresh = useCallback10(() => setRefreshKey((key) => key + 1), []);
4790
- const providerRef = useRef10(providerV2);
5024
+ const generationRef = useRef11(0);
5025
+ const [refreshKey, setRefreshKey] = useState11(0);
5026
+ const refresh = useCallback11(() => setRefreshKey((key) => key + 1), []);
5027
+ const providerRef = useRef11(providerV2);
4791
5028
  if (providerRef.current !== providerV2) {
4792
5029
  providerRef.current = providerV2;
4793
5030
  generationRef.current += 1;
@@ -4795,7 +5032,7 @@ function useWorkspaceSettings(provider, enabled) {
4795
5032
  stateRef.current = reset;
4796
5033
  setState(reset);
4797
5034
  }
4798
- useEffect11(() => {
5035
+ useEffect12(() => {
4799
5036
  if (!providerV2) return;
4800
5037
  const generation = generationRef.current;
4801
5038
  let cancelled = false;
@@ -4810,7 +5047,7 @@ function useWorkspaceSettings(provider, enabled) {
4810
5047
  cancelled = true;
4811
5048
  };
4812
5049
  }, [providerV2, refreshKey]);
4813
- useEffect11(() => {
5050
+ useEffect12(() => {
4814
5051
  if (!providerV2) return;
4815
5052
  window.addEventListener("focus", refresh);
4816
5053
  const stopResume = maybeGetDocBlocksHost()?.lifecycle?.onResume?.(refresh);
@@ -4829,7 +5066,7 @@ function useWorkspaceSettings(provider, enabled) {
4829
5066
  void subscription?.dispose();
4830
5067
  };
4831
5068
  }, [providerV2, refresh]);
4832
- const save = useCallback10(
5069
+ const save = useCallback11(
4833
5070
  async (patch) => {
4834
5071
  if (!providerV2) throw new Error(UNAVAILABLE_WORKSPACE_SETTINGS.message ?? void 0);
4835
5072
  const generation = generationRef.current;
@@ -4846,7 +5083,7 @@ function useWorkspaceSettings(provider, enabled) {
4846
5083
  }
4847
5084
 
4848
5085
  // src/WorkspaceSettings/useWorkspaceOutputs.ts
4849
- import { useEffect as useEffect12, useState as useState11, useSyncExternalStore as useSyncExternalStore2 } from "react";
5086
+ import { useEffect as useEffect13, useState as useState12, useSyncExternalStore as useSyncExternalStore2 } from "react";
4850
5087
  import { getFileSystemProviderV2 as getFileSystemProviderV25 } from "@bendyline/docblocks/filesystem";
4851
5088
  import {
4852
5089
  createWorkspaceOutputsScheduler,
@@ -4870,8 +5107,8 @@ var noSubscription = () => () => void 0;
4870
5107
  var idleStatus = () => IDLE_WORKSPACE_OUTPUTS_STATUS;
4871
5108
  function useWorkspaceOutputs(provider, workspaceId, settings, enabled) {
4872
5109
  const providerV2 = provider && enabled ? getFileSystemProviderV25(provider) : null;
4873
- const [scheduler, setScheduler] = useState11(null);
4874
- useEffect12(() => {
5110
+ const [scheduler, setScheduler] = useState12(null);
5111
+ useEffect13(() => {
4875
5112
  if (!providerV2 || !workspaceId) {
4876
5113
  setScheduler(null);
4877
5114
  return;
@@ -4892,7 +5129,7 @@ function useWorkspaceOutputs(provider, workspaceId, settings, enabled) {
4892
5129
  next.dispose();
4893
5130
  };
4894
5131
  }, [providerV2, workspaceId]);
4895
- useEffect12(() => {
5132
+ useEffect13(() => {
4896
5133
  scheduler?.updateSettings(settings);
4897
5134
  }, [scheduler, settings]);
4898
5135
  const status = useSyncExternalStore2(
@@ -4927,22 +5164,22 @@ async function assertNotGeneratedCatalog(provider, path) {
4927
5164
  }
4928
5165
 
4929
5166
  // src/WorkspaceSettings/GeneratedFileView.tsx
4930
- import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
5167
+ import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4931
5168
  function GeneratedFileNotice({ kind, onOpenSettings, onBack }) {
4932
- return /* @__PURE__ */ jsxs10("div", { className: "db-generated-file-notice", role: "note", children: [
4933
- onBack && /* @__PURE__ */ jsx11("button", { type: "button", className: "db-generated-file-back", onClick: onBack, children: "Back to files" }),
4934
- /* @__PURE__ */ jsxs10("span", { className: "db-generated-file-notice-text", children: [
4935
- /* @__PURE__ */ jsx11("strong", { children: "Generated by DocBlocks \xB7 read-only." }),
5169
+ return /* @__PURE__ */ jsxs11("div", { className: "db-generated-file-notice", role: "note", children: [
5170
+ onBack && /* @__PURE__ */ jsx12("button", { type: "button", className: "db-generated-file-back", onClick: onBack, children: "Back to files" }),
5171
+ /* @__PURE__ */ jsxs11("span", { className: "db-generated-file-notice-text", children: [
5172
+ /* @__PURE__ */ jsx12("strong", { children: "Generated by DocBlocks \xB7 read-only." }),
4936
5173
  " ",
4937
5174
  kind === "catalog-html" ? "This catalog page is rebuilt from the documents in this folder as you save." : "This catalog data is rebuilt from the documents in this folder as you save."
4938
5175
  ] }),
4939
- onOpenSettings && /* @__PURE__ */ jsx11("button", { type: "button", className: "db-git-secondary-btn", onClick: onOpenSettings, children: "Catalog settings\u2026" })
5176
+ onOpenSettings && /* @__PURE__ */ jsx12("button", { type: "button", className: "db-git-secondary-btn", onClick: onOpenSettings, children: "Catalog settings\u2026" })
4940
5177
  ] });
4941
5178
  }
4942
5179
  function GeneratedCatalogPage({ html, fileName, ...notice }) {
4943
- return /* @__PURE__ */ jsxs10("div", { className: "db-generated-file", children: [
4944
- /* @__PURE__ */ jsx11(GeneratedFileNotice, { kind: "catalog-html", ...notice }),
4945
- /* @__PURE__ */ jsx11(
5180
+ return /* @__PURE__ */ jsxs11("div", { className: "db-generated-file", children: [
5181
+ /* @__PURE__ */ jsx12(GeneratedFileNotice, { kind: "catalog-html", ...notice }),
5182
+ /* @__PURE__ */ jsx12(
4946
5183
  "iframe",
4947
5184
  {
4948
5185
  className: "db-generated-file-preview",
@@ -4955,10 +5192,10 @@ function GeneratedCatalogPage({ html, fileName, ...notice }) {
4955
5192
  ] });
4956
5193
  }
4957
5194
  function GeneratedFileFrame({ kind, children, ...notice }) {
4958
- if (!kind) return /* @__PURE__ */ jsx11(Fragment7, { children });
4959
- return /* @__PURE__ */ jsxs10("div", { className: "db-generated-file", children: [
4960
- /* @__PURE__ */ jsx11(GeneratedFileNotice, { kind, ...notice }),
4961
- /* @__PURE__ */ jsx11("div", { className: "db-generated-file-body", children })
5195
+ if (!kind) return /* @__PURE__ */ jsx12(Fragment7, { children });
5196
+ return /* @__PURE__ */ jsxs11("div", { className: "db-generated-file", children: [
5197
+ /* @__PURE__ */ jsx12(GeneratedFileNotice, { kind, ...notice }),
5198
+ /* @__PURE__ */ jsx12("div", { className: "db-generated-file-body", children })
4962
5199
  ] });
4963
5200
  }
4964
5201
 
@@ -4970,14 +5207,14 @@ import {
4970
5207
  } from "@bendyline/docblocks/workspace-settings";
4971
5208
 
4972
5209
  // src/hooks/useDocumentSession.ts
4973
- import { useState as useState12, useSyncExternalStore as useSyncExternalStore3 } from "react";
5210
+ import { useState as useState13, useSyncExternalStore as useSyncExternalStore3 } from "react";
4974
5211
  import {
4975
5212
  DocumentRecoveryJournal,
4976
5213
  DocumentSession,
4977
5214
  getDefaultDocumentRecoveryStorage
4978
5215
  } from "@bendyline/docblocks/document";
4979
5216
  function useDocumentSession(autoSaveDelayMs = 500) {
4980
- const [session] = useState12(
5217
+ const [session] = useState13(
4981
5218
  () => new DocumentSession({
4982
5219
  autoSaveDelayMs,
4983
5220
  recoveryJournal: new DocumentRecoveryJournal(getDefaultDocumentRecoveryStorage())
@@ -4993,14 +5230,14 @@ function useDocumentSession(autoSaveDelayMs = 500) {
4993
5230
 
4994
5231
  // src/Export/DeferredExportToolbarControls.tsx
4995
5232
  import { lazy, Suspense } from "react";
4996
- import { jsx as jsx12 } from "react/jsx-runtime";
5233
+ import { jsx as jsx13 } from "react/jsx-runtime";
4997
5234
  var ExportToolbarControlsImplementation = lazy(
4998
- () => import("./ExportToolbarControls-A2YBKUUD.js").then((module) => ({
5235
+ () => import("./ExportToolbarControls-HOV424TO.js").then((module) => ({
4999
5236
  default: module.ExportToolbarControls
5000
5237
  }))
5001
5238
  );
5002
5239
  function ExportToolbarControls(props) {
5003
- return /* @__PURE__ */ jsx12(Suspense, { fallback: null, children: /* @__PURE__ */ jsx12(ExportToolbarControlsImplementation, { ...props }) });
5240
+ return /* @__PURE__ */ jsx13(Suspense, { fallback: null, children: /* @__PURE__ */ jsx13(ExportToolbarControlsImplementation, { ...props }) });
5004
5241
  }
5005
5242
 
5006
5243
  // src/Export/host-export-save.ts
@@ -5040,27 +5277,27 @@ async function saveBlobToHost(exports, documentId, blob, filename, grantId) {
5040
5277
  }
5041
5278
 
5042
5279
  // src/Git/useGit.ts
5043
- import { useCallback as useCallback12, useEffect as useEffect14, useMemo as useMemo3, useRef as useRef12, useState as useState14 } from "react";
5280
+ import { useCallback as useCallback13, useEffect as useEffect15, useMemo as useMemo3, useRef as useRef13, useState as useState15 } from "react";
5044
5281
  import { maybeGetDocBlocksHost as maybeGetDocBlocksHost2 } from "@bendyline/docblocks/host";
5045
5282
 
5046
5283
  // src/Git/useGitStatus.ts
5047
- import { useCallback as useCallback11, useEffect as useEffect13, useRef as useRef11, useState as useState13 } from "react";
5284
+ import { useCallback as useCallback12, useEffect as useEffect14, useRef as useRef12, useState as useState14 } from "react";
5048
5285
  var SCHEDULE_REFRESH_MS = 1500;
5049
5286
  function statusKey(status) {
5050
5287
  return JSON.stringify(status);
5051
5288
  }
5052
5289
  function useGitStatus(gitApi, repositoryId, enabled) {
5053
- const [status, setStatus] = useState13(null);
5054
- const lastKeyRef = useRef11(null);
5055
- const timerRef = useRef11(null);
5056
- const generationRef = useRef11(0);
5057
- const apply = useCallback11((next) => {
5290
+ const [status, setStatus] = useState14(null);
5291
+ const lastKeyRef = useRef12(null);
5292
+ const timerRef = useRef12(null);
5293
+ const generationRef = useRef12(0);
5294
+ const apply = useCallback12((next) => {
5058
5295
  const key = statusKey(next);
5059
5296
  if (key === lastKeyRef.current) return;
5060
5297
  lastKeyRef.current = key;
5061
5298
  setStatus(next);
5062
5299
  }, []);
5063
- useEffect13(() => {
5300
+ useEffect14(() => {
5064
5301
  generationRef.current += 1;
5065
5302
  if (timerRef.current) {
5066
5303
  clearTimeout(timerRef.current);
@@ -5078,7 +5315,7 @@ function useGitStatus(gitApi, repositoryId, enabled) {
5078
5315
  unsubscribe();
5079
5316
  };
5080
5317
  }, [gitApi, repositoryId, enabled, apply]);
5081
- const refresh = useCallback11(() => {
5318
+ const refresh = useCallback12(() => {
5082
5319
  if (!gitApi || !repositoryId || !enabled) return;
5083
5320
  const generation = generationRef.current;
5084
5321
  void gitApi.status(repositoryId).then(
@@ -5092,14 +5329,14 @@ function useGitStatus(gitApi, repositoryId, enabled) {
5092
5329
  () => void 0
5093
5330
  );
5094
5331
  }, [gitApi, repositoryId, enabled, apply]);
5095
- const scheduleRefresh = useCallback11(() => {
5332
+ const scheduleRefresh = useCallback12(() => {
5096
5333
  if (timerRef.current) clearTimeout(timerRef.current);
5097
5334
  timerRef.current = setTimeout(() => {
5098
5335
  timerRef.current = null;
5099
5336
  refresh();
5100
5337
  }, SCHEDULE_REFRESH_MS);
5101
5338
  }, [refresh]);
5102
- useEffect13(() => {
5339
+ useEffect14(() => {
5103
5340
  return () => {
5104
5341
  if (timerRef.current) clearTimeout(timerRef.current);
5105
5342
  };
@@ -5125,8 +5362,8 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5125
5362
  const host = maybeGetDocBlocksHost2();
5126
5363
  const gitApi = host?.git ?? null;
5127
5364
  const workspaceId = gitApi ? requestedWorkspaceId : null;
5128
- const [capabilities, setCapabilities] = useState14(null);
5129
- useEffect14(() => {
5365
+ const [capabilities, setCapabilities] = useState15(null);
5366
+ useEffect15(() => {
5130
5367
  if (!gitApi) return;
5131
5368
  let cancelled = false;
5132
5369
  let timer = null;
@@ -5152,10 +5389,10 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5152
5389
  };
5153
5390
  }, [gitApi]);
5154
5391
  const available = gitApi !== null && capabilities?.gitAvailable === true;
5155
- const [repo, setRepo] = useState14(null);
5156
- const [remoteWeb, setRemoteWeb] = useState14(null);
5157
- const [pendingGrant, setPendingGrant] = useState14(null);
5158
- const loadRemote = useCallback12(
5392
+ const [repo, setRepo] = useState15(null);
5393
+ const [remoteWeb, setRemoteWeb] = useState15(null);
5394
+ const [pendingGrant, setPendingGrant] = useState15(null);
5395
+ const loadRemote = useCallback13(
5159
5396
  (repositoryId2, isCancelled) => {
5160
5397
  if (!gitApi) return;
5161
5398
  void gitApi.listRemotes(repositoryId2).then(
@@ -5169,7 +5406,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5169
5406
  },
5170
5407
  [gitApi]
5171
5408
  );
5172
- useEffect14(() => {
5409
+ useEffect15(() => {
5173
5410
  setRepo(null);
5174
5411
  setRemoteWeb(null);
5175
5412
  setPendingGrant(null);
@@ -5195,8 +5432,8 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5195
5432
  cancelled = true;
5196
5433
  };
5197
5434
  }, [gitApi, workspaceId, available, loadRemote]);
5198
- const [grantBusy, setGrantBusy] = useState14(false);
5199
- const enableExpandedRepo = useCallback12(
5435
+ const [grantBusy, setGrantBusy] = useState15(false);
5436
+ const enableExpandedRepo = useCallback13(
5200
5437
  async (opts) => {
5201
5438
  if (!gitApi || !workspaceId) return false;
5202
5439
  setGrantBusy(true);
@@ -5217,15 +5454,15 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5217
5454
  const isRepo = repositoryId !== null;
5218
5455
  const { status, refresh, scheduleRefresh } = useGitStatus(gitApi, repositoryId, isRepo);
5219
5456
  const badges = useMemo3(() => buildBadgeMap(status?.changes ?? []), [status]);
5220
- const [busy, setBusy] = useState14(null);
5221
- const [lastResult, setLastResult] = useState14(null);
5222
- const [dialog, setDialog] = useState14({ kind: "none" });
5223
- const openDialog = useCallback12((next) => {
5457
+ const [busy, setBusy] = useState15(null);
5458
+ const [lastResult, setLastResult] = useState15(null);
5459
+ const [dialog, setDialog] = useState15({ kind: "none" });
5460
+ const openDialog = useCallback13((next) => {
5224
5461
  setLastResult(null);
5225
5462
  setDialog(next);
5226
5463
  }, []);
5227
- const closeDialog = useCallback12(() => setDialog({ kind: "none" }), []);
5228
- const routeError = useCallback12(
5464
+ const closeDialog = useCallback13(() => setDialog({ kind: "none" }), []);
5465
+ const routeError = useCallback13(
5229
5466
  (operation, error) => {
5230
5467
  if (error.code === "auth-failed") {
5231
5468
  setDialog({
@@ -5239,15 +5476,15 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5239
5476
  },
5240
5477
  []
5241
5478
  );
5242
- const statusRef = useRef12(status);
5243
- useEffect14(() => {
5479
+ const statusRef = useRef13(status);
5480
+ useEffect15(() => {
5244
5481
  statusRef.current = status;
5245
5482
  }, [status]);
5246
- const workingTreeChangedRef = useRef12(onWorkingTreeChanged);
5247
- useEffect14(() => {
5483
+ const workingTreeChangedRef = useRef13(onWorkingTreeChanged);
5484
+ useEffect15(() => {
5248
5485
  workingTreeChangedRef.current = onWorkingTreeChanged;
5249
5486
  }, [onWorkingTreeChanged]);
5250
- const runAction = useCallback12(
5487
+ const runAction = useCallback13(
5251
5488
  async (kind, operation, action, successMessage) => {
5252
5489
  if (!gitApi || !repositoryId) return false;
5253
5490
  setBusy(kind);
@@ -5277,7 +5514,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5277
5514
  },
5278
5515
  [gitApi, repositoryId, refresh, routeError]
5279
5516
  );
5280
- const commit = useCallback12(
5517
+ const commit = useCallback13(
5281
5518
  (message, paths) => runAction(
5282
5519
  "commit",
5283
5520
  "commit",
@@ -5286,7 +5523,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5286
5523
  ),
5287
5524
  [runAction]
5288
5525
  );
5289
- const push = useCallback12(async () => {
5526
+ const push = useCallback13(async () => {
5290
5527
  const setUpstream = statusRef.current?.upstream === null;
5291
5528
  await runAction(
5292
5529
  "push",
@@ -5295,13 +5532,13 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5295
5532
  setUpstream ? "Branch published" : "Pushed"
5296
5533
  );
5297
5534
  }, [runAction]);
5298
- const pull = useCallback12(async () => {
5535
+ const pull = useCallback13(async () => {
5299
5536
  await runAction("pull", "pull", (api, repository) => api.pull(repository), "Pulled");
5300
5537
  }, [runAction]);
5301
- const fetchRemote = useCallback12(async () => {
5538
+ const fetchRemote = useCallback13(async () => {
5302
5539
  await runAction("fetch", "fetch", (api, repository) => api.fetch(repository), "Fetched");
5303
5540
  }, [runAction]);
5304
- const createBranch = useCallback12(
5541
+ const createBranch = useCallback13(
5305
5542
  (name) => runAction(
5306
5543
  "branch",
5307
5544
  "branch",
@@ -5310,7 +5547,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5310
5547
  ),
5311
5548
  [runAction]
5312
5549
  );
5313
- const switchBranch = useCallback12(
5550
+ const switchBranch = useCallback13(
5314
5551
  (name) => runAction(
5315
5552
  "branch",
5316
5553
  "branch",
@@ -5319,7 +5556,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5319
5556
  ),
5320
5557
  [runAction]
5321
5558
  );
5322
- const openOnRemote = useCallback12(
5559
+ const openOnRemote = useCallback13(
5323
5560
  (filePath) => {
5324
5561
  if (!remoteWeb || !host) return;
5325
5562
  let url = remoteWeb.webUrl;
@@ -5331,7 +5568,7 @@ function useGit(provider, requestedWorkspaceId, theme, onWorkingTreeChanged) {
5331
5568
  },
5332
5569
  [remoteWeb, host]
5333
5570
  );
5334
- const createPullRequest = useCallback12(async () => {
5571
+ const createPullRequest = useCallback13(async () => {
5335
5572
  if (!gitApi || !repositoryId) return;
5336
5573
  setBusy("pr");
5337
5574
  setLastResult(null);
@@ -5455,7 +5692,7 @@ function retainFileSystemProvider(provider) {
5455
5692
  }
5456
5693
 
5457
5694
  // src/DocBlocksShell/document-title.ts
5458
- import { useEffect as useEffect15 } from "react";
5695
+ import { useEffect as useEffect16 } from "react";
5459
5696
  var APP_TITLE = "DocBlocks";
5460
5697
  var INSTALLED_DISPLAY_QUERIES = [
5461
5698
  "(display-mode: window-controls-overlay)",
@@ -5479,7 +5716,7 @@ function titleForSelectedFile(selectedFile, homeDocumentTitle = APP_TITLE, homeD
5479
5716
  return includeAppName ? `${name} - ${APP_TITLE}` : name;
5480
5717
  }
5481
5718
  function useDocumentTitle(selectedFile, homeDocumentTitle, homeDocumentPath) {
5482
- useEffect15(() => {
5719
+ useEffect16(() => {
5483
5720
  if (typeof document === "undefined") return;
5484
5721
  const displayQueries = typeof globalThis.matchMedia === "function" ? INSTALLED_DISPLAY_QUERIES.map((query) => globalThis.matchMedia(query)) : [];
5485
5722
  const updateTitle = () => {
@@ -5565,7 +5802,7 @@ function resolveShellEditorLinkTarget(href, documentPath) {
5565
5802
  targetSegments.push(segment);
5566
5803
  }
5567
5804
  const path = targetSegments.join("/");
5568
- if (!path || path.length > HOST_WIRE_LIMITS2.pathCharacters || !isMarkdownFileName(path)) {
5805
+ if (!path || path.length > HOST_WIRE_LIMITS2.pathCharacters || !isMarkdownFileName(path) && !/\.html?$/i.test(path)) {
5569
5806
  return null;
5570
5807
  }
5571
5808
  return { kind: "workspace", path };
@@ -5588,10 +5825,36 @@ function hasControlCharacter(value) {
5588
5825
  return false;
5589
5826
  }
5590
5827
 
5591
- // src/DocBlocksShell/import-files.ts
5828
+ // src/DocBlocksShell/wiki-articles.ts
5592
5829
  import {
5830
+ ensureWikiArticle,
5831
+ wikiArticleExists,
5593
5832
  FsError as FsError4,
5594
- getFileSystemProviderV2 as getFileSystemProviderV27,
5833
+ getFileSystemProviderV2 as getFileSystemProviderV27
5834
+ } from "@bendyline/docblocks/filesystem";
5835
+ function wikiArticleExtension(format) {
5836
+ return newFileHtmlOutput(format) ? ".html" : ".md";
5837
+ }
5838
+ async function ensureShellWikiArticle(provider, path, format, render) {
5839
+ if (!/\.html?$/i.test(path)) return ensureWikiArticle(provider, path);
5840
+ if (await wikiArticleExists(provider, path)) return false;
5841
+ if (!getFileSystemProviderV27(provider)) {
5842
+ throw new FsError4("not-supported", "This workspace cannot create articles safely.");
5843
+ }
5844
+ try {
5845
+ await createNewOutsideInDocument(provider, path, newFileHtmlOutput(format), render);
5846
+ return true;
5847
+ } catch (error) {
5848
+ if (error instanceof FsError4 && (error.code === "already-exists" || error.code === "conflict") && await wikiArticleExists(provider, path))
5849
+ return false;
5850
+ throw error;
5851
+ }
5852
+ }
5853
+
5854
+ // src/DocBlocksShell/import-files.ts
5855
+ import {
5856
+ FsError as FsError5,
5857
+ getFileSystemProviderV2 as getFileSystemProviderV28,
5595
5858
  parseWorkspacePath as parseWorkspacePath6
5596
5859
  } from "@bendyline/docblocks/filesystem";
5597
5860
  var MAX_NAME_ATTEMPTS = 100;
@@ -5602,7 +5865,7 @@ function withCopySuffix(path, n) {
5602
5865
  return `${path.slice(0, dot)} (${n})${path.slice(dot)}`;
5603
5866
  }
5604
5867
  function isAlreadyExists(error) {
5605
- return error instanceof FsError4 && error.code === "already-exists";
5868
+ return error instanceof FsError5 && error.code === "already-exists";
5606
5869
  }
5607
5870
  async function claimAvailablePath(provider, desiredPath) {
5608
5871
  for (let attempt = 1; attempt <= MAX_NAME_ATTEMPTS; attempt++) {
@@ -5617,7 +5880,7 @@ async function claimAvailablePath(provider, desiredPath) {
5617
5880
  throw new Error(`Too many documents are already named like \u201C${desiredPath}\u201D.`);
5618
5881
  }
5619
5882
  async function writeProviderBytes(provider, path, data, mode) {
5620
- const providerV2 = getFileSystemProviderV27(provider);
5883
+ const providerV2 = getFileSystemProviderV28(provider);
5621
5884
  if (providerV2) {
5622
5885
  await providerV2.writeFile(parseWorkspacePath6(path), data, {
5623
5886
  mode,
@@ -5627,7 +5890,7 @@ async function writeProviderBytes(provider, path, data, mode) {
5627
5890
  return;
5628
5891
  }
5629
5892
  if (mode === "create" && await providerEntryExists(provider, path)) {
5630
- throw new FsError4("already-exists", "File already exists.", { operation: "write", path });
5893
+ throw new FsError5("already-exists", "File already exists.", { operation: "write", path });
5631
5894
  }
5632
5895
  await provider.writeBinary(path, data);
5633
5896
  }
@@ -5660,7 +5923,7 @@ function directoriesUnder(files, root) {
5660
5923
  return [...directories].sort((a, b) => b.split("/").length - a.split("/").length);
5661
5924
  }
5662
5925
  async function removeIfEmptyDirectory(provider, path) {
5663
- const providerV2 = getFileSystemProviderV27(provider);
5926
+ const providerV2 = getFileSystemProviderV28(provider);
5664
5927
  if (!providerV2) return;
5665
5928
  await providerV2.remove(parseWorkspacePath6(path), { recursive: false, missing: "ignore" }).catch(() => void 0);
5666
5929
  }
@@ -5777,37 +6040,37 @@ function summariseImport(result) {
5777
6040
  }
5778
6041
 
5779
6042
  // src/components/usePromptDialog.ts
5780
- import React2, { useCallback as useCallback14, useEffect as useEffect17, useRef as useRef14, useState as useState16 } from "react";
6043
+ import React2, { useCallback as useCallback15, useEffect as useEffect18, useRef as useRef15, useState as useState17 } from "react";
5781
6044
 
5782
6045
  // src/components/PromptDialog.tsx
5783
- import { useCallback as useCallback13, useEffect as useEffect16, useId as useId3, useRef as useRef13, useState as useState15 } from "react";
5784
- import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
6046
+ import { useCallback as useCallback14, useEffect as useEffect17, useId as useId3, useRef as useRef14, useState as useState16 } from "react";
6047
+ import { Fragment as Fragment8, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
5785
6048
  function PromptDialog({ request, onSettle }) {
5786
- const [value, setValue] = useState15(request.initialValue ?? "");
5787
- const inputRef = useRef13(null);
6049
+ const [value, setValue] = useState16(request.initialValue ?? "");
6050
+ const inputRef = useRef14(null);
5788
6051
  const inputId = useId3();
5789
- const cancel = useCallback13(() => onSettle(null), [onSettle]);
5790
- const submit = useCallback13(() => {
6052
+ const cancel = useCallback14(() => onSettle(null), [onSettle]);
6053
+ const submit = useCallback14(() => {
5791
6054
  if (value.trim() === "") return;
5792
6055
  onSettle(value);
5793
6056
  }, [onSettle, value]);
5794
6057
  const initialSelection = request.initialSelection;
5795
- useEffect16(() => {
6058
+ useEffect17(() => {
5796
6059
  const input = inputRef.current;
5797
6060
  if (!input) return;
5798
6061
  if (initialSelection) input.setSelectionRange(initialSelection[0], initialSelection[1]);
5799
6062
  else input.select();
5800
6063
  }, []);
5801
- return /* @__PURE__ */ jsx13(
6064
+ return /* @__PURE__ */ jsx14(
5802
6065
  Dialog,
5803
6066
  {
5804
6067
  title: request.title,
5805
6068
  onClose: cancel,
5806
6069
  initialFocusRef: inputRef,
5807
6070
  closeOnBackdrop: false,
5808
- footer: /* @__PURE__ */ jsxs11(Fragment8, { children: [
5809
- /* @__PURE__ */ jsx13("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
5810
- /* @__PURE__ */ jsx13(
6071
+ footer: /* @__PURE__ */ jsxs12(Fragment8, { children: [
6072
+ /* @__PURE__ */ jsx14("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
6073
+ /* @__PURE__ */ jsx14(
5811
6074
  "button",
5812
6075
  {
5813
6076
  type: "button",
@@ -5818,9 +6081,9 @@ function PromptDialog({ request, onSettle }) {
5818
6081
  }
5819
6082
  )
5820
6083
  ] }),
5821
- children: /* @__PURE__ */ jsxs11("div", { className: "db-git-form-row", children: [
5822
- /* @__PURE__ */ jsx13("label", { className: "db-git-form-label", htmlFor: inputId, children: request.label }),
5823
- /* @__PURE__ */ jsx13(
6084
+ children: /* @__PURE__ */ jsxs12("div", { className: "db-git-form-row", children: [
6085
+ /* @__PURE__ */ jsx14("label", { className: "db-git-form-label", htmlFor: inputId, children: request.label }),
6086
+ /* @__PURE__ */ jsx14(
5824
6087
  "input",
5825
6088
  {
5826
6089
  id: inputId,
@@ -5843,17 +6106,17 @@ function PromptDialog({ request, onSettle }) {
5843
6106
 
5844
6107
  // src/components/usePromptDialog.ts
5845
6108
  function usePromptDialog() {
5846
- const [pending, setPending] = useState16(null);
5847
- const pendingRef = useRef14(null);
5848
- const nextIdRef = useRef14(0);
5849
- const settlePending = useCallback14((value) => {
6109
+ const [pending, setPending] = useState17(null);
6110
+ const pendingRef = useRef15(null);
6111
+ const nextIdRef = useRef15(0);
6112
+ const settlePending = useCallback15((value) => {
5850
6113
  const current = pendingRef.current;
5851
6114
  if (!current) return;
5852
6115
  pendingRef.current = null;
5853
6116
  setPending((open) => open?.id === current.id ? null : open);
5854
6117
  current.settle(value);
5855
6118
  }, []);
5856
- const prompt = useCallback14(
6119
+ const prompt = useCallback15(
5857
6120
  (request) => new Promise((resolve) => {
5858
6121
  settlePending(null);
5859
6122
  const next = { id: ++nextIdRef.current, request, settle: resolve };
@@ -5862,7 +6125,7 @@ function usePromptDialog() {
5862
6125
  }),
5863
6126
  [settlePending]
5864
6127
  );
5865
- useEffect17(() => {
6128
+ useEffect18(() => {
5866
6129
  return () => settlePending(null);
5867
6130
  }, [settlePending]);
5868
6131
  const promptDialog = pending ? React2.createElement(PromptDialog, {
@@ -5875,37 +6138,37 @@ function usePromptDialog() {
5875
6138
  }
5876
6139
 
5877
6140
  // src/components/NewDocumentDialog.tsx
5878
- import { useCallback as useCallback15, useEffect as useEffect18, useId as useId4, useRef as useRef15, useState as useState17 } from "react";
5879
- import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
6141
+ import { useCallback as useCallback16, useEffect as useEffect19, useId as useId4, useRef as useRef16, useState as useState18 } from "react";
6142
+ import { Fragment as Fragment9, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
5880
6143
  function NewDocumentDialog({
5881
6144
  onSettle,
5882
6145
  initialFormat = "markdown"
5883
6146
  }) {
5884
- const [name, setName] = useState17("Untitled");
5885
- const [format, setFormat] = useState17(initialFormat);
5886
- const inputRef = useRef15(null);
6147
+ const [name, setName] = useState18("Untitled");
6148
+ const [format, setFormat] = useState18(initialFormat);
6149
+ const inputRef = useRef16(null);
5887
6150
  const nameId = useId4();
5888
6151
  const formatId = useId4();
5889
6152
  const hintId = useId4();
5890
6153
  const hint = newFileFormatHint(format);
5891
6154
  const filename = newFileName(name, format);
5892
- const cancel = useCallback15(() => onSettle(null), [onSettle]);
5893
- const submit = useCallback15(() => {
6155
+ const cancel = useCallback16(() => onSettle(null), [onSettle]);
6156
+ const submit = useCallback16(() => {
5894
6157
  if (filename) onSettle({ filename, format });
5895
6158
  }, [filename, format, onSettle]);
5896
- useEffect18(() => {
6159
+ useEffect19(() => {
5897
6160
  inputRef.current?.select();
5898
6161
  }, []);
5899
- return /* @__PURE__ */ jsx14(
6162
+ return /* @__PURE__ */ jsx15(
5900
6163
  Dialog,
5901
6164
  {
5902
6165
  title: "New document",
5903
6166
  onClose: cancel,
5904
6167
  initialFocusRef: inputRef,
5905
6168
  closeOnBackdrop: false,
5906
- footer: /* @__PURE__ */ jsxs12(Fragment9, { children: [
5907
- /* @__PURE__ */ jsx14("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
5908
- /* @__PURE__ */ jsx14(
6169
+ footer: /* @__PURE__ */ jsxs13(Fragment9, { children: [
6170
+ /* @__PURE__ */ jsx15("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
6171
+ /* @__PURE__ */ jsx15(
5909
6172
  "button",
5910
6173
  {
5911
6174
  type: "button",
@@ -5916,9 +6179,9 @@ function NewDocumentDialog({
5916
6179
  }
5917
6180
  )
5918
6181
  ] }),
5919
- children: /* @__PURE__ */ jsxs12("div", { className: "db-new-document-fields", children: [
5920
- /* @__PURE__ */ jsx14("label", { className: "db-git-form-label", htmlFor: nameId, children: "Document name" }),
5921
- /* @__PURE__ */ jsx14(
6182
+ children: /* @__PURE__ */ jsxs13("div", { className: "db-new-document-fields", children: [
6183
+ /* @__PURE__ */ jsx15("label", { className: "db-git-form-label", htmlFor: nameId, children: "Document name" }),
6184
+ /* @__PURE__ */ jsx15(
5922
6185
  "input",
5923
6186
  {
5924
6187
  id: nameId,
@@ -5934,8 +6197,8 @@ function NewDocumentDialog({
5934
6197
  }
5935
6198
  }
5936
6199
  ),
5937
- /* @__PURE__ */ jsx14("label", { className: "db-git-form-label", htmlFor: formatId, children: "Type" }),
5938
- /* @__PURE__ */ jsx14(
6200
+ /* @__PURE__ */ jsx15("label", { className: "db-git-form-label", htmlFor: formatId, children: "Type" }),
6201
+ /* @__PURE__ */ jsx15(
5939
6202
  "select",
5940
6203
  {
5941
6204
  id: formatId,
@@ -5943,10 +6206,10 @@ function NewDocumentDialog({
5943
6206
  "aria-describedby": hint ? hintId : void 0,
5944
6207
  value: format,
5945
6208
  onChange: (e) => setFormat(e.target.value),
5946
- children: /* @__PURE__ */ jsx14(NewFileFormatOptions, {})
6209
+ children: /* @__PURE__ */ jsx15(NewFileFormatOptions, {})
5947
6210
  }
5948
6211
  ),
5949
- hint && /* @__PURE__ */ jsx14("p", { id: hintId, className: "db-new-document-hint", children: hint })
6212
+ hint && /* @__PURE__ */ jsx15("p", { id: hintId, className: "db-new-document-hint", children: hint })
5950
6213
  ] })
5951
6214
  }
5952
6215
  );
@@ -5954,7 +6217,7 @@ function NewDocumentDialog({
5954
6217
 
5955
6218
  // src/DocBlocksShell/EditorErrorBoundary.tsx
5956
6219
  import { Component } from "react";
5957
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6220
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
5958
6221
  var EditorErrorBoundary = class extends Component {
5959
6222
  constructor() {
5960
6223
  super(...arguments);
@@ -5969,11 +6232,11 @@ var EditorErrorBoundary = class extends Component {
5969
6232
  render() {
5970
6233
  const { error } = this.state;
5971
6234
  if (!error) return this.props.children;
5972
- return /* @__PURE__ */ jsx15("div", { className: "db-shell-empty db-shell-empty--workspace", role: "alert", children: /* @__PURE__ */ jsxs13("div", { className: "db-workspace-empty-content", children: [
5973
- /* @__PURE__ */ jsx15("h1", { children: "This document couldn\u2019t be displayed" }),
5974
- /* @__PURE__ */ jsx15("p", { children: "The editor ran into a problem drawing it. The document itself is unaffected." }),
5975
- /* @__PURE__ */ jsx15("div", { className: "db-workspace-empty-actions", children: /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => this.setState({ error: null }), children: "Try again" }) }),
5976
- /* @__PURE__ */ jsx15("p", { className: "db-workspace-empty-hint", children: error.message })
6235
+ return /* @__PURE__ */ jsx16("div", { className: "db-shell-empty db-shell-empty--workspace", role: "alert", children: /* @__PURE__ */ jsxs14("div", { className: "db-workspace-empty-content", children: [
6236
+ /* @__PURE__ */ jsx16("h1", { children: "This document couldn\u2019t be displayed" }),
6237
+ /* @__PURE__ */ jsx16("p", { children: "The editor ran into a problem drawing it. The document itself is unaffected." }),
6238
+ /* @__PURE__ */ jsx16("div", { className: "db-workspace-empty-actions", children: /* @__PURE__ */ jsx16("button", { type: "button", onClick: () => this.setState({ error: null }), children: "Try again" }) }),
6239
+ /* @__PURE__ */ jsx16("p", { className: "db-workspace-empty-hint", children: error.message })
5977
6240
  ] }) });
5978
6241
  }
5979
6242
  };
@@ -6038,7 +6301,7 @@ function resolveShellTheme({
6038
6301
  }
6039
6302
 
6040
6303
  // src/DocBlocksShell/last-state.ts
6041
- import { tryParseWorkspacePath as tryParseWorkspacePath2 } from "@bendyline/docblocks/filesystem";
6304
+ import { tryParseWorkspacePath as tryParseWorkspacePath3 } from "@bendyline/docblocks/filesystem";
6042
6305
  var LAST_STATE_KEY = "docblocks:lastState";
6043
6306
  var MAX_WORKSPACE_ID_CHARACTERS = 256;
6044
6307
  var MAX_WORKSPACE_PATH_CHARACTERS = 4096;
@@ -6059,7 +6322,7 @@ function parseLastState(value) {
6059
6322
  if (!hasExactKeys(record, ["workspaceId", "filePath", "view"])) return null;
6060
6323
  if (!isBoundedText(record.workspaceId, MAX_WORKSPACE_ID_CHARACTERS)) return null;
6061
6324
  if (!isBoundedText(record.filePath, MAX_WORKSPACE_PATH_CHARACTERS)) return null;
6062
- const parsedPath = tryParseWorkspacePath2(record.filePath);
6325
+ const parsedPath = tryParseWorkspacePath3(record.filePath);
6063
6326
  if (!parsedPath) return null;
6064
6327
  if (typeof record.view !== "string" || !EDITOR_VIEWS.has(record.view)) return null;
6065
6328
  return {
@@ -6262,10 +6525,10 @@ function saveViewPreferences(preferences) {
6262
6525
  }
6263
6526
 
6264
6527
  // src/layout/useFormFactor.ts
6265
- import { useEffect as useEffect20, useMemo as useMemo4, useState as useState19 } from "react";
6528
+ import { useEffect as useEffect21, useMemo as useMemo4, useState as useState20 } from "react";
6266
6529
 
6267
6530
  // src/layout/useMediaQuery.ts
6268
- import { useEffect as useEffect19, useState as useState18 } from "react";
6531
+ import { useEffect as useEffect20, useState as useState19 } from "react";
6269
6532
  var INSTALLED_DISPLAY_QUERIES2 = [
6270
6533
  "(display-mode: window-controls-overlay)",
6271
6534
  "(display-mode: standalone)"
@@ -6278,8 +6541,8 @@ function isInstalledDisplayMode() {
6278
6541
  return INSTALLED_DISPLAY_QUERIES2.some(matchesMediaQuery);
6279
6542
  }
6280
6543
  function useMediaQuery(query) {
6281
- const [matches, setMatches] = useState18(() => matchesMediaQuery(query));
6282
- useEffect19(() => {
6544
+ const [matches, setMatches] = useState19(() => matchesMediaQuery(query));
6545
+ useEffect20(() => {
6283
6546
  if (typeof globalThis.matchMedia !== "function") return;
6284
6547
  const list = globalThis.matchMedia(query);
6285
6548
  const update = () => setMatches(list.matches);
@@ -6298,11 +6561,11 @@ function initialSize() {
6298
6561
  return { width: window.innerWidth, height: window.innerHeight };
6299
6562
  }
6300
6563
  function useFormFactor(rootRef, override) {
6301
- const [size, setSize] = useState19(initialSize);
6564
+ const [size, setSize] = useState20(initialSize);
6302
6565
  const coarse = useMediaQuery(COARSE_POINTER_QUERY);
6303
6566
  const anyHover = useMediaQuery(ANY_HOVER_QUERY);
6304
- const [installed, setInstalled] = useState19(isInstalledDisplayMode);
6305
- useEffect20(() => {
6567
+ const [installed, setInstalled] = useState20(isInstalledDisplayMode);
6568
+ useEffect21(() => {
6306
6569
  const element = rootRef.current;
6307
6570
  const readWindow = () => setSize(initialSize());
6308
6571
  if (!element || typeof ResizeObserver === "undefined") {
@@ -6326,7 +6589,7 @@ function useFormFactor(rootRef, override) {
6326
6589
  observer.observe(element);
6327
6590
  return () => observer.disconnect();
6328
6591
  }, [rootRef]);
6329
- useEffect20(() => {
6592
+ useEffect21(() => {
6330
6593
  if (typeof globalThis.matchMedia !== "function") return;
6331
6594
  const update = () => setInstalled(isInstalledDisplayMode());
6332
6595
  const lists = ["(display-mode: window-controls-overlay)", "(display-mode: standalone)"].map(
@@ -6353,9 +6616,9 @@ function useFormFactor(rootRef, override) {
6353
6616
  }
6354
6617
 
6355
6618
  // src/layout/useInert.ts
6356
- import { useEffect as useEffect21 } from "react";
6619
+ import { useEffect as useEffect22 } from "react";
6357
6620
  function useInert(ref, inert) {
6358
- useEffect21(() => {
6621
+ useEffect22(() => {
6359
6622
  const element = ref.current;
6360
6623
  if (!element) return;
6361
6624
  element.toggleAttribute("inert", inert);
@@ -6364,9 +6627,9 @@ function useInert(ref, inert) {
6364
6627
  }
6365
6628
 
6366
6629
  // src/layout/useKeyboardInset.ts
6367
- import { useEffect as useEffect22 } from "react";
6630
+ import { useEffect as useEffect23 } from "react";
6368
6631
  function useKeyboardInset(enabled) {
6369
- useEffect22(() => {
6632
+ useEffect23(() => {
6370
6633
  if (!enabled || typeof window === "undefined") return;
6371
6634
  const viewport = window.visualViewport;
6372
6635
  if (!viewport) return;
@@ -6398,10 +6661,10 @@ function useKeyboardInset(enabled) {
6398
6661
  }
6399
6662
 
6400
6663
  // src/layout/useFormFactorAttributes.ts
6401
- import { useEffect as useEffect23, useRef as useRef16 } from "react";
6664
+ import { useEffect as useEffect24, useRef as useRef17 } from "react";
6402
6665
  function useFormFactorAttributes(rootRef, formFactor, layoutMode, drawerOpen) {
6403
- const published = useRef16(/* @__PURE__ */ new Map());
6404
- useEffect23(() => {
6666
+ const published = useRef17(/* @__PURE__ */ new Map());
6667
+ useEffect24(() => {
6405
6668
  const owned = published.current;
6406
6669
  return () => {
6407
6670
  for (const [target, names] of owned) {
@@ -6410,7 +6673,7 @@ function useFormFactorAttributes(rootRef, formFactor, layoutMode, drawerOpen) {
6410
6673
  owned.clear();
6411
6674
  };
6412
6675
  }, []);
6413
- useEffect23(() => {
6676
+ useEffect24(() => {
6414
6677
  if (typeof document === "undefined") return;
6415
6678
  const attributes = {
6416
6679
  ...formFactorDataAttributes(formFactor, layoutMode)
@@ -6436,10 +6699,10 @@ function useFormFactorAttributes(rootRef, formFactor, layoutMode, drawerOpen) {
6436
6699
  }
6437
6700
 
6438
6701
  // src/layout/useInterfaceFontAttribute.ts
6439
- import { useEffect as useEffect24 } from "react";
6702
+ import { useEffect as useEffect25 } from "react";
6440
6703
  var INTERFACE_FONT_ATTRIBUTE = "data-db-interface-font";
6441
6704
  function useInterfaceFontAttribute(preference) {
6442
- useEffect24(() => {
6705
+ useEffect25(() => {
6443
6706
  if (typeof document === "undefined") return;
6444
6707
  const root = document.documentElement;
6445
6708
  if (preference !== "system") {
@@ -6452,7 +6715,7 @@ function useInterfaceFontAttribute(preference) {
6452
6715
  }
6453
6716
 
6454
6717
  // src/DocBlocksShell/useEdgeSwipe.ts
6455
- import { useEffect as useEffect25 } from "react";
6718
+ import { useEffect as useEffect26 } from "react";
6456
6719
  var EDGE_ZONE_PX = 24;
6457
6720
  var AXIS_LOCK_PX = 10;
6458
6721
  var COMMIT_FRACTION = 0.4;
@@ -6465,7 +6728,7 @@ function useEdgeSwipe({
6465
6728
  onOpen,
6466
6729
  onClose
6467
6730
  }) {
6468
- useEffect25(() => {
6731
+ useEffect26(() => {
6469
6732
  const surface = surfaceRef.current;
6470
6733
  if (!enabled || !surface) return;
6471
6734
  let pointerId = null;
@@ -6547,11 +6810,11 @@ function useEdgeSwipe({
6547
6810
  }
6548
6811
 
6549
6812
  // src/DocBlocksShell/useEditorFileDrop.ts
6550
- import { useEffect as useEffect26, useState as useState20 } from "react";
6813
+ import { useEffect as useEffect27, useState as useState21 } from "react";
6551
6814
  var INTERNAL_DRAG_TYPE2 = "application/x-docblocks-entry";
6552
6815
  function useEditorFileDrop(targetRef, enabled, onFiles) {
6553
- const [dropActive, setDropActive] = useState20(false);
6554
- useEffect26(() => {
6816
+ const [dropActive, setDropActive] = useState21(false);
6817
+ useEffect27(() => {
6555
6818
  const element = targetRef.current;
6556
6819
  if (!enabled || !element) {
6557
6820
  setDropActive(false);
@@ -6613,11 +6876,11 @@ function useEditorFileDrop(targetRef, enabled, onFiles) {
6613
6876
  }
6614
6877
 
6615
6878
  // src/DocBlocksShell/useShellShortcuts.ts
6616
- import { useEffect as useEffect27, useRef as useRef17 } from "react";
6879
+ import { useEffect as useEffect28, useRef as useRef18 } from "react";
6617
6880
  function useShellShortcuts(handlers, layoutMode) {
6618
- const handlersRef = useRef17(handlers);
6881
+ const handlersRef = useRef18(handlers);
6619
6882
  handlersRef.current = handlers;
6620
- useEffect27(() => {
6883
+ useEffect28(() => {
6621
6884
  if (typeof window === "undefined") return;
6622
6885
  const onKeyDown = (event) => {
6623
6886
  const id = matchShortcut(event, layoutMode);
@@ -6634,8 +6897,8 @@ function useShellShortcuts(handlers, layoutMode) {
6634
6897
  }
6635
6898
 
6636
6899
  // src/DocBlocksShell/UpdateAvailableNotice.tsx
6637
- import { useState as useState21 } from "react";
6638
- import { Fragment as Fragment10, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
6900
+ import { useState as useState22 } from "react";
6901
+ import { Fragment as Fragment10, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
6639
6902
  var UPDATE_PROMPT_ID = "db-update-available-prompt";
6640
6903
  function UpdateAvailableNotice({
6641
6904
  available,
@@ -6643,12 +6906,12 @@ function UpdateAvailableNotice({
6643
6906
  blocked = false,
6644
6907
  statusBarVisible
6645
6908
  }) {
6646
- const [promptOpen, setPromptOpen] = useState21(false);
6647
- const [applying, setApplying] = useState21(false);
6909
+ const [promptOpen, setPromptOpen] = useState22(false);
6910
+ const [applying, setApplying] = useState22(false);
6648
6911
  if (!available || !onApplyUpdate) return null;
6649
6912
  const promptVisible = promptOpen && !blocked;
6650
- return /* @__PURE__ */ jsxs14(Fragment10, { children: [
6651
- /* @__PURE__ */ jsx16(
6913
+ return /* @__PURE__ */ jsxs15(Fragment10, { children: [
6914
+ /* @__PURE__ */ jsx17(
6652
6915
  "button",
6653
6916
  {
6654
6917
  type: "button",
@@ -6662,16 +6925,16 @@ function UpdateAvailableNotice({
6662
6925
  children: "Update available"
6663
6926
  }
6664
6927
  ),
6665
- promptVisible && /* @__PURE__ */ jsxs14(
6928
+ promptVisible && /* @__PURE__ */ jsxs15(
6666
6929
  "div",
6667
6930
  {
6668
6931
  id: UPDATE_PROMPT_ID,
6669
6932
  className: `db-update-banner${statusBarVisible ? "" : " db-update-banner--floating"}`,
6670
6933
  role: "alert",
6671
6934
  children: [
6672
- /* @__PURE__ */ jsx16("span", { children: "A new version of DocBlocks is available. Reload to update the editor and site pages." }),
6673
- /* @__PURE__ */ jsxs14("div", { className: "db-update-banner-actions", children: [
6674
- /* @__PURE__ */ jsx16(
6935
+ /* @__PURE__ */ jsx17("span", { children: "A new version of DocBlocks is available. Reload to update the editor and site pages." }),
6936
+ /* @__PURE__ */ jsxs15("div", { className: "db-update-banner-actions", children: [
6937
+ /* @__PURE__ */ jsx17(
6675
6938
  "button",
6676
6939
  {
6677
6940
  type: "button",
@@ -6683,7 +6946,7 @@ function UpdateAvailableNotice({
6683
6946
  children: applying ? "Updating\u2026" : "Reload"
6684
6947
  }
6685
6948
  ),
6686
- /* @__PURE__ */ jsx16("button", { type: "button", onClick: () => setPromptOpen(false), disabled: applying, children: "Later" })
6949
+ /* @__PURE__ */ jsx17("button", { type: "button", onClick: () => setPromptOpen(false), disabled: applying, children: "Later" })
6687
6950
  ] })
6688
6951
  ]
6689
6952
  }
@@ -6692,19 +6955,19 @@ function UpdateAvailableNotice({
6692
6955
  }
6693
6956
 
6694
6957
  // src/DocBlocksShell/WelcomeScreen.tsx
6695
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
6958
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
6696
6959
  var WELCOME_HEADLINE = "The Markdown editor that turns one file into anything.";
6697
6960
  var WELCOME_SUMMARY = "Write the way you normally would, then share the same file as a PDF, a Word document, a slide deck, or a video.";
6698
6961
  var WELCOME_PRIVACY_NOTE = "No account, and your documents stay on this device.";
6699
- var DOCBLOCKS_APPS_URL = "https://docblocks.com/desktop/";
6962
+ var DOCBLOCKS_APPS_URL = "https://docblocks.com/docs/#apps";
6700
6963
  function WelcomeScreen({
6701
6964
  onCreateDocument,
6702
6965
  onTour,
6703
6966
  onDismiss,
6704
6967
  offerApps = false
6705
6968
  }) {
6706
- return /* @__PURE__ */ jsxs15("section", { className: "db-welcome-gateway", "aria-labelledby": "db-welcome-gateway-title", children: [
6707
- /* @__PURE__ */ jsx17(
6969
+ return /* @__PURE__ */ jsxs16("section", { className: "db-welcome-gateway", "aria-labelledby": "db-welcome-gateway-title", children: [
6970
+ /* @__PURE__ */ jsx18(
6708
6971
  "button",
6709
6972
  {
6710
6973
  type: "button",
@@ -6715,16 +6978,16 @@ function WelcomeScreen({
6715
6978
  children: "\xD7"
6716
6979
  }
6717
6980
  ),
6718
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-gateway-body", children: [
6719
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-gateway-copy", children: [
6720
- /* @__PURE__ */ jsx17("p", { className: "db-welcome-gateway-eyebrow", children: "Welcome to DocBlocks" }),
6721
- /* @__PURE__ */ jsx17("h1", { id: "db-welcome-gateway-title", children: WELCOME_HEADLINE }),
6722
- /* @__PURE__ */ jsx17("p", { children: WELCOME_SUMMARY }),
6723
- /* @__PURE__ */ jsx17("p", { className: "db-welcome-gateway-note", children: WELCOME_PRIVACY_NOTE }),
6724
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-gateway-actions", children: [
6725
- /* @__PURE__ */ jsx17("button", { type: "button", className: "db-welcome-gateway-primary", onClick: onCreateDocument, children: "Create a document" }),
6726
- /* @__PURE__ */ jsx17("button", { type: "button", className: "db-welcome-gateway-cta", onClick: onTour, children: "Tour the welcome document" }),
6727
- offerApps && /* @__PURE__ */ jsx17(
6981
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-gateway-body", children: [
6982
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-gateway-copy", children: [
6983
+ /* @__PURE__ */ jsx18("p", { className: "db-welcome-gateway-eyebrow", children: "Welcome to DocBlocks" }),
6984
+ /* @__PURE__ */ jsx18("h1", { id: "db-welcome-gateway-title", children: WELCOME_HEADLINE }),
6985
+ /* @__PURE__ */ jsx18("p", { children: WELCOME_SUMMARY }),
6986
+ /* @__PURE__ */ jsx18("p", { className: "db-welcome-gateway-note", children: WELCOME_PRIVACY_NOTE }),
6987
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-gateway-actions", children: [
6988
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "db-welcome-gateway-primary", onClick: onCreateDocument, children: "Create a document" }),
6989
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "db-welcome-gateway-cta", onClick: onTour, children: "Tour the welcome document" }),
6990
+ offerApps && /* @__PURE__ */ jsx18(
6728
6991
  "a",
6729
6992
  {
6730
6993
  className: "db-welcome-gateway-apps",
@@ -6736,61 +6999,61 @@ function WelcomeScreen({
6736
6999
  )
6737
7000
  ] })
6738
7001
  ] }),
6739
- /* @__PURE__ */ jsx17(WelcomeLoop, {})
7002
+ /* @__PURE__ */ jsx18(WelcomeLoop, {})
6740
7003
  ] })
6741
7004
  ] });
6742
7005
  }
6743
7006
  function WelcomeLoop() {
6744
- return /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop", "aria-hidden": "true", children: [
6745
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop-source", children: [
6746
- /* @__PURE__ */ jsx17("span", { className: "db-welcome-loop-file", children: "Lisbon.md" }),
6747
- /* @__PURE__ */ jsxs15("code", { children: [
6748
- /* @__PURE__ */ jsx17("span", { className: "db-welcome-loop-mark", children: "# " }),
7007
+ return /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop", "aria-hidden": "true", children: [
7008
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop-source", children: [
7009
+ /* @__PURE__ */ jsx18("span", { className: "db-welcome-loop-file", children: "Lisbon.md" }),
7010
+ /* @__PURE__ */ jsxs16("code", { children: [
7011
+ /* @__PURE__ */ jsx18("span", { className: "db-welcome-loop-mark", children: "# " }),
6749
7012
  "Lisbon in three days",
6750
7013
  "\n",
6751
7014
  "Trams, tiles, and pastries.",
6752
7015
  "\n\n",
6753
- /* @__PURE__ */ jsx17("span", { className: "db-welcome-loop-mark", children: "## " }),
7016
+ /* @__PURE__ */ jsx18("span", { className: "db-welcome-loop-mark", children: "## " }),
6754
7017
  "Day one",
6755
7018
  "\n",
6756
- /* @__PURE__ */ jsx17("span", { className: "db-welcome-loop-mark", children: "- " }),
7019
+ /* @__PURE__ */ jsx18("span", { className: "db-welcome-loop-mark", children: "- " }),
6757
7020
  "Ride Tram 28",
6758
7021
  "\n",
6759
- /* @__PURE__ */ jsx17("span", { className: "db-welcome-loop-mark", children: "- " }),
7022
+ /* @__PURE__ */ jsx18("span", { className: "db-welcome-loop-mark", children: "- " }),
6760
7023
  "Sunset at Gra\xE7a"
6761
7024
  ] })
6762
7025
  ] }),
6763
- /* @__PURE__ */ jsx17("div", { className: "db-welcome-loop-arrow", children: "\u2192" }),
6764
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop-stage", children: [
6765
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--page", children: [
6766
- /* @__PURE__ */ jsx17("strong", { children: "Lisbon in three days" }),
6767
- /* @__PURE__ */ jsx17("span", { children: "Trams, tiles, and pastries." }),
6768
- /* @__PURE__ */ jsx17("em", { children: "Day one" }),
6769
- /* @__PURE__ */ jsx17("span", { children: "\u2022 Ride Tram 28" }),
6770
- /* @__PURE__ */ jsx17("span", { children: "\u2022 Sunset at Gra\xE7a" })
7026
+ /* @__PURE__ */ jsx18("div", { className: "db-welcome-loop-arrow", children: "\u2192" }),
7027
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop-stage", children: [
7028
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--page", children: [
7029
+ /* @__PURE__ */ jsx18("strong", { children: "Lisbon in three days" }),
7030
+ /* @__PURE__ */ jsx18("span", { children: "Trams, tiles, and pastries." }),
7031
+ /* @__PURE__ */ jsx18("em", { children: "Day one" }),
7032
+ /* @__PURE__ */ jsx18("span", { children: "\u2022 Ride Tram 28" }),
7033
+ /* @__PURE__ */ jsx18("span", { children: "\u2022 Sunset at Gra\xE7a" })
6771
7034
  ] }),
6772
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--slide", children: [
6773
- /* @__PURE__ */ jsx17("strong", { children: "Lisbon in three days" }),
6774
- /* @__PURE__ */ jsx17("span", { children: "Trams, tiles, and pastries." })
7035
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--slide", children: [
7036
+ /* @__PURE__ */ jsx18("strong", { children: "Lisbon in three days" }),
7037
+ /* @__PURE__ */ jsx18("span", { children: "Trams, tiles, and pastries." })
6775
7038
  ] }),
6776
- /* @__PURE__ */ jsxs15("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--video", children: [
6777
- /* @__PURE__ */ jsx17("em", { children: "Day one" }),
6778
- /* @__PURE__ */ jsx17("span", { children: "\u2022 Ride Tram 28" }),
6779
- /* @__PURE__ */ jsx17("span", { children: "\u2022 Sunset at Gra\xE7a" }),
6780
- /* @__PURE__ */ jsx17("i", { className: "db-welcome-loop-play", children: "\u25B6" }),
6781
- /* @__PURE__ */ jsx17("i", { className: "db-welcome-loop-progress" })
7039
+ /* @__PURE__ */ jsxs16("div", { className: "db-welcome-loop-frame db-welcome-loop-frame--video", children: [
7040
+ /* @__PURE__ */ jsx18("em", { children: "Day one" }),
7041
+ /* @__PURE__ */ jsx18("span", { children: "\u2022 Ride Tram 28" }),
7042
+ /* @__PURE__ */ jsx18("span", { children: "\u2022 Sunset at Gra\xE7a" }),
7043
+ /* @__PURE__ */ jsx18("i", { className: "db-welcome-loop-play", children: "\u25B6" }),
7044
+ /* @__PURE__ */ jsx18("i", { className: "db-welcome-loop-progress" })
6782
7045
  ] })
6783
7046
  ] }),
6784
- /* @__PURE__ */ jsxs15("ol", { className: "db-welcome-loop-labels", children: [
6785
- /* @__PURE__ */ jsx17("li", { children: "PDF" }),
6786
- /* @__PURE__ */ jsx17("li", { children: "Slides" }),
6787
- /* @__PURE__ */ jsx17("li", { children: "Video" })
7047
+ /* @__PURE__ */ jsxs16("ol", { className: "db-welcome-loop-labels", children: [
7048
+ /* @__PURE__ */ jsx18("li", { children: "PDF" }),
7049
+ /* @__PURE__ */ jsx18("li", { children: "Slides" }),
7050
+ /* @__PURE__ */ jsx18("li", { children: "Video" })
6788
7051
  ] })
6789
7052
  ] });
6790
7053
  }
6791
7054
 
6792
7055
  // src/DocBlocksShell/useDocumentLinkProvider.ts
6793
- import { useCallback as useCallback16 } from "react";
7056
+ import { useCallback as useCallback17 } from "react";
6794
7057
 
6795
7058
  // src/DocBlocksShell/document-links.ts
6796
7059
  import { parseWorkspacePath as parseWorkspacePath7 } from "@bendyline/docblocks/filesystem";
@@ -6839,7 +7102,7 @@ function createDocumentLinkCandidates(entries, selectedFile, query) {
6839
7102
  // src/DocBlocksShell/useDocumentLinkProvider.ts
6840
7103
  var LISTING_WAIT_MS = 5e3;
6841
7104
  function useDocumentLinkProvider(index, selectedFile, ensureIndex) {
6842
- return useCallback16(
7105
+ return useCallback17(
6843
7106
  async (query) => {
6844
7107
  if (!index || !selectedFile) return [];
6845
7108
  ensureIndex();
@@ -6852,8 +7115,8 @@ function useDocumentLinkProvider(index, selectedFile, ensureIndex) {
6852
7115
  }
6853
7116
 
6854
7117
  // src/WorkspaceIndex/useWorkspaceIndex.ts
6855
- import { useCallback as useCallback17, useEffect as useEffect28, useMemo as useMemo5, useRef as useRef18, useState as useState22, useSyncExternalStore as useSyncExternalStore4 } from "react";
6856
- import { getFileSystemProviderV2 as getFileSystemProviderV28 } from "@bendyline/docblocks/filesystem";
7118
+ import { useCallback as useCallback18, useEffect as useEffect29, useMemo as useMemo5, useRef as useRef19, useState as useState23, useSyncExternalStore as useSyncExternalStore4 } from "react";
7119
+ import { getFileSystemProviderV2 as getFileSystemProviderV29 } from "@bendyline/docblocks/filesystem";
6857
7120
  import { maybeGetDocBlocksHost as maybeGetDocBlocksHost3 } from "@bendyline/docblocks/host";
6858
7121
  var IDLE_WORKSPACE_INDEX_STATUS = Object.freeze({
6859
7122
  phase: "idle",
@@ -6883,10 +7146,10 @@ function whenIdle(callback) {
6883
7146
  return () => clearTimeout(handle);
6884
7147
  }
6885
7148
  function useWorkspaceIndex(provider, workspaceId, mode = "eager") {
6886
- const providerV2 = provider && mode !== "off" ? getFileSystemProviderV28(provider) : null;
6887
- const [index, setIndex] = useState22(null);
6888
- const startRequestedRef = useRef18(false);
6889
- useEffect28(() => {
7149
+ const providerV2 = provider && mode !== "off" ? getFileSystemProviderV29(provider) : null;
7150
+ const [index, setIndex] = useState23(null);
7151
+ const startRequestedRef = useRef19(false);
7152
+ useEffect29(() => {
6890
7153
  setIndex(null);
6891
7154
  startRequestedRef.current = false;
6892
7155
  if (!providerV2 || !workspaceId) return;
@@ -6908,7 +7171,7 @@ function useWorkspaceIndex(provider, workspaceId, mode = "eager") {
6908
7171
  created?.dispose();
6909
7172
  };
6910
7173
  }, [providerV2, workspaceId, mode]);
6911
- useEffect28(() => {
7174
+ useEffect29(() => {
6912
7175
  if (!index) return;
6913
7176
  const refresh = () => index.refresh();
6914
7177
  const onVisibilityChange = () => {
@@ -6925,16 +7188,16 @@ function useWorkspaceIndex(provider, workspaceId, mode = "eager") {
6925
7188
  stopResume?.();
6926
7189
  };
6927
7190
  }, [index]);
6928
- const subscribe = useCallback17(
7191
+ const subscribe = useCallback18(
6929
7192
  (listener) => index ? index.subscribe(listener) : () => void 0,
6930
7193
  [index]
6931
7194
  );
6932
- const getStatus = useCallback17(
7195
+ const getStatus = useCallback18(
6933
7196
  () => index ? index.getStatus() : IDLE_WORKSPACE_INDEX_STATUS,
6934
7197
  [index]
6935
7198
  );
6936
7199
  const status = useSyncExternalStore4(subscribe, getStatus, getStatus);
6937
- const ensure = useCallback17(() => {
7200
+ const ensure = useCallback18(() => {
6938
7201
  if (!index) {
6939
7202
  startRequestedRef.current = true;
6940
7203
  return;
@@ -6946,7 +7209,7 @@ function useWorkspaceIndex(provider, workspaceId, mode = "eager") {
6946
7209
  }
6947
7210
 
6948
7211
  // src/WorkspaceIndex/recent-documents.ts
6949
- import { tryParseWorkspacePath as tryParseWorkspacePath3 } from "@bendyline/docblocks/filesystem";
7212
+ import { tryParseWorkspacePath as tryParseWorkspacePath4 } from "@bendyline/docblocks/filesystem";
6950
7213
  var RECENT_DOCUMENTS_STORAGE_KEY = "docblocks:recentDocuments:v1";
6951
7214
  var MAX_PATHS_PER_WORKSPACE = 50;
6952
7215
  var MAX_WORKSPACES = 50;
@@ -6965,7 +7228,7 @@ function defaultStorage() {
6965
7228
  }
6966
7229
  function canonicalPath(path) {
6967
7230
  if (path.length === 0 || path.length > MAX_PATH_LENGTH) return null;
6968
- return tryParseWorkspacePath3(path) || null;
7231
+ return tryParseWorkspacePath4(path) || null;
6969
7232
  }
6970
7233
  function parseRecentDocuments(value) {
6971
7234
  const parsed = /* @__PURE__ */ new Map();
@@ -7071,14 +7334,14 @@ var WorkspaceAuthorityBarrier = class {
7071
7334
 
7072
7335
  // src/DocBlocksShell/transient-workspace-move.ts
7073
7336
  import {
7074
- getFileSystemProviderV2 as getFileSystemProviderV29
7337
+ getFileSystemProviderV2 as getFileSystemProviderV210
7075
7338
  } from "@bendyline/docblocks/filesystem";
7076
7339
  function pathDepth(path) {
7077
7340
  return path ? path.split("/").length : 0;
7078
7341
  }
7079
7342
  async function copyTransientWorkspaceContents(source, destination) {
7080
- const sourceV2 = getFileSystemProviderV29(source);
7081
- const destinationV2 = getFileSystemProviderV29(destination);
7343
+ const sourceV2 = getFileSystemProviderV210(source);
7344
+ const destinationV2 = getFileSystemProviderV210(destination);
7082
7345
  if (!sourceV2 || !destinationV2) {
7083
7346
  throw new Error("Moving a temporary document requires a current workspace provider.");
7084
7347
  }
@@ -7175,63 +7438,75 @@ function isWelcomeDocumentPath(path) {
7175
7438
  function isAppDefaultWorkspace(workspaceId, hostDefaultWorkspaceIds) {
7176
7439
  return hostDefaultWorkspaceIds ? hostDefaultWorkspaceIds.has(workspaceId) : workspaceId === DEFAULT_WORKSPACE_ID;
7177
7440
  }
7178
- var WELCOME_DOCUMENT_CONTENT = [
7179
- "---",
7180
- "squisq-theme: warm-earth",
7181
- "squisq-cover-slide: false",
7182
- "---",
7183
- "",
7184
- '# Welcome to DocBlocks {[title subtitle="Write once. Share it as a document, a deck, or a video."]}',
7185
- "",
7186
- "This is an ordinary document, so go ahead and change it. Click anywhere and type; DocBlocks saves as you go.",
7187
- "",
7188
- "## Write the way you already do {[list]}",
7189
- "",
7190
- "- Type to add text, and use the toolbar for headings, lists, and bold or italic text.",
7191
- "- Choose **Insert** in the toolbar to add a table, a checklist, a chart, a diagram, or a picture.",
7192
- "- Choose **Source** to see the Markdown text behind this page. Both views edit the same document.",
7193
- "",
7194
- "## One document, many ways to share it {[diagram]}",
7195
- "",
7196
- "```diagram",
7197
- " +-----------------+",
7198
- " | Your document |",
7199
- " +--------+--------+",
7200
- " |",
7201
- " +-----------+------+-----+------------+",
7202
- " | | | |",
7203
- " v v v v",
7204
- "+-------+ +--------+ +----------+ +---------+",
7205
- "| PDF | | Word | | Slides | | Video |",
7206
- "+-------+ +--------+ +----------+ +---------+",
7207
- "```",
7208
- "",
7209
- "## Present it or send it {[content]}",
7210
- "",
7211
- "Each heading starts a new slide, so this document is already a slideshow. Choose **Slideshow** at the top to see it, or **Export** to save it as a PDF, a Word document, a PowerPoint deck, an e-book, a web page, or a video.",
7212
- "",
7213
- "## Your documents stay yours {[factCard]}",
7214
- "",
7215
- "There is no account to create. Your documents stay on this device as plain text files that other apps can open too. Choose **Back up** at the bottom of the file list to keep a copy somewhere safe.",
7216
- "",
7217
- "## Make your own {[list]}",
7218
- "",
7219
- "1. Choose **New File** above the file list and give it a name.",
7220
- "2. Write a heading and a few sentences.",
7221
- "3. Choose **Slideshow** to present it, or **Export** to share it.",
7222
- "",
7223
- "## Go further {[list]}",
7224
- "",
7225
- "- [Get the desktop app](https://docblocks.com/desktop/) to work in folders on your computer.",
7226
- "- [Add DocBlocks to VS Code](https://docblocks.com/vscode/) to edit documents beside your code.",
7227
- "- [Read the guides](https://docblocks.com/docs/) for everything else.",
7228
- // End with a newline, as the Write view saves every document. Without it,
7229
- // merely opening the welcome document in Write rewrote the file.
7230
- ""
7231
- ].join("\n");
7441
+ function welcomeDocumentContent({
7442
+ browserStorage,
7443
+ desktopApp
7444
+ }) {
7445
+ return [
7446
+ "---",
7447
+ "squisq-theme: warm-earth",
7448
+ "squisq-cover-slide: false",
7449
+ "---",
7450
+ "",
7451
+ '# Welcome to DocBlocks {[title subtitle="Write once. Share it as a document, a deck, or a video."]}',
7452
+ "",
7453
+ "This is an ordinary document, so go ahead and change it. Click anywhere and type; DocBlocks saves as you go.",
7454
+ "",
7455
+ "## Write the way you already do {[list]}",
7456
+ "",
7457
+ "- Type to add text, and use the toolbar for headings, lists, and bold or italic text.",
7458
+ "- Choose **Insert** in the toolbar to add a table, a checklist, a chart, a diagram, or a picture.",
7459
+ "- Choose **Source** to see the Markdown text behind this page. Both views edit the same document.",
7460
+ "",
7461
+ "## One document, many ways to share it {[diagram]}",
7462
+ "",
7463
+ "```diagram",
7464
+ " +-----------------+",
7465
+ " | Your document |",
7466
+ " +--------+--------+",
7467
+ " |",
7468
+ " +-----------+------+-----+------------+",
7469
+ " | | | |",
7470
+ " v v v v",
7471
+ "+-------+ +--------+ +----------+ +---------+",
7472
+ "| PDF | | Word | | Slides | | Video |",
7473
+ "+-------+ +--------+ +----------+ +---------+",
7474
+ "```",
7475
+ "",
7476
+ "## Present it or send it {[content]}",
7477
+ "",
7478
+ "Each heading starts a new slide, so this document is already a slideshow. Choose **Slideshow** at the top to see it, or **Export** to save it as a PDF, a Word document, a PowerPoint deck, an e-book, a web page, or a video.",
7479
+ "",
7480
+ "## Your documents stay yours {[factCard]}",
7481
+ "",
7482
+ // Not "files other apps can open": in the browser they live in browser storage.
7483
+ "There is no account to create. Your documents stay on this device, written in plain Markdown that any text editor can read." + // Only browser storage shows Back up in the file list. Elsewhere the
7484
+ // documents are files the device itself keeps.
7485
+ (browserStorage ? " Choose **Back up** at the bottom of the file list to keep a copy somewhere safe." : ""),
7486
+ "",
7487
+ "## Make your own {[list]}",
7488
+ "",
7489
+ "1. Choose **New File** above the file list and give it a name.",
7490
+ "2. Write a heading and a few sentences.",
7491
+ "3. Choose **Slideshow** to present it, or **Export** to share it.",
7492
+ "",
7493
+ "## Go further {[list]}",
7494
+ "",
7495
+ desktopApp ? "- Choose **Open Folder** in the workspace menu above the file list to work on Markdown files you already have." : "- [Get the desktop app](https://docblocks.com/desktop/) to work in folders on your computer.",
7496
+ "- [Add DocBlocks to VS Code](https://docblocks.com/vscode/) to edit documents beside your code.",
7497
+ "- [Read the guides](https://docblocks.com/docs/) for everything else.",
7498
+ // End with a newline, as the Write view saves every document. Without it,
7499
+ // merely opening the welcome document in Write rewrote the file.
7500
+ ""
7501
+ ].join("\n");
7502
+ }
7503
+ var WELCOME_DOCUMENT_CONTENT = welcomeDocumentContent({
7504
+ browserStorage: true,
7505
+ desktopApp: false
7506
+ });
7232
7507
 
7233
7508
  // src/DocBlocksShell/DocBlocksShell.tsx
7234
- import { Fragment as Fragment11, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
7509
+ import { Fragment as Fragment11, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
7235
7510
  var editorShellModulePromise = null;
7236
7511
  function loadEditorShell() {
7237
7512
  editorShellModulePromise ?? (editorShellModulePromise = (async () => {
@@ -7243,13 +7518,13 @@ function loadEditorShell() {
7243
7518
  return editorShellModulePromise;
7244
7519
  }
7245
7520
  var EditorShell = lazy2(loadEditorShell);
7246
- var DocumentConflictDialog = lazy2(() => import("./DocumentConflictDialog-5LRVIGXS.js"));
7247
- var GitUI = lazy2(() => import("./GitUI-RCWUZVS6.js").then((m) => ({ default: m.GitUI })));
7521
+ var DocumentConflictDialog = lazy2(() => import("./DocumentConflictDialog-DZ2BV2IH.js"));
7522
+ var GitUI = lazy2(() => import("./GitUI-F3CNBURE.js").then((m) => ({ default: m.GitUI })));
7248
7523
  var QuickOpenDialog = lazy2(
7249
- () => import("./QuickOpenDialog-2KK4SEBU.js").then((m) => ({ default: m.QuickOpenDialog }))
7524
+ () => import("./QuickOpenDialog-ENVFGIHS.js").then((m) => ({ default: m.QuickOpenDialog }))
7250
7525
  );
7251
7526
  var KeyboardShortcutsDialog = lazy2(
7252
- () => import("./KeyboardShortcutsDialog-G5OBDTNR.js").then((m) => ({ default: m.KeyboardShortcutsDialog }))
7527
+ () => import("./KeyboardShortcutsDialog-HVKDFP5Z.js").then((m) => ({ default: m.KeyboardShortcutsDialog }))
7253
7528
  );
7254
7529
  var WorkspaceSearchPanel = lazy2(
7255
7530
  () => import("./WorkspaceSearchPanel-J6BKEZQJ.js").then((m) => ({
@@ -7263,37 +7538,37 @@ var GitToolbarControl = lazy2(
7263
7538
  () => import("./GitToolbarControl-B7KG4MFP.js").then((m) => ({ default: m.GitToolbarControl }))
7264
7539
  );
7265
7540
  var AiToolbarControl = lazy2(
7266
- () => import("./AiEditorAssistant-YLJ5OZK2.js").then((module) => ({
7541
+ () => import("./AiEditorAssistant-V4MZRC6Q.js").then((module) => ({
7267
7542
  default: module.AiToolbarControl
7268
7543
  }))
7269
7544
  );
7270
7545
  var PresentationMaker = lazy2(
7271
- () => import("./PresentationMaker-P2JP6UUX.js").then((module) => ({
7546
+ () => import("./PresentationMaker-H5B44Q7P.js").then((module) => ({
7272
7547
  default: module.PresentationMaker
7273
7548
  }))
7274
7549
  );
7275
7550
  var AiReviewPanel = lazy2(
7276
- () => import("./AiEditorAssistant-YLJ5OZK2.js").then((module) => ({
7551
+ () => import("./AiEditorAssistant-V4MZRC6Q.js").then((module) => ({
7277
7552
  default: module.AiReviewPanel
7278
7553
  }))
7279
7554
  );
7280
7555
  var AiIllustratePanel = lazy2(
7281
- () => import("./AiDiagrams-QE3LVYFK.js").then((module) => ({
7556
+ () => import("./AiDiagrams-5P657GFC.js").then((module) => ({
7282
7557
  default: module.AiIllustratePanel
7283
7558
  }))
7284
7559
  );
7285
7560
  var TextFromNarration = lazy2(
7286
- () => import("./TextFromNarration-NITJQ4IB.js").then((module) => ({
7561
+ () => import("./TextFromNarration-EYOMW2IW.js").then((module) => ({
7287
7562
  default: module.TextFromNarration
7288
7563
  }))
7289
7564
  );
7290
7565
  var SpeechToolbarControl = lazy2(
7291
- () => import("./SpeechToolbarControl-CLU7RB2G.js").then((module) => ({
7566
+ () => import("./SpeechToolbarControl-KNI2C4JJ.js").then((module) => ({
7292
7567
  default: module.SpeechToolbarControl
7293
7568
  }))
7294
7569
  );
7295
7570
  var SpeechMenuBridge = lazy2(
7296
- () => import("./SpeechToolbarControl-CLU7RB2G.js").then((module) => ({
7571
+ () => import("./SpeechToolbarControl-KNI2C4JJ.js").then((module) => ({
7297
7572
  default: module.SpeechMenuBridge
7298
7573
  }))
7299
7574
  );
@@ -7371,10 +7646,10 @@ function isMemoryWorkspaceProvider(provider) {
7371
7646
  return typeof candidate.captureContents === "function" && typeof candidate.replaceContents === "function" && typeof candidate.treeVersion === "number";
7372
7647
  }
7373
7648
  function useOsTheme() {
7374
- const [dark, setDark] = useState23(
7649
+ const [dark, setDark] = useState24(
7375
7650
  () => typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches
7376
7651
  );
7377
- useEffect29(() => {
7652
+ useEffect30(() => {
7378
7653
  const mq = window.matchMedia("(prefers-color-scheme: dark)");
7379
7654
  const handler = (e) => setDark(e.matches);
7380
7655
  mq.addEventListener("change", handler);
@@ -7434,19 +7709,19 @@ function pathContains(parentPath2, candidatePath) {
7434
7709
  return workspacePathContains(parseWorkspacePath8(parentPath2), parseWorkspacePath8(candidatePath));
7435
7710
  }
7436
7711
  async function readProviderDirectory2(provider, path) {
7437
- const providerV2 = getFileSystemProviderV210(provider);
7712
+ const providerV2 = getFileSystemProviderV211(provider);
7438
7713
  if (!providerV2) return provider.readDirectory(path);
7439
7714
  const entries = await providerV2.readDirectory(parseWorkspacePath8(path));
7440
7715
  return entries.map((entry) => ({ kind: entry.kind, name: entry.name, path: entry.path }));
7441
7716
  }
7442
7717
  async function readProviderText(provider, path) {
7443
- const providerV2 = getFileSystemProviderV210(provider);
7718
+ const providerV2 = getFileSystemProviderV211(provider);
7444
7719
  if (!providerV2) return provider.readFile(path);
7445
7720
  const file = await providerV2.readFile(parseWorkspacePath8(path));
7446
7721
  return file ? decodeUtf8Text(file.data, { label: "The document", path: parseWorkspacePath8(path) }) : null;
7447
7722
  }
7448
7723
  async function pinnedProviderFileExists(provider, path) {
7449
- const providerV2 = getFileSystemProviderV210(provider);
7724
+ const providerV2 = getFileSystemProviderV211(provider);
7450
7725
  if (!providerV2) return provider.exists(path);
7451
7726
  const entry = await providerV2.stat(parseWorkspacePath8(path));
7452
7727
  if (!entry) return false;
@@ -7454,7 +7729,7 @@ async function pinnedProviderFileExists(provider, path) {
7454
7729
  return true;
7455
7730
  }
7456
7731
  async function removePinnedProviderFile(provider, path) {
7457
- const providerV2 = getFileSystemProviderV210(provider);
7732
+ const providerV2 = getFileSystemProviderV211(provider);
7458
7733
  if (providerV2) {
7459
7734
  const canonical = parseWorkspacePath8(path);
7460
7735
  const entry = await providerV2.stat(canonical);
@@ -7472,7 +7747,7 @@ async function removePinnedProviderFile(provider, path) {
7472
7747
  return true;
7473
7748
  }
7474
7749
  async function readStableFileSnapshot(provider, path) {
7475
- const providerV2 = getFileSystemProviderV210(provider);
7750
+ const providerV2 = getFileSystemProviderV211(provider);
7476
7751
  if (providerV2) {
7477
7752
  const read2 = await providerV2.readFile(parseWorkspacePath8(path));
7478
7753
  return {
@@ -7516,7 +7791,7 @@ async function ensureHandleWritePermission(handle) {
7516
7791
  }
7517
7792
  }
7518
7793
  async function copyProviderToContainer(src, container, pathPrefix) {
7519
- const providerV2 = getFileSystemProviderV210(src);
7794
+ const providerV2 = getFileSystemProviderV211(src);
7520
7795
  if (providerV2) {
7521
7796
  const snapshot = await providerV2.snapshot();
7522
7797
  for (const entry of snapshot.entries) {
@@ -7560,7 +7835,7 @@ async function createHostProviderFromWorkspace(ws) {
7560
7835
  var DESKTOP_TOOLBAR_WRAP_WIDTH = 900;
7561
7836
  var WEB_TOOLBAR_WRAP_WIDTH = 700;
7562
7837
  function FolderGlyph() {
7563
- return /* @__PURE__ */ jsx18(
7838
+ return /* @__PURE__ */ jsx19(
7564
7839
  "svg",
7565
7840
  {
7566
7841
  viewBox: "0 0 24 24",
@@ -7569,12 +7844,12 @@ function FolderGlyph() {
7569
7844
  strokeWidth: "1.5",
7570
7845
  strokeLinejoin: "round",
7571
7846
  "aria-hidden": "true",
7572
- children: /* @__PURE__ */ jsx18("path", { d: "M3 7a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7z" })
7847
+ children: /* @__PURE__ */ jsx19("path", { d: "M3 7a1 1 0 0 1 1-1h5l2 2h9a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7z" })
7573
7848
  }
7574
7849
  );
7575
7850
  }
7576
7851
  function FileGlyph() {
7577
- return /* @__PURE__ */ jsxs16(
7852
+ return /* @__PURE__ */ jsxs17(
7578
7853
  "svg",
7579
7854
  {
7580
7855
  viewBox: "0 0 24 24",
@@ -7584,14 +7859,14 @@ function FileGlyph() {
7584
7859
  strokeLinejoin: "round",
7585
7860
  "aria-hidden": "true",
7586
7861
  children: [
7587
- /* @__PURE__ */ jsx18("path", { d: "M6 3h8l5 5v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" }),
7588
- /* @__PURE__ */ jsx18("path", { d: "M14 3v5h5" })
7862
+ /* @__PURE__ */ jsx19("path", { d: "M6 3h8l5 5v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" }),
7863
+ /* @__PURE__ */ jsx19("path", { d: "M14 3v5h5" })
7589
7864
  ]
7590
7865
  }
7591
7866
  );
7592
7867
  }
7593
7868
  function CollapseSidebarGlyph() {
7594
- return /* @__PURE__ */ jsxs16(
7869
+ return /* @__PURE__ */ jsxs17(
7595
7870
  "svg",
7596
7871
  {
7597
7872
  viewBox: "0 0 64 64",
@@ -7602,9 +7877,9 @@ function CollapseSidebarGlyph() {
7602
7877
  strokeLinejoin: "round",
7603
7878
  "aria-hidden": "true",
7604
7879
  children: [
7605
- /* @__PURE__ */ jsx18("rect", { x: "7", y: "10", width: "50", height: "44", rx: "6" }),
7606
- /* @__PURE__ */ jsx18("path", { d: "M24 10v44" }),
7607
- /* @__PURE__ */ jsx18("path", { d: "m17 25-7 7 7 7" })
7880
+ /* @__PURE__ */ jsx19("rect", { x: "7", y: "10", width: "50", height: "44", rx: "6" }),
7881
+ /* @__PURE__ */ jsx19("path", { d: "M24 10v44" }),
7882
+ /* @__PURE__ */ jsx19("path", { d: "m17 25-7 7 7 7" })
7608
7883
  ]
7609
7884
  }
7610
7885
  );
@@ -7641,10 +7916,10 @@ function DocBlocksShell({
7641
7916
  offlineReady = false
7642
7917
  }) {
7643
7918
  const osTheme = useOsTheme();
7644
- const [themePreference, setThemePreference] = useState23(loadThemePreference);
7645
- const [accentColor, setAccentColor] = useState23(loadAccentColor);
7646
- const [interfaceFont, setInterfaceFont] = useState23(loadInterfaceFont);
7647
- const [writeCanvasSettings, setWriteCanvasSettings] = useState23(
7919
+ const [themePreference, setThemePreference] = useState24(loadThemePreference);
7920
+ const [accentColor, setAccentColor] = useState24(loadAccentColor);
7921
+ const [interfaceFont, setInterfaceFont] = useState24(loadInterfaceFont);
7922
+ const [writeCanvasSettings, setWriteCanvasSettings] = useState24(
7648
7923
  loadWriteCanvasPreferences
7649
7924
  );
7650
7925
  const editorWriteCanvasSettings = useMemo6(
@@ -7658,66 +7933,67 @@ function DocBlocksShell({
7658
7933
  const resolvedTheme = resolveShellTheme({ preference: themePreference, hostTheme, osTheme });
7659
7934
  const ai = hostSupports2("aiAssist") ? maybeGetDocBlocksHost4()?.ai : void 0;
7660
7935
  const speech = hostSupports2("speechModelManagement") ? maybeGetDocBlocksHost4()?.speech : void 0;
7661
- const [settingsRequest, setSettingsRequest] = useState23(0);
7662
- const openSettings = useCallback18(() => setSettingsRequest((count) => count + 1), []);
7936
+ const trayMenu = hostSupports2("trayIcon") ? maybeGetDocBlocksHost4()?.menu : void 0;
7937
+ const [settingsRequest, setSettingsRequest] = useState24(0);
7938
+ const openSettings = useCallback19(() => setSettingsRequest((count) => count + 1), []);
7663
7939
  const speechInput = useMemo6(
7664
7940
  () => speech && hostSupports2("speechInput") ? createSpeechInputProvider(speech, { onRequestSetup: openSettings }) : null,
7665
7941
  [speech, openSettings]
7666
7942
  );
7667
- const handleThemeChange = useCallback18((pref) => {
7943
+ const handleThemeChange = useCallback19((pref) => {
7668
7944
  setThemePreference(pref);
7669
7945
  saveThemePreference(pref);
7670
7946
  }, []);
7671
- useEffect29(() => {
7947
+ useEffect30(() => {
7672
7948
  if (hostSupports2("ownsWindowChrome") || typeof document === "undefined") return;
7673
7949
  const metas = document.querySelectorAll('meta[name="theme-color"]');
7674
7950
  metas.forEach((meta) => {
7675
7951
  meta.content = DB_CHROME_COLORS[resolvedTheme];
7676
7952
  });
7677
7953
  }, [resolvedTheme]);
7678
- const handleAccentColorChange = useCallback18((color) => {
7954
+ const handleAccentColorChange = useCallback19((color) => {
7679
7955
  setAccentColor(color);
7680
7956
  saveAccentColor(color);
7681
7957
  }, []);
7682
- const handleWriteCanvasSettingsChange = useCallback18((settings) => {
7958
+ const handleWriteCanvasSettingsChange = useCallback19((settings) => {
7683
7959
  setWriteCanvasSettings(settings);
7684
7960
  saveWriteCanvasPreferences(settings);
7685
7961
  }, []);
7686
- const [proofingPreferences, setProofingPreferences] = useState23(loadProofingPreferences);
7687
- const handleProofingPreferencesChange = useCallback18((settings) => {
7962
+ const [proofingPreferences, setProofingPreferences] = useState24(loadProofingPreferences);
7963
+ const handleProofingPreferencesChange = useCallback19((settings) => {
7688
7964
  setProofingPreferences(settings);
7689
7965
  saveProofingPreferences(settings);
7690
7966
  }, []);
7691
- const [viewPreferences, setViewPreferences] = useState23(loadViewPreferences);
7692
- const handleViewPreferencesChange = useCallback18((prefs) => {
7967
+ const [viewPreferences, setViewPreferences] = useState24(loadViewPreferences);
7968
+ const handleViewPreferencesChange = useCallback19((prefs) => {
7693
7969
  setViewPreferences(prefs);
7694
7970
  saveViewPreferences(prefs);
7695
7971
  }, []);
7696
- const shellRef = useRef19(null);
7972
+ const shellRef = useRef20(null);
7697
7973
  const formFactor = useFormFactor(shellRef);
7698
- const [sidebarPreference, setSidebarPreference] = useState23(loadSidebarPreference);
7974
+ const [sidebarPreference, setSidebarPreference] = useState24(loadSidebarPreference);
7699
7975
  const layoutMode = resolveLayoutMode(formFactor, sidebarPreference);
7700
7976
  const singlePane = layoutMode === "single-pane";
7701
- const updateSidebarPreference = useCallback18((preference) => {
7977
+ const updateSidebarPreference = useCallback19((preference) => {
7702
7978
  setSidebarPreference(preference);
7703
7979
  saveSidebarPreference(preference);
7704
7980
  }, []);
7705
7981
  const defaultPreviewViewportPreset = useResponsivePreviewViewportPreset();
7706
- const [mobileShowEditor, setMobileShowEditor] = useState23(
7982
+ const [mobileShowEditor, setMobileShowEditor] = useState24(
7707
7983
  () => singlePane && isWelcomeGatewayDismissed()
7708
7984
  );
7709
- useEffect29(() => {
7985
+ useEffect30(() => {
7710
7986
  if (singlePane) void loadEditorShell();
7711
7987
  }, [singlePane]);
7712
7988
  useFormFactorAttributes(shellRef, formFactor, layoutMode, !mobileShowEditor);
7713
7989
  useInterfaceFontAttribute(interfaceFont);
7714
7990
  useKeyboardInset(formFactor.pointer === "coarse");
7715
7991
  const drawerOpen = singlePane && !mobileShowEditor;
7716
- const [sidebarWidth, setSidebarWidth] = useState23(loadSidebarWidth);
7992
+ const [sidebarWidth, setSidebarWidth] = useState24(loadSidebarWidth);
7717
7993
  const canRestoreSplitView = sidebarPreference === "collapsed" && formFactor.splitPaneAllowed;
7718
- const editorAreaRef = useRef19(null);
7719
- const [desktopToolbarWrapped, setDesktopToolbarWrapped] = useState23(false);
7720
- useEffect29(() => {
7994
+ const editorAreaRef = useRef20(null);
7995
+ const [desktopToolbarWrapped, setDesktopToolbarWrapped] = useState24(false);
7996
+ useEffect30(() => {
7721
7997
  const editorArea = editorAreaRef.current;
7722
7998
  if (!editorArea || singlePane) {
7723
7999
  setDesktopToolbarWrapped(false);
@@ -7738,24 +8014,25 @@ function DocBlocksShell({
7738
8014
  }, [singlePane]);
7739
8015
  const showBrowserStorageWarning = hostSupports2("browserOriginStorage");
7740
8016
  const hostEnvironment = getHostEnvironment2();
8017
+ const desktopApp = hostEnvironment?.surface === "electron";
7741
8018
  const appVersion = hostEnvironment ? `${hostEnvironment.appVersion} ${hostEnvironment.surfaceLabel}` : issueReportVersion ?? "web";
7742
8019
  const issueReportUrl = buildIssueReportUrl({
7743
8020
  reportedAt: /* @__PURE__ */ new Date(),
7744
8021
  version: appVersion,
7745
8022
  userAgent: typeof navigator === "undefined" ? "" : navigator.userAgent
7746
8023
  });
7747
- const [browserStoragePersistent, setBrowserStoragePersistent] = useState23(false);
7748
- const [saveToast, setSaveToast] = useState23(null);
7749
- const saveToastTimerRef = useRef19(null);
7750
- const showToast = useCallback18((kind, message) => {
8024
+ const [browserStoragePersistent, setBrowserStoragePersistent] = useState24(false);
8025
+ const [saveToast, setSaveToast] = useState24(null);
8026
+ const saveToastTimerRef = useRef20(null);
8027
+ const showToast = useCallback19((kind, message) => {
7751
8028
  setSaveToast({ kind, message });
7752
8029
  if (saveToastTimerRef.current) clearTimeout(saveToastTimerRef.current);
7753
8030
  saveToastTimerRef.current = setTimeout(() => setSaveToast(null), 3e3);
7754
8031
  }, []);
7755
8032
  const { prompt: promptForText, promptDialog } = usePromptDialog();
7756
- const [newDocumentDialogOpen, setNewDocumentDialogOpen] = useState23(false);
8033
+ const [newDocumentDialogOpen, setNewDocumentDialogOpen] = useState24(false);
7757
8034
  const { confirm: confirmAction, acknowledge, confirmDialog } = useConfirmDialog();
7758
- const confirmDeleteEntry = useCallback18(
8035
+ const confirmDeleteEntry = useCallback19(
7759
8036
  (message) => confirmAction({
7760
8037
  title: "Delete",
7761
8038
  message,
@@ -7764,15 +8041,15 @@ function DocBlocksShell({
7764
8041
  }),
7765
8042
  [confirmAction]
7766
8043
  );
7767
- const [installPromptEvent, setInstallPromptEvent] = useState23(
8044
+ const [installPromptEvent, setInstallPromptEvent] = useState24(
7768
8045
  null
7769
8046
  );
7770
- const panesRef = useRef19(null);
7771
- const sidebarRef = useRef19(null);
8047
+ const panesRef = useRef20(null);
8048
+ const sidebarRef = useRef20(null);
7772
8049
  useInert(sidebarRef, singlePane && !drawerOpen);
7773
8050
  useInert(editorAreaRef, drawerOpen);
7774
- const openDrawer = useCallback18(() => setMobileShowEditor(false), []);
7775
- const closeDrawer = useCallback18(() => setMobileShowEditor(true), []);
8051
+ const openDrawer = useCallback19(() => setMobileShowEditor(false), []);
8052
+ const closeDrawer = useCallback19(() => setMobileShowEditor(true), []);
7776
8053
  useEdgeSwipe({
7777
8054
  surfaceRef: panesRef,
7778
8055
  drawerRef: sidebarRef,
@@ -7781,8 +8058,8 @@ function DocBlocksShell({
7781
8058
  onOpen: openDrawer,
7782
8059
  onClose: closeDrawer
7783
8060
  });
7784
- const dragStateRef = useRef19(null);
7785
- const handleResizerPointerDown = useCallback18(
8061
+ const dragStateRef = useRef20(null);
8062
+ const handleResizerPointerDown = useCallback19(
7786
8063
  (e) => {
7787
8064
  if (e.button !== 0) return;
7788
8065
  dragStateRef.current = { startX: e.clientX, startWidth: sidebarWidth };
@@ -7834,15 +8111,15 @@ function DocBlocksShell({
7834
8111
  },
7835
8112
  [sidebarWidth, updateSidebarPreference]
7836
8113
  );
7837
- const [provider, setProvider] = useState23(null);
7838
- const [workspaceStartupError, setWorkspaceStartupError] = useState23(null);
8114
+ const [provider, setProvider] = useState24(null);
8115
+ const [workspaceStartupError, setWorkspaceStartupError] = useState24(null);
7839
8116
  const workspaceStarting = provider === null && workspaceStartupError === null;
7840
- const [activeWorkspaceId, setActiveWorkspaceId] = useState23(null);
7841
- const [defaultNewFileFormat, setDefaultNewFileFormat] = useState23(DEFAULT_NEW_FILE_FORMAT);
7842
- useEffect29(() => {
8117
+ const [activeWorkspaceId, setActiveWorkspaceId] = useState24(null);
8118
+ const [defaultNewFileFormat, setDefaultNewFileFormat] = useState24(DEFAULT_NEW_FILE_FORMAT);
8119
+ useEffect30(() => {
7843
8120
  setDefaultNewFileFormat(loadLastNewFileFormat(activeWorkspaceId));
7844
8121
  }, [activeWorkspaceId]);
7845
- const rememberNewFileFormat = useCallback18(
8122
+ const rememberNewFileFormat = useCallback19(
7846
8123
  (format) => {
7847
8124
  setDefaultNewFileFormat(format);
7848
8125
  if (activeWorkspaceId && getTransientWorkspace(activeWorkspaceId) === null) {
@@ -7851,19 +8128,19 @@ function DocBlocksShell({
7851
8128
  },
7852
8129
  [activeWorkspaceId]
7853
8130
  );
7854
- const [activeWorkspaceDescriptor, setActiveWorkspaceDescriptor] = useState23(null);
8131
+ const [activeWorkspaceDescriptor, setActiveWorkspaceDescriptor] = useState24(null);
7855
8132
  const defaultProofingIgnoreStore = useMemo6(
7856
8133
  () => createLocalProofingIgnoreStore(() => activeWorkspaceId),
7857
8134
  [activeWorkspaceId]
7858
8135
  );
7859
8136
  const effectiveProofingIgnoreStore = proofingIgnoreStore === void 0 ? defaultProofingIgnoreStore : proofingIgnoreStore;
7860
- useEffect29(() => {
8137
+ useEffect30(() => {
7861
8138
  if (!provider || getTransientWorkspace(provider.id)) return;
7862
- const providerV2 = getFileSystemProviderV210(provider);
8139
+ const providerV2 = getFileSystemProviderV211(provider);
7863
8140
  return providerV2 ? retainFileSystemProvider(providerV2) : void 0;
7864
8141
  }, [provider]);
7865
- const [descriptorRefreshKey, setDescriptorRefreshKey] = useState23(0);
7866
- useEffect29(() => {
8142
+ const [descriptorRefreshKey, setDescriptorRefreshKey] = useState24(0);
8143
+ useEffect30(() => {
7867
8144
  let cancelled = false;
7868
8145
  if (!activeWorkspaceId) {
7869
8146
  setActiveWorkspaceDescriptor(null);
@@ -7876,12 +8153,12 @@ function DocBlocksShell({
7876
8153
  cancelled = true;
7877
8154
  };
7878
8155
  }, [activeWorkspaceId, descriptorRefreshKey]);
7879
- const [pinnedDocuments, setPinnedDocuments] = useState23(loadPinnedDocuments);
7880
- const [pinnedDocumentAvailability, setPinnedDocumentAvailability] = useState23(() => /* @__PURE__ */ new Map());
7881
- useEffect29(() => {
8156
+ const [pinnedDocuments, setPinnedDocuments] = useState24(loadPinnedDocuments);
8157
+ const [pinnedDocumentAvailability, setPinnedDocumentAvailability] = useState24(() => /* @__PURE__ */ new Map());
8158
+ useEffect30(() => {
7882
8159
  savePinnedDocuments(pinnedDocuments);
7883
8160
  }, [pinnedDocuments]);
7884
- useEffect29(() => {
8161
+ useEffect30(() => {
7885
8162
  const menu = maybeGetDocBlocksHost4()?.menu;
7886
8163
  if (!hostSupports2("pinnedDocumentMirror") || !menu) return;
7887
8164
  menu.setPinnedDocuments(
@@ -7892,7 +8169,7 @@ function DocBlocksShell({
7892
8169
  }))
7893
8170
  );
7894
8171
  }, [pinnedDocuments]);
7895
- const setPinnedAvailability = useCallback18(
8172
+ const setPinnedAvailability = useCallback19(
7896
8173
  (document2, availability) => {
7897
8174
  const key = pinnedDocumentKey(document2);
7898
8175
  setPinnedDocumentAvailability((current) => {
@@ -7911,7 +8188,7 @@ function DocBlocksShell({
7911
8188
  })),
7912
8189
  [pinnedDocumentAvailability, pinnedDocuments]
7913
8190
  );
7914
- useEffect29(() => {
8191
+ useEffect30(() => {
7915
8192
  if (!provider || !activeWorkspaceId) return;
7916
8193
  const activePins = pinnedDocuments.filter(
7917
8194
  (document2) => document2.workspaceId === activeWorkspaceId
@@ -7947,7 +8224,7 @@ function DocBlocksShell({
7947
8224
  void scan();
7948
8225
  const scanOnFocus = () => void scan();
7949
8226
  window.addEventListener("focus", scanOnFocus);
7950
- const providerV2 = getFileSystemProviderV210(provider);
8227
+ const providerV2 = getFileSystemProviderV211(provider);
7951
8228
  const subscription = providerV2?.capabilities.watch ? providerV2.watch(
7952
8229
  (event) => {
7953
8230
  if (event.type !== "modified") void scan();
@@ -7961,10 +8238,10 @@ function DocBlocksShell({
7961
8238
  void subscription?.dispose();
7962
8239
  };
7963
8240
  }, [activeWorkspaceId, pinnedDocuments, provider, setPinnedAvailability]);
7964
- const [transientMoveDestinations, setTransientMoveDestinations] = useState23(
8241
+ const [transientMoveDestinations, setTransientMoveDestinations] = useState24(
7965
8242
  []
7966
8243
  );
7967
- useEffect29(() => {
8244
+ useEffect30(() => {
7968
8245
  if (activeWorkspaceDescriptor?.type !== "transient") {
7969
8246
  setTransientMoveDestinations([]);
7970
8247
  return;
@@ -7986,19 +8263,19 @@ function DocBlocksShell({
7986
8263
  };
7987
8264
  }, [activeWorkspaceDescriptor]);
7988
8265
  const nativeWorkspaceId = provider && activeWorkspaceDescriptor?.id === activeWorkspaceId && provider.id === activeWorkspaceId && activeWorkspaceDescriptor.type === "host-native" ? activeWorkspaceDescriptor.id : null;
7989
- const workingTreeChangedRef = useRef19(() => void 0);
7990
- const handleWorkingTreeChanged = useCallback18(() => workingTreeChangedRef.current(), []);
8266
+ const workingTreeChangedRef = useRef20(() => void 0);
8267
+ const handleWorkingTreeChanged = useCallback19(() => workingTreeChangedRef.current(), []);
7991
8268
  const git = useGit(provider, nativeWorkspaceId, resolvedTheme, handleWorkingTreeChanged);
7992
- const gitRef = useRef19(git);
8269
+ const gitRef = useRef20(git);
7993
8270
  gitRef.current = git;
7994
8271
  const { scheduleRefresh: gitScheduleRefresh } = git;
7995
8272
  const workspaceSettingsEnabled = provider !== null && provider.id === activeWorkspaceId && getTransientWorkspace(provider.id) === null;
7996
8273
  const workspaceSettings = useWorkspaceSettings(provider, workspaceSettingsEnabled);
7997
8274
  const { refresh: refreshWorkspaceSettings, save: saveWorkspaceSettings } = workspaceSettings;
7998
8275
  const workspaceDefaultThemeId = resolveWorkspaceDefaultThemeId(workspaceSettings.settings);
7999
- const workspaceDefaultThemeRef = useRef19(workspaceDefaultThemeId);
8276
+ const workspaceDefaultThemeRef = useRef20(workspaceDefaultThemeId);
8000
8277
  workspaceDefaultThemeRef.current = workspaceDefaultThemeId;
8001
- const outsideInRenderRef = useRef19({
8278
+ const outsideInRenderRef = useRef20({
8002
8279
  defaultThemeId: () => workspaceDefaultThemeRef.current
8003
8280
  });
8004
8281
  const workspaceOutputs = useWorkspaceOutputs(
@@ -8007,12 +8284,12 @@ function DocBlocksShell({
8007
8284
  workspaceSettings.settings,
8008
8285
  workspaceSettingsEnabled
8009
8286
  );
8010
- const workspaceOutputsSchedulerRef = useRef19(workspaceOutputs.scheduler);
8287
+ const workspaceOutputsSchedulerRef = useRef20(workspaceOutputs.scheduler);
8011
8288
  workspaceOutputsSchedulerRef.current = workspaceOutputs.scheduler;
8012
8289
  const workspaceIndex = useWorkspaceIndex(provider, activeWorkspaceId, workspaceIndexMode);
8013
- const workspaceIndexRef = useRef19(workspaceIndex.index);
8290
+ const workspaceIndexRef = useRef20(workspaceIndex.index);
8014
8291
  workspaceIndexRef.current = workspaceIndex.index;
8015
- const notifyWorkspaceChanged = useCallback18(
8292
+ const notifyWorkspaceChanged = useCallback19(
8016
8293
  (workspaceId, path) => {
8017
8294
  const scheduler = workspaceOutputsSchedulerRef.current;
8018
8295
  if (scheduler && workspaceId && scheduler.workspaceId === workspaceId) scheduler.notify(path);
@@ -8029,9 +8306,9 @@ function DocBlocksShell({
8029
8306
  workspaceOutputsSchedulerRef.current?.notify();
8030
8307
  workspaceIndexRef.current?.refresh({ force: true });
8031
8308
  };
8032
- const [workspaceSettingsOpen, setWorkspaceSettingsOpen] = useState23(false);
8033
- const [versioningPreference, setVersioningPreference] = useState23(loadVersioningPreference);
8034
- const handleVersioningPreferenceChange = useCallback18((pref) => {
8309
+ const [workspaceSettingsOpen, setWorkspaceSettingsOpen] = useState24(false);
8310
+ const [versioningPreference, setVersioningPreference] = useState24(loadVersioningPreference);
8311
+ const handleVersioningPreferenceChange = useCallback19((pref) => {
8035
8312
  setVersioningPreference(pref);
8036
8313
  saveVersioningPreference(pref);
8037
8314
  }, []);
@@ -8045,39 +8322,39 @@ function DocBlocksShell({
8045
8322
  () => workspaceVersionKeep !== void 0 ? { type: "keep-last-n", n: workspaceVersionKeep } : versioningPrunePolicy,
8046
8323
  [versioningPrunePolicy, workspaceVersionKeep]
8047
8324
  );
8048
- const handleOpenWorkspaceSettings = useCallback18(() => {
8325
+ const handleOpenWorkspaceSettings = useCallback19(() => {
8049
8326
  if (!activeWorkspaceDescriptor) return;
8050
8327
  setWorkspaceSettingsOpen(true);
8051
8328
  }, [activeWorkspaceDescriptor]);
8052
- const handleSaveWorkspaceSettings = useCallback18(
8329
+ const handleSaveWorkspaceSettings = useCallback19(
8053
8330
  async (patch) => {
8054
8331
  const descriptor = activeWorkspaceDescriptor;
8055
8332
  if (!isEmptyWorkspaceSettingsPatch2(patch)) await saveWorkspaceSettings(patch);
8056
8333
  if (descriptor && descriptor.versioningOverride !== void 0) {
8057
8334
  const moved = { ...descriptor };
8058
8335
  delete moved.versioningOverride;
8059
- await saveWorkspace2(moved);
8336
+ await saveWorkspace3(moved);
8060
8337
  setDescriptorRefreshKey((k) => k + 1);
8061
8338
  }
8062
8339
  },
8063
8340
  [activeWorkspaceDescriptor, saveWorkspaceSettings]
8064
8341
  );
8065
- const handleRegenerateWorkspaceOutputs = useCallback18(() => {
8342
+ const handleRegenerateWorkspaceOutputs = useCallback19(() => {
8066
8343
  void workspaceOutputsSchedulerRef.current?.regenerate({ force: true });
8067
8344
  }, []);
8068
- const [selectedFile, setSelectedFile] = useState23(null);
8069
- const [selectedSourceFile, setSelectedSourceFile] = useState23(null);
8070
- const [selectedOutsideIn, setSelectedOutsideIn] = useState23(null);
8071
- const [selectedOutsideInEditingEnabled, setSelectedOutsideInEditingEnabled] = useState23(false);
8072
- const [selectedImage, setSelectedImage] = useState23(void 0);
8073
- const [selectedManagedOutput, setSelectedManagedOutput] = useState23(void 0);
8074
- const [findMode, setFindMode] = useState23(false);
8075
- const findModeRef = useRef19(findMode);
8345
+ const [selectedFile, setSelectedFile] = useState24(null);
8346
+ const [selectedSourceFile, setSelectedSourceFile] = useState24(null);
8347
+ const [selectedOutsideIn, setSelectedOutsideIn] = useState24(null);
8348
+ const [selectedOutsideInEditingEnabled, setSelectedOutsideInEditingEnabled] = useState24(false);
8349
+ const [selectedImage, setSelectedImage] = useState24(void 0);
8350
+ const [selectedManagedOutput, setSelectedManagedOutput] = useState24(void 0);
8351
+ const [findMode, setFindMode] = useState24(false);
8352
+ const findModeRef = useRef20(findMode);
8076
8353
  findModeRef.current = findMode;
8077
- useEffect29(() => {
8354
+ useEffect30(() => {
8078
8355
  setFindMode(false);
8079
8356
  }, [selectedFile]);
8080
- const openFind = useCallback18(() => {
8357
+ const openFind = useCallback19(() => {
8081
8358
  if (!findModeRef.current) {
8082
8359
  setFindMode(true);
8083
8360
  return;
@@ -8086,14 +8363,14 @@ function DocBlocksShell({
8086
8363
  }, []);
8087
8364
  const findShortcut = useMemo6(findShortcutLabel, []);
8088
8365
  const searchShortcut = useMemo6(() => shortcutLabel("workspace-search", isApplePlatform()), []);
8089
- const [quickOpenOpen, setQuickOpenOpen] = useState23(false);
8090
- const [shortcutsOpen, setShortcutsOpen] = useState23(false);
8091
- const openKeyboardShortcuts = useCallback18(() => setShortcutsOpen(true), []);
8092
- const [quickOpenFocusRequest, setQuickOpenFocusRequest] = useState23(0);
8093
- const [sidebarView, setSidebarView] = useState23("files");
8094
- const [searchMounted, setSearchMounted] = useState23(false);
8095
- const [searchFocusRequest, setSearchFocusRequest] = useState23(0);
8096
- useEffect29(() => {
8366
+ const [quickOpenOpen, setQuickOpenOpen] = useState24(false);
8367
+ const [shortcutsOpen, setShortcutsOpen] = useState24(false);
8368
+ const openKeyboardShortcuts = useCallback19(() => setShortcutsOpen(true), []);
8369
+ const [quickOpenFocusRequest, setQuickOpenFocusRequest] = useState24(0);
8370
+ const [sidebarView, setSidebarView] = useState24("files");
8371
+ const [searchMounted, setSearchMounted] = useState24(false);
8372
+ const [searchFocusRequest, setSearchFocusRequest] = useState24(0);
8373
+ useEffect30(() => {
8097
8374
  if (!activeWorkspaceId || !selectedFile) return;
8098
8375
  if (getTransientWorkspace(activeWorkspaceId) === null) {
8099
8376
  recordRecentDocument(activeWorkspaceId, selectedFile);
@@ -8104,7 +8381,7 @@ function DocBlocksShell({
8104
8381
  const layoutMode2 = defaultOutsideInLayoutMode(selectedOutsideIn);
8105
8382
  return layoutMode2 && viewPreferences.layoutMode === void 0 ? { ...viewPreferences, layoutMode: layoutMode2 } : viewPreferences;
8106
8383
  }, [selectedOutsideIn, viewPreferences]);
8107
- const adoptSelectedDocument = useCallback18((document2) => {
8384
+ const adoptSelectedDocument = useCallback19((document2) => {
8108
8385
  setSelectedFile(document2?.displayPath ?? null);
8109
8386
  setSelectedSourceFile(document2?.sourcePath ?? null);
8110
8387
  setSelectedOutsideIn(document2?.outsideIn ?? null);
@@ -8112,7 +8389,7 @@ function DocBlocksShell({
8112
8389
  setSelectedImage(document2?.image);
8113
8390
  setSelectedManagedOutput(document2?.managedOutput);
8114
8391
  }, []);
8115
- const adoptRegularDocument = useCallback18((path) => {
8392
+ const adoptRegularDocument = useCallback19((path) => {
8116
8393
  if (path === null) {
8117
8394
  setSelectedFile(null);
8118
8395
  setSelectedSourceFile(null);
@@ -8135,7 +8412,7 @@ function DocBlocksShell({
8135
8412
  }
8136
8413
  return URL.createObjectURL(new Blob([selectedImage.data], { type: selectedImage.mimeType }));
8137
8414
  }, [selectedImage]);
8138
- useEffect29(
8415
+ useEffect30(
8139
8416
  () => () => {
8140
8417
  if (selectedImageUrl) URL.revokeObjectURL(selectedImageUrl);
8141
8418
  },
@@ -8153,7 +8430,13 @@ function DocBlocksShell({
8153
8430
  return {
8154
8431
  resolveTarget: (filename) => resolveTarget(documentId, filename),
8155
8432
  pickTarget: (filename, currentTarget) => pickTarget(documentId, filename, currentTarget?.grantId ?? null),
8156
- saveBlob: (blob, filename, target) => saveBlobToHost(exports, documentId, blob, filename, target?.grantId ?? null)
8433
+ saveBlob: (blob, filename, target) => saveBlobToHost(exports, documentId, blob, filename, target?.grantId ?? null),
8434
+ ...hostSupports2("openSavedExports") && exports.openSaved ? {
8435
+ openSaved: async (target) => {
8436
+ if (!target.grantId) throw new Error("The saved export has no native target.");
8437
+ await exports.openSaved(documentId, target.grantId);
8438
+ }
8439
+ } : {}
8157
8440
  };
8158
8441
  }
8159
8442
  }
@@ -8175,8 +8458,8 @@ function DocBlocksShell({
8175
8458
  () => hostSupports2("exportDestinations") && exportDestinationAdapter ? createImageSaveOutput(exportDestinationAdapter) : void 0,
8176
8459
  [exportDestinationAdapter]
8177
8460
  );
8178
- const [selectedFolder, setSelectedFolder] = useState23(null);
8179
- const [folderEntries, setFolderEntries] = useState23([]);
8461
+ const [selectedFolder, setSelectedFolder] = useState24(null);
8462
+ const [folderEntries, setFolderEntries] = useState24([]);
8180
8463
  const visibleFolderEntries = useMemo6(
8181
8464
  () => filterVisibleFileEntries(folderEntries),
8182
8465
  [folderEntries]
@@ -8190,8 +8473,8 @@ function DocBlocksShell({
8190
8473
  } : null,
8191
8474
  [documentSnapshot.frozen, documentSnapshot.generation, documentSnapshot.targetKey]
8192
8475
  );
8193
- const [editorGeneration, setEditorGeneration] = useState23(documentSnapshot.generation);
8194
- useEffect29(() => {
8476
+ const [editorGeneration, setEditorGeneration] = useState24(documentSnapshot.generation);
8477
+ useEffect30(() => {
8195
8478
  const sync = () => setEditorGeneration(documentSession.getSnapshot().generation);
8196
8479
  const unsubscribe = documentSession.subscribe(sync);
8197
8480
  sync();
@@ -8200,7 +8483,7 @@ function DocBlocksShell({
8200
8483
  };
8201
8484
  }, [documentSession]);
8202
8485
  const editorGenerationSettled = editorGeneration === documentSnapshot.generation;
8203
- useEffect29(() => {
8486
+ useEffect30(() => {
8204
8487
  setOutsideInPdfWorker(pdfWorkerSrc2);
8205
8488
  }, [pdfWorkerSrc2]);
8206
8489
  const editorKey = String(editorGeneration);
@@ -8208,43 +8491,43 @@ function DocBlocksShell({
8208
8491
  void editorKey;
8209
8492
  return pickEmptyDocumentPrompt();
8210
8493
  }, [editorKey]);
8211
- const [explorerKey, setExplorerKey] = useState23(0);
8494
+ const [explorerKey, setExplorerKey] = useState24(0);
8212
8495
  const workspaceOutputsResult = workspaceOutputs.status.result;
8213
- useEffect29(() => {
8496
+ useEffect30(() => {
8214
8497
  if (!workspaceOutputsResult?.outputs.some((output) => output.created)) return;
8215
- if (provider && getFileSystemProviderV210(provider)?.capabilities.watch) return;
8498
+ if (provider && getFileSystemProviderV211(provider)?.capabilities.watch) return;
8216
8499
  setExplorerKey((key) => key + 1);
8217
8500
  }, [provider, workspaceOutputsResult]);
8218
- const [fileExplorerSortMode, setFileExplorerSortMode] = useState23(loadFileExplorerSortMode);
8219
- const handleFileExplorerSortModeChange = useCallback18((mode) => {
8501
+ const [fileExplorerSortMode, setFileExplorerSortMode] = useState24(loadFileExplorerSortMode);
8502
+ const handleFileExplorerSortModeChange = useCallback19((mode) => {
8220
8503
  setFileExplorerSortMode(mode);
8221
8504
  saveFileExplorerSortMode(mode);
8222
8505
  }, []);
8223
- const [initialView, setInitialView] = useState23("wysiwyg");
8224
- const [initialSharedMode, setInitialSharedMode] = useState23(null);
8225
- const [showWelcomeGateway, setShowWelcomeGateway] = useState23(false);
8506
+ const [initialView, setInitialView] = useState24("wysiwyg");
8507
+ const [initialSharedMode, setInitialSharedMode] = useState24(null);
8508
+ const [showWelcomeGateway, setShowWelcomeGateway] = useState24(false);
8226
8509
  const welcomeScreenShown = showWelcomeGateway && !singlePane;
8227
- const welcomeCoveredRef = useRef19(null);
8510
+ const welcomeCoveredRef = useRef20(null);
8228
8511
  useInert(welcomeCoveredRef, welcomeScreenShown);
8229
- const closeWelcomeGateway = useCallback18(() => {
8512
+ const closeWelcomeGateway = useCallback19(() => {
8230
8513
  setShowWelcomeGateway((showing) => {
8231
8514
  if (showing) markWelcomeGatewayDismissed();
8232
8515
  return false;
8233
8516
  });
8234
8517
  }, []);
8235
- const [aiPanel, setAiPanel] = useState23(null);
8236
- const navigationRequestRef = useRef19(0);
8518
+ const [aiPanel, setAiPanel] = useState24(null);
8519
+ const navigationRequestRef = useRef20(0);
8237
8520
  const workspaceAuthorityBarrier = useMemo6(() => new WorkspaceAuthorityBarrier(), []);
8238
- const preparedCloseRequestRef = useRef19(null);
8239
- const pendingDbkConflictsRef = useRef19(/* @__PURE__ */ new Map());
8240
- const afterWorkspaceCommit = useCallback18(
8521
+ const preparedCloseRequestRef = useRef20(null);
8522
+ const pendingDbkConflictsRef = useRef20(/* @__PURE__ */ new Map());
8523
+ const afterWorkspaceCommit = useCallback19(
8241
8524
  (workspaceId, path) => {
8242
8525
  gitScheduleRefresh();
8243
8526
  notifyWorkspaceChanged(workspaceId, path);
8244
8527
  },
8245
8528
  [gitScheduleRefresh, notifyWorkspaceChanged]
8246
8529
  );
8247
- const createDocumentTarget = useCallback18(
8530
+ const createDocumentTarget = useCallback19(
8248
8531
  (fsProvider, workspaceId, filePath, outsideIn = null) => {
8249
8532
  if (outsideIn) {
8250
8533
  return createOutsideInDocumentTarget(
@@ -8420,20 +8703,20 @@ function DocBlocksShell({
8420
8703
  },
8421
8704
  [afterWorkspaceCommit, gitScheduleRefresh]
8422
8705
  );
8423
- const mediaContainerRef = useRef19(null);
8424
- const [mediaProvider, setMediaProvider] = useState23(null);
8425
- const [mediaProviderScope, setMediaProviderScope] = useState23(null);
8706
+ const mediaContainerRef = useRef20(null);
8707
+ const [mediaProvider, setMediaProvider] = useState24(null);
8708
+ const [mediaProviderScope, setMediaProviderScope] = useState24(null);
8426
8709
  const mediaProviderCurrent = mediaProviderScope !== null && mediaProviderScope.provider === provider && mediaProviderScope.file === selectedFile && mediaProviderScope.outsideIn === selectedOutsideIn;
8427
- const [mediaEpoch, setMediaEpoch] = useState23(0);
8428
- const versionsContainerRef = useRef19(null);
8429
- const [versionsContainer, setVersionsContainer] = useState23(null);
8430
- const pushHash = useCallback18((wsId, filePath) => {
8710
+ const [mediaEpoch, setMediaEpoch] = useState24(0);
8711
+ const versionsContainerRef = useRef20(null);
8712
+ const [versionsContainer, setVersionsContainer] = useState24(null);
8713
+ const pushHash = useCallback19((wsId, filePath) => {
8431
8714
  const hash = buildHash(wsId, filePath);
8432
8715
  if (window.location.hash !== hash) {
8433
8716
  window.history.pushState(null, "", hash);
8434
8717
  }
8435
8718
  }, []);
8436
- const openFromIds = useCallback18(
8719
+ const openFromIds = useCallback19(
8437
8720
  async (wsId, filePath, push, view, navigationRequestId, sharedMode = null) => {
8438
8721
  const requestId = navigationRequestId ?? ++navigationRequestRef.current;
8439
8722
  if (requestId !== navigationRequestRef.current) return null;
@@ -8515,12 +8798,12 @@ function DocBlocksShell({
8515
8798
  }
8516
8799
  return fsProvider;
8517
8800
  } finally {
8518
- if (!transient && !adopted) await getFileSystemProviderV210(fsProvider)?.dispose();
8801
+ if (!transient && !adopted) await getFileSystemProviderV211(fsProvider)?.dispose();
8519
8802
  }
8520
8803
  },
8521
8804
  [adoptSelectedDocument, createDocumentTarget, documentSession, pushHash]
8522
8805
  );
8523
- const adoptTransientWorkspace = useCallback18(
8806
+ const adoptTransientWorkspace = useCallback19(
8524
8807
  async (options) => {
8525
8808
  const descriptor = {
8526
8809
  id: options.id,
@@ -8550,7 +8833,7 @@ function DocBlocksShell({
8550
8833
  },
8551
8834
  [openFromIds, closeWelcomeGateway]
8552
8835
  );
8553
- const openSharedDocument = useCallback18(
8836
+ const openSharedDocument = useCallback19(
8554
8837
  async (payload, navigationRequestId) => {
8555
8838
  const id = `transient-shared-${navigationRequestId}`;
8556
8839
  const mem = await createMemoryFileSystemProvider(id, "Shared document");
@@ -8567,7 +8850,7 @@ function DocBlocksShell({
8567
8850
  )
8568
8851
  });
8569
8852
  } catch (error) {
8570
- await getFileSystemProviderV210(mem)?.dispose();
8853
+ await getFileSystemProviderV211(mem)?.dispose();
8571
8854
  throw error;
8572
8855
  }
8573
8856
  return adoptTransientWorkspace({
@@ -8583,7 +8866,7 @@ function DocBlocksShell({
8583
8866
  },
8584
8867
  [adoptTransientWorkspace]
8585
8868
  );
8586
- const seedWelcomeFile = useCallback18(
8869
+ const seedWelcomeFile = useCallback19(
8587
8870
  async (fs, seedWhenEmpty, navigationRequestId) => {
8588
8871
  const isCurrent = () => navigationRequestId === void 0 || navigationRequestId === navigationRequestRef.current;
8589
8872
  if (!isCurrent()) return;
@@ -8608,12 +8891,15 @@ function DocBlocksShell({
8608
8891
  }
8609
8892
  if (!seedWhenEmpty || entries.length > 0) return;
8610
8893
  const welcomePath = WELCOME_DOCUMENT_PATH;
8611
- const welcomeContent = WELCOME_DOCUMENT_CONTENT;
8894
+ const welcomeContent = welcomeDocumentContent({
8895
+ browserStorage: showBrowserStorageWarning,
8896
+ desktopApp
8897
+ });
8612
8898
  let seededContent = welcomeContent;
8613
8899
  try {
8614
8900
  await writeProviderText(fs, welcomePath, welcomeContent, "create");
8615
8901
  } catch (error) {
8616
- if (!(error instanceof FsError5 && error.code === "already-exists")) throw error;
8902
+ if (!(error instanceof FsError6 && error.code === "already-exists")) throw error;
8617
8903
  const existing = await readProviderText(fs, welcomePath);
8618
8904
  if (existing === null) throw error;
8619
8905
  seededContent = existing;
@@ -8632,25 +8918,32 @@ function DocBlocksShell({
8632
8918
  saveLastState({ workspaceId: fs.id, filePath: welcomePath, view: "wysiwyg" });
8633
8919
  if (!isWelcomeGatewayDismissed()) setShowWelcomeGateway(true);
8634
8920
  },
8635
- [adoptRegularDocument, createDocumentTarget, documentSession, pushHash]
8921
+ [
8922
+ adoptRegularDocument,
8923
+ createDocumentTarget,
8924
+ desktopApp,
8925
+ documentSession,
8926
+ pushHash,
8927
+ showBrowserStorageWarning
8928
+ ]
8636
8929
  );
8637
- const startupOpenFromIdsRef = useRef19(openFromIds);
8930
+ const startupOpenFromIdsRef = useRef20(openFromIds);
8638
8931
  startupOpenFromIdsRef.current = openFromIds;
8639
- const startupOpenSharedDocumentRef = useRef19(openSharedDocument);
8932
+ const startupOpenSharedDocumentRef = useRef20(openSharedDocument);
8640
8933
  startupOpenSharedDocumentRef.current = openSharedDocument;
8641
- const startupSeedWelcomeFileRef = useRef19(seedWelcomeFile);
8934
+ const startupSeedWelcomeFileRef = useRef20(seedWelcomeFile);
8642
8935
  startupSeedWelcomeFileRef.current = seedWelcomeFile;
8643
- const [focusAfterWelcome, setFocusAfterWelcome] = useState23(false);
8644
- const handleTourWelcome = useCallback18(() => {
8936
+ const [focusAfterWelcome, setFocusAfterWelcome] = useState24(false);
8937
+ const handleTourWelcome = useCallback19(() => {
8645
8938
  closeWelcomeGateway();
8646
8939
  setFocusAfterWelcome(true);
8647
8940
  }, [closeWelcomeGateway]);
8648
- useEffect29(() => {
8941
+ useEffect30(() => {
8649
8942
  if (!focusAfterWelcome || welcomeScreenShown) return;
8650
8943
  setFocusAfterWelcome(false);
8651
8944
  editorAreaRef.current?.querySelector('[contenteditable="true"]')?.focus({ preventScroll: true });
8652
8945
  }, [focusAfterWelcome, welcomeScreenShown]);
8653
- useEffect29(() => {
8946
+ useEffect30(() => {
8654
8947
  const requestId = ++navigationRequestRef.current;
8655
8948
  let cancelled = false;
8656
8949
  const isCurrent = () => !cancelled && requestId === navigationRequestRef.current;
@@ -8669,8 +8962,8 @@ function DocBlocksShell({
8669
8962
  getDocBlocksHost().env.platform,
8670
8963
  (/* @__PURE__ */ new Date()).toISOString()
8671
8964
  );
8672
- for (const id of reconciliation.removeIds) await removeWorkspace(id);
8673
- for (const workspace of reconciliation.upsert) await saveWorkspace2(workspace);
8965
+ for (const id of reconciliation.removeIds) await removeWorkspace2(id);
8966
+ for (const workspace of reconciliation.upsert) await saveWorkspace3(workspace);
8674
8967
  workspaceIdRemap = reconciliation.idRemap;
8675
8968
  for (const workspace of hostWorkspaces) {
8676
8969
  if (workspace.isDefault === true) defaultHostWorkspaceIds.add(workspace.id);
@@ -8763,12 +9056,12 @@ function DocBlocksShell({
8763
9056
  const p = await createHostProviderFromWorkspace(ws);
8764
9057
  if (!p) continue;
8765
9058
  if (!isCurrent()) {
8766
- await getFileSystemProviderV210(p)?.dispose();
9059
+ await getFileSystemProviderV211(p)?.dispose();
8767
9060
  return;
8768
9061
  }
8769
9062
  await touchWorkspace2(ws.id);
8770
9063
  if (!isCurrent()) {
8771
- await getFileSystemProviderV210(p)?.dispose();
9064
+ await getFileSystemProviderV211(p)?.dispose();
8772
9065
  return;
8773
9066
  }
8774
9067
  fsProvider = p;
@@ -8777,12 +9070,12 @@ function DocBlocksShell({
8777
9070
  const restored = await (await loadNativeFileSystem()).restoreNativeFolder(ws.id);
8778
9071
  if (restored) {
8779
9072
  if (!isCurrent()) {
8780
- await getFileSystemProviderV210(restored)?.dispose();
9073
+ await getFileSystemProviderV211(restored)?.dispose();
8781
9074
  return;
8782
9075
  }
8783
9076
  await touchWorkspace2(ws.id);
8784
9077
  if (!isCurrent()) {
8785
- await getFileSystemProviderV210(restored)?.dispose();
9078
+ await getFileSystemProviderV211(restored)?.dispose();
8786
9079
  return;
8787
9080
  }
8788
9081
  fsProvider = restored;
@@ -8791,12 +9084,12 @@ function DocBlocksShell({
8791
9084
  } else {
8792
9085
  const p = await createIndexedDbFileSystemProvider(ws.id, ws.name);
8793
9086
  if (!isCurrent()) {
8794
- await getFileSystemProviderV210(p)?.dispose();
9087
+ await getFileSystemProviderV211(p)?.dispose();
8795
9088
  return;
8796
9089
  }
8797
9090
  await touchWorkspace2(ws.id);
8798
9091
  if (!isCurrent()) {
8799
- await getFileSystemProviderV210(p)?.dispose();
9092
+ await getFileSystemProviderV211(p)?.dispose();
8800
9093
  return;
8801
9094
  }
8802
9095
  fsProvider = p;
@@ -8814,11 +9107,11 @@ function DocBlocksShell({
8814
9107
  rootPath: info.rootPath,
8815
9108
  lastOpened: (/* @__PURE__ */ new Date()).toISOString()
8816
9109
  };
8817
- await saveWorkspace2(descriptor);
9110
+ await saveWorkspace3(descriptor);
8818
9111
  if (!isCurrent()) return;
8819
9112
  const p = await createHostFileSystemProvider(info.id, info.name, info.rootPath);
8820
9113
  if (!isCurrent()) {
8821
- await getFileSystemProviderV210(p)?.dispose();
9114
+ await getFileSystemProviderV211(p)?.dispose();
8822
9115
  return;
8823
9116
  }
8824
9117
  fsProvider = p;
@@ -8827,7 +9120,7 @@ function DocBlocksShell({
8827
9120
  if (!isCurrent()) return;
8828
9121
  const p = await createIndexedDbFileSystemProvider(defaultWs.id, defaultWs.name);
8829
9122
  if (!isCurrent()) {
8830
- await getFileSystemProviderV210(p)?.dispose();
9123
+ await getFileSystemProviderV211(p)?.dispose();
8831
9124
  return;
8832
9125
  }
8833
9126
  fsProvider = p;
@@ -8848,7 +9141,7 @@ function DocBlocksShell({
8848
9141
  setActiveWorkspaceId(fsProvider.id);
8849
9142
  adopted = true;
8850
9143
  } finally {
8851
- if (!adopted) await getFileSystemProviderV210(fsProvider)?.dispose();
9144
+ if (!adopted) await getFileSystemProviderV211(fsProvider)?.dispose();
8852
9145
  }
8853
9146
  })().catch(() => {
8854
9147
  if (isCurrent()) {
@@ -8862,7 +9155,7 @@ function DocBlocksShell({
8862
9155
  if (navigationRequestRef.current === requestId) navigationRequestRef.current += 1;
8863
9156
  };
8864
9157
  }, [pushHash, workspaceAuthorityBarrier]);
8865
- useEffect29(() => {
9158
+ useEffect30(() => {
8866
9159
  const onPopState = () => {
8867
9160
  const requestedHash = window.location.hash;
8868
9161
  const restoreCurrentHash = () => {
@@ -8904,7 +9197,7 @@ function DocBlocksShell({
8904
9197
  window.addEventListener("popstate", onPopState);
8905
9198
  return () => window.removeEventListener("popstate", onPopState);
8906
9199
  }, [activeWorkspaceId, openFromIds, openSharedDocument, selectedFile, showToast]);
8907
- useEffect29(() => {
9200
+ useEffect30(() => {
8908
9201
  if (!showBrowserStorageWarning || typeof navigator === "undefined") return;
8909
9202
  const storage = navigator.storage;
8910
9203
  if (!storage || typeof storage.persisted !== "function") return;
@@ -8917,7 +9210,7 @@ function DocBlocksShell({
8917
9210
  cancelled = true;
8918
9211
  };
8919
9212
  }, [showBrowserStorageWarning]);
8920
- useEffect29(() => {
9213
+ useEffect30(() => {
8921
9214
  if (hasDocBlocksHost() || typeof window === "undefined") return;
8922
9215
  const onBeforeInstallPrompt = (e) => {
8923
9216
  e.preventDefault();
@@ -8940,7 +9233,7 @@ function DocBlocksShell({
8940
9233
  window.removeEventListener("appinstalled", onAppInstalled);
8941
9234
  };
8942
9235
  }, []);
8943
- const handleInstallApp = useCallback18(async () => {
9236
+ const handleInstallApp = useCallback19(async () => {
8944
9237
  const promptEvent = installPromptEvent;
8945
9238
  setInstallPromptEvent(null);
8946
9239
  if (!promptEvent) return;
@@ -8950,7 +9243,7 @@ function DocBlocksShell({
8950
9243
  } catch {
8951
9244
  }
8952
9245
  }, [installPromptEvent]);
8953
- useEffect29(() => {
9246
+ useEffect30(() => {
8954
9247
  const handler = (e) => {
8955
9248
  const target = e.target.closest?.("[data-view]");
8956
9249
  if (target) {
@@ -8966,7 +9259,7 @@ function DocBlocksShell({
8966
9259
  window.addEventListener("click", handler, true);
8967
9260
  return () => window.removeEventListener("click", handler, true);
8968
9261
  }, [activeWorkspaceId, selectedFile, closeWelcomeGateway]);
8969
- const saveActiveDocument = useCallback18(() => {
9262
+ const saveActiveDocument = useCallback19(() => {
8970
9263
  void (async () => {
8971
9264
  try {
8972
9265
  await documentSession.flush("manual");
@@ -8979,30 +9272,30 @@ function DocBlocksShell({
8979
9272
  }
8980
9273
  })();
8981
9274
  }, [documentSession, showToast]);
8982
- useEffect29(() => {
9275
+ useEffect30(() => {
8983
9276
  return () => {
8984
9277
  if (saveToastTimerRef.current) clearTimeout(saveToastTimerRef.current);
8985
9278
  };
8986
9279
  }, []);
8987
- const [offlineReadyToast, setOfflineReadyToast] = useState23(false);
8988
- const offlineReadyAnnouncedRef = useRef19(false);
8989
- useEffect29(() => {
9280
+ const [offlineReadyToast, setOfflineReadyToast] = useState24(false);
9281
+ const offlineReadyAnnouncedRef = useRef20(false);
9282
+ useEffect30(() => {
8990
9283
  if (!offlineReady || offlineReadyAnnouncedRef.current) return;
8991
9284
  offlineReadyAnnouncedRef.current = true;
8992
9285
  setOfflineReadyToast(true);
8993
9286
  const timer = setTimeout(() => setOfflineReadyToast(false), 3e3);
8994
9287
  return () => clearTimeout(timer);
8995
9288
  }, [offlineReady]);
8996
- const [storageFull, setStorageFull] = useState23(false);
8997
- useEffect29(() => {
9289
+ const [storageFull, setStorageFull] = useState24(false);
9290
+ useEffect30(() => {
8998
9291
  if (documentSnapshot.error && isQuotaExceededError(documentSnapshot.error)) {
8999
9292
  setStorageFull(true);
9000
9293
  } else if (documentSnapshot.status === "saved") {
9001
9294
  setStorageFull(false);
9002
9295
  }
9003
9296
  }, [documentSnapshot.error, documentSnapshot.status]);
9004
- const autoPersistRequestedRef = useRef19(false);
9005
- useEffect29(() => {
9297
+ const autoPersistRequestedRef = useRef20(false);
9298
+ useEffect30(() => {
9006
9299
  if (!showBrowserStorageWarning || browserStoragePersistent) return;
9007
9300
  if (autoPersistRequestedRef.current) return;
9008
9301
  if (documentSnapshot.status !== "saved") return;
@@ -9050,8 +9343,8 @@ function DocBlocksShell({
9050
9343
  mp.dispose();
9051
9344
  };
9052
9345
  }, [provider, selectedFile, selectedOutsideIn, mediaEpoch]);
9053
- const [mediaEditRenders, setMediaEditRenders] = useState23(null);
9054
- useEffect29(() => {
9346
+ const [mediaEditRenders, setMediaEditRenders] = useState24(null);
9347
+ useEffect30(() => {
9055
9348
  setMediaEditRenders(null);
9056
9349
  if (!mediaProvider) return;
9057
9350
  let manager = null;
@@ -9075,7 +9368,7 @@ function DocBlocksShell({
9075
9368
  selectedSourceFile ?? selectedFile,
9076
9369
  workspaceIndex.ensure
9077
9370
  );
9078
- useEffect29(() => {
9371
+ useEffect30(() => {
9079
9372
  const ref = versioningRef;
9080
9373
  if (!ref) return;
9081
9374
  const assign = (mgr2) => {
@@ -9099,9 +9392,9 @@ function DocBlocksShell({
9099
9392
  (output) => output.status === "written" && sameProviderPath(output.path, selectedFile)
9100
9393
  ) ? workspaceOutputsResult : null;
9101
9394
  }, [selectedFile, selectedManagedOutput, workspaceOutputsResult]);
9102
- useEffect29(() => {
9395
+ useEffect30(() => {
9103
9396
  if (!provider) return;
9104
- const providerV2 = getFileSystemProviderV210(provider);
9397
+ const providerV2 = getFileSystemProviderV211(provider);
9105
9398
  const lifecycle = maybeGetDocBlocksHost4()?.lifecycle;
9106
9399
  if (!providerV2 || !providerV2.capabilities.watch && !lifecycle?.onResume && !managedOutputRefreshToken)
9107
9400
  return;
@@ -9180,7 +9473,7 @@ function DocBlocksShell({
9180
9473
  documentSnapshot.targetKey,
9181
9474
  showToast
9182
9475
  ]);
9183
- const transitionAwayFromDocument = useCallback18(
9476
+ const transitionAwayFromDocument = useCallback19(
9184
9477
  async (requestId) => {
9185
9478
  if (requestId !== navigationRequestRef.current) return false;
9186
9479
  try {
@@ -9199,15 +9492,15 @@ function DocBlocksShell({
9199
9492
  },
9200
9493
  [documentSession, showToast]
9201
9494
  );
9202
- const [showConflictComparison, setShowConflictComparison] = useState23(false);
9203
- const readConflictFile = useCallback18(async () => {
9495
+ const [showConflictComparison, setShowConflictComparison] = useState24(false);
9496
+ const readConflictFile = useCallback19(async () => {
9204
9497
  const path = selectedSourceFile ?? selectedFile;
9205
9498
  if (!provider || !path || activeWorkspaceId && getTransientWorkspace(activeWorkspaceId)) {
9206
9499
  return null;
9207
9500
  }
9208
9501
  return readStableFileSnapshot(provider, path);
9209
9502
  }, [provider, selectedSourceFile, selectedFile, activeWorkspaceId]);
9210
- const handleUseExternalDocument = useCallback18(async () => {
9503
+ const handleUseExternalDocument = useCallback19(async () => {
9211
9504
  const conflictKey = documentSession.getSnapshot().conflict?.targetKey;
9212
9505
  const pendingDbk = conflictKey ? pendingDbkConflictsRef.current.get(conflictKey) : void 0;
9213
9506
  try {
@@ -9231,7 +9524,7 @@ function DocBlocksShell({
9231
9524
  return false;
9232
9525
  }
9233
9526
  }, [activeWorkspaceId, adoptSelectedDocument, documentSession, pushHash]);
9234
- const handleKeepLocalDocument = useCallback18(async () => {
9527
+ const handleKeepLocalDocument = useCallback19(async () => {
9235
9528
  const conflictKey = documentSession.getSnapshot().conflict?.targetKey;
9236
9529
  try {
9237
9530
  await documentSession.resolveConflict("use-local");
@@ -9246,7 +9539,7 @@ function DocBlocksShell({
9246
9539
  return false;
9247
9540
  }
9248
9541
  }, [documentSession]);
9249
- useEffect29(() => {
9542
+ useEffect30(() => {
9250
9543
  const needsWarning = ["dirty", "saving", "error", "conflict"].includes(documentSnapshot.status);
9251
9544
  if (!documentSnapshot.targetKey) return;
9252
9545
  const flushBestEffort = () => {
@@ -9272,7 +9565,7 @@ function DocBlocksShell({
9272
9565
  document.removeEventListener("visibilitychange", onVisibilityChange);
9273
9566
  };
9274
9567
  }, [documentSession, documentSnapshot.status, documentSnapshot.targetKey]);
9275
- useEffect29(() => {
9568
+ useEffect30(() => {
9276
9569
  const lifecycle = maybeGetDocBlocksHost4()?.lifecycle;
9277
9570
  if (!hostSupports2("guardedClose") || !lifecycle) return;
9278
9571
  const stopPrepare = lifecycle.onPrepareClose(async (request) => {
@@ -9298,7 +9591,7 @@ function DocBlocksShell({
9298
9591
  stopCancel?.();
9299
9592
  };
9300
9593
  }, [documentSession]);
9301
- const handleWorkspaceSelect = useCallback18(
9594
+ const handleWorkspaceSelect = useCallback19(
9302
9595
  async (ws) => {
9303
9596
  const requestId = ++navigationRequestRef.current;
9304
9597
  await touchWorkspace2(ws.id);
@@ -9330,7 +9623,7 @@ function DocBlocksShell({
9330
9623
  },
9331
9624
  [adoptSelectedDocument, pushHash, transitionAwayFromDocument]
9332
9625
  );
9333
- const unpinDocument = useCallback18((document2) => {
9626
+ const unpinDocument = useCallback19((document2) => {
9334
9627
  const key = pinnedDocumentKey(document2);
9335
9628
  setPinnedDocuments((current) => removePinnedDocument(current, document2));
9336
9629
  setPinnedDocumentAvailability((current) => {
@@ -9340,7 +9633,7 @@ function DocBlocksShell({
9340
9633
  return next;
9341
9634
  });
9342
9635
  }, []);
9343
- const confirmMissingPinnedDocument = useCallback18(
9636
+ const confirmMissingPinnedDocument = useCallback19(
9344
9637
  async (document2) => {
9345
9638
  setPinnedAvailability(document2, "missing");
9346
9639
  const shouldUnpin = await confirmAction({
@@ -9353,7 +9646,7 @@ function DocBlocksShell({
9353
9646
  },
9354
9647
  [confirmAction, setPinnedAvailability, unpinDocument]
9355
9648
  );
9356
- const handleTogglePin = useCallback18(
9649
+ const handleTogglePin = useCallback19(
9357
9650
  (path) => {
9358
9651
  if (!activeWorkspaceId || !provider) return;
9359
9652
  const canonicalPath2 = parseWorkspacePath8(path);
@@ -9391,7 +9684,7 @@ function DocBlocksShell({
9391
9684
  () => pinnedDocuments.filter((document2) => document2.workspaceId === activeWorkspaceId).map((document2) => document2.path),
9392
9685
  [activeWorkspaceId, pinnedDocuments]
9393
9686
  );
9394
- const handlePinnedDocumentSelect = useCallback18(
9687
+ const handlePinnedDocumentSelect = useCallback19(
9395
9688
  async (document2) => {
9396
9689
  const requestId = ++navigationRequestRef.current;
9397
9690
  const workspace = await getWorkspace(document2.workspaceId);
@@ -9499,7 +9792,7 @@ function DocBlocksShell({
9499
9792
  } finally {
9500
9793
  if (nextProvider && ownsNextProvider && !adoptedNextProvider) {
9501
9794
  try {
9502
- await getFileSystemProviderV210(nextProvider)?.dispose();
9795
+ await getFileSystemProviderV211(nextProvider)?.dispose();
9503
9796
  } catch {
9504
9797
  }
9505
9798
  }
@@ -9519,7 +9812,7 @@ function DocBlocksShell({
9519
9812
  showToast
9520
9813
  ]
9521
9814
  );
9522
- const handleMoveTransientWorkspace = useCallback18(
9815
+ const handleMoveTransientWorkspace = useCallback19(
9523
9816
  async (destinationWorkspaceId) => {
9524
9817
  if (!activeWorkspaceId || !provider || !selectedFile) {
9525
9818
  throw new Error("Select the temporary document before moving it.");
@@ -9605,14 +9898,14 @@ function DocBlocksShell({
9605
9898
  }
9606
9899
  }
9607
9900
  try {
9608
- await removeWorkspace(activeWorkspaceId);
9901
+ await removeWorkspace2(activeWorkspaceId);
9609
9902
  } catch {
9610
9903
  }
9611
9904
  setDescriptorRefreshKey((key) => key + 1);
9612
9905
  } catch (error) {
9613
9906
  if (destinationProvider && !adoptedDestination) {
9614
9907
  try {
9615
- await getFileSystemProviderV210(destinationProvider)?.dispose();
9908
+ await getFileSystemProviderV211(destinationProvider)?.dispose();
9616
9909
  } catch {
9617
9910
  }
9618
9911
  }
@@ -9633,7 +9926,7 @@ function DocBlocksShell({
9633
9926
  transientMoveDestinations
9634
9927
  ]
9635
9928
  );
9636
- const handleOpenFolder = useCallback18(async () => {
9929
+ const handleOpenFolder = useCallback19(async () => {
9637
9930
  const requestId = ++navigationRequestRef.current;
9638
9931
  try {
9639
9932
  if (hostSupports2("workspaceFolderPicker")) {
@@ -9647,7 +9940,7 @@ function DocBlocksShell({
9647
9940
  rootPath: info.rootPath,
9648
9941
  lastOpened: (/* @__PURE__ */ new Date()).toISOString()
9649
9942
  };
9650
- await saveWorkspace2(descriptor2);
9943
+ await saveWorkspace3(descriptor2);
9651
9944
  if (requestId !== navigationRequestRef.current) return;
9652
9945
  const provider2 = await createHostFileSystemProvider(info.id, info.name, info.rootPath);
9653
9946
  if (!await transitionAwayFromDocument(requestId)) return;
@@ -9667,7 +9960,7 @@ function DocBlocksShell({
9667
9960
  type: "native",
9668
9961
  lastOpened: (/* @__PURE__ */ new Date()).toISOString()
9669
9962
  };
9670
- await saveWorkspace2(descriptor);
9963
+ await saveWorkspace3(descriptor);
9671
9964
  if (requestId !== navigationRequestRef.current) return;
9672
9965
  if (!await transitionAwayFromDocument(requestId)) return;
9673
9966
  setProvider(nativeProvider);
@@ -9679,7 +9972,7 @@ function DocBlocksShell({
9679
9972
  } catch {
9680
9973
  }
9681
9974
  }, [adoptSelectedDocument, pushHash, transitionAwayFromDocument]);
9682
- const handleWorkspaceCloned = useCallback18(
9975
+ const handleWorkspaceCloned = useCallback19(
9683
9976
  (info) => {
9684
9977
  const requestId = ++navigationRequestRef.current;
9685
9978
  void (async () => {
@@ -9690,7 +9983,7 @@ function DocBlocksShell({
9690
9983
  rootPath: info.rootPath,
9691
9984
  lastOpened: (/* @__PURE__ */ new Date()).toISOString()
9692
9985
  };
9693
- await saveWorkspace2(descriptor);
9986
+ await saveWorkspace3(descriptor);
9694
9987
  if (requestId !== navigationRequestRef.current) return;
9695
9988
  const cloneProvider = await createHostFileSystemProvider(info.id, info.name, info.rootPath);
9696
9989
  if (!await transitionAwayFromDocument(requestId)) return;
@@ -9704,10 +9997,10 @@ function DocBlocksShell({
9704
9997
  },
9705
9998
  [adoptSelectedDocument, pushHash, transitionAwayFromDocument]
9706
9999
  );
9707
- const handleNewFile = useCallback18(() => {
10000
+ const handleNewFile = useCallback19(() => {
9708
10001
  if (provider) setNewDocumentDialogOpen(true);
9709
10002
  }, [provider]);
9710
- const createNewDocument = useCallback18(
10003
+ const createNewDocument = useCallback19(
9711
10004
  async ({ filename, format }) => {
9712
10005
  if (!provider) return;
9713
10006
  const path = "/" + filename;
@@ -9718,12 +10011,12 @@ function DocBlocksShell({
9718
10011
  if (requestId !== navigationRequestRef.current) return null;
9719
10012
  if (format === "markdown") {
9720
10013
  const content = "# " + filename.replace(/\.[^.]+$/, "") + "\n";
9721
- const providerV2 = getFileSystemProviderV210(provider);
10014
+ const providerV2 = getFileSystemProviderV211(provider);
9722
10015
  if (providerV2) {
9723
10016
  try {
9724
10017
  await writeProviderText(provider, path, content, "create");
9725
10018
  } catch (error) {
9726
- if (error instanceof FsError5 && error.code === "already-exists") {
10019
+ if (error instanceof FsError6 && error.code === "already-exists") {
9727
10020
  throw new Error("A document with that name already exists.");
9728
10021
  }
9729
10022
  throw error;
@@ -9806,7 +10099,7 @@ function DocBlocksShell({
9806
10099
  showToast
9807
10100
  ]
9808
10101
  );
9809
- const handleNewFolder = useCallback18(async () => {
10102
+ const handleNewFolder = useCallback19(async () => {
9810
10103
  if (!provider) return;
9811
10104
  const name = await promptForText({
9812
10105
  title: "New folder",
@@ -9822,7 +10115,7 @@ function DocBlocksShell({
9822
10115
  }
9823
10116
  const path = `/${folderName}`;
9824
10117
  try {
9825
- const providerV2 = getFileSystemProviderV210(provider);
10118
+ const providerV2 = getFileSystemProviderV211(provider);
9826
10119
  if (providerV2) {
9827
10120
  await providerV2.createDirectory(parseWorkspacePath8(path), { mode: "create" });
9828
10121
  } else {
@@ -9832,7 +10125,7 @@ function DocBlocksShell({
9832
10125
  await provider.createDirectory(path);
9833
10126
  }
9834
10127
  } catch (error) {
9835
- const message = error instanceof FsError5 && error.code === "already-exists" ? "A file or folder with that name already exists." : error instanceof Error ? error.message : "Could not create the folder.";
10128
+ const message = error instanceof FsError6 && error.code === "already-exists" ? "A file or folder with that name already exists." : error instanceof Error ? error.message : "Could not create the folder.";
9836
10129
  showToast("error", message);
9837
10130
  return;
9838
10131
  }
@@ -9854,15 +10147,15 @@ function DocBlocksShell({
9854
10147
  singlePane
9855
10148
  ]);
9856
10149
  const canFind = selectedFile !== null && mediaProvider !== null && selectedManagedOutput !== "catalog-html" && selectedImage === void 0;
9857
- const canFindRef = useRef19(canFind);
10150
+ const canFindRef = useRef20(canFind);
9858
10151
  canFindRef.current = canFind;
9859
10152
  const { ensure: ensureWorkspaceIndex } = workspaceIndex;
9860
- const openQuickOpen = useCallback18(() => {
10153
+ const openQuickOpen = useCallback19(() => {
9861
10154
  ensureWorkspaceIndex();
9862
10155
  setQuickOpenOpen(true);
9863
10156
  setQuickOpenFocusRequest((request) => request + 1);
9864
10157
  }, [ensureWorkspaceIndex]);
9865
- const openWorkspaceSearch = useCallback18(() => {
10158
+ const openWorkspaceSearch = useCallback19(() => {
9866
10159
  ensureWorkspaceIndex();
9867
10160
  if (singlePane) setMobileShowEditor(false);
9868
10161
  else if (sidebarPreference === "collapsed") updateSidebarPreference("pinned");
@@ -9930,7 +10223,7 @@ function DocBlocksShell({
9930
10223
  ]
9931
10224
  );
9932
10225
  useShellShortcuts(shortcutHandlers, layoutMode);
9933
- useEffect29(
10226
+ useEffect30(
9934
10227
  () => maybeGetDocBlocksHost4()?.lifecycle?.onBack?.(() => {
9935
10228
  if (dismissTopmostDialog()) return true;
9936
10229
  const escape = new KeyboardEvent("keydown", {
@@ -9952,8 +10245,8 @@ function DocBlocksShell({
9952
10245
  }),
9953
10246
  [singlePane, mobileShowEditor]
9954
10247
  );
9955
- const actionNewHandledRef = useRef19(false);
9956
- useEffect29(() => {
10248
+ const actionNewHandledRef = useRef20(false);
10249
+ useEffect30(() => {
9957
10250
  if (!provider || actionNewHandledRef.current) return;
9958
10251
  if (typeof window === "undefined") return;
9959
10252
  const params = new URLSearchParams(window.location.search);
@@ -9968,7 +10261,7 @@ function DocBlocksShell({
9968
10261
  );
9969
10262
  void handleNewFile();
9970
10263
  }, [provider, handleNewFile]);
9971
- const handleRevealWorkspace = useCallback18(async () => {
10264
+ const handleRevealWorkspace = useCallback19(async () => {
9972
10265
  const shell = maybeGetDocBlocksHost4()?.shell;
9973
10266
  if (!hostSupports2("revealInFileManager") || !shell?.revealInFolder || !activeWorkspaceId) {
9974
10267
  return;
@@ -9978,7 +10271,7 @@ function DocBlocksShell({
9978
10271
  await shell.revealInFolder(ws.id);
9979
10272
  }
9980
10273
  }, [activeWorkspaceId]);
9981
- const handleOpenWorkspaceFolder = useCallback18(() => {
10274
+ const handleOpenWorkspaceFolder = useCallback19(() => {
9982
10275
  const shell = maybeGetDocBlocksHost4()?.shell;
9983
10276
  if (!hostSupports2("openWorkspaceFolder") || !shell?.openWorkspaceFolder || !nativeWorkspaceId) {
9984
10277
  return;
@@ -9990,7 +10283,7 @@ function DocBlocksShell({
9990
10283
  );
9991
10284
  });
9992
10285
  }, [nativeWorkspaceId, showToast]);
9993
- useEffect29(() => {
10286
+ useEffect30(() => {
9994
10287
  const host = maybeGetDocBlocksHost4();
9995
10288
  if (!hostSupports2("menuCommands") || !host?.onMenuCommand) return;
9996
10289
  return host.onMenuCommand((cmd) => {
@@ -10057,8 +10350,8 @@ function DocBlocksShell({
10057
10350
  }
10058
10351
  });
10059
10352
  }, [handleNewFile, handleOpenFolder, handleRevealWorkspace, acknowledge, openFind]);
10060
- const handleSelect = useCallback18(
10061
- async (path, kind) => {
10353
+ const handleSelect = useCallback19(
10354
+ async (path, kind, createWikiArticle = false) => {
10062
10355
  if (!provider || !activeWorkspaceId) return;
10063
10356
  const requestId = ++navigationRequestRef.current;
10064
10357
  if (kind === "directory") {
@@ -10075,6 +10368,17 @@ function DocBlocksShell({
10075
10368
  try {
10076
10369
  const transitioned = await documentSession.transitionWithLoad(async () => {
10077
10370
  if (requestId !== navigationRequestRef.current) return null;
10371
+ if (createWikiArticle) {
10372
+ if (await ensureShellWikiArticle(
10373
+ provider,
10374
+ parseWorkspacePath8(path),
10375
+ defaultNewFileFormat,
10376
+ outsideInRenderRef.current
10377
+ )) {
10378
+ notifyWorkspaceChanged(activeWorkspaceId, path);
10379
+ }
10380
+ if (requestId !== navigationRequestRef.current) return null;
10381
+ }
10078
10382
  openedDocument = await loadEditableShellDocument(
10079
10383
  provider,
10080
10384
  path,
@@ -10126,10 +10430,73 @@ function DocBlocksShell({
10126
10430
  documentSession,
10127
10431
  notifyWorkspaceChanged,
10128
10432
  transitionAwayFromDocument,
10129
- showToast
10433
+ showToast,
10434
+ defaultNewFileFormat
10130
10435
  ]
10131
10436
  );
10132
- const handleEnableOutsideInEditing = useCallback18(
10437
+ const wikiNavigationRef = useRef20(handleSelect);
10438
+ wikiNavigationRef.current = handleSelect;
10439
+ const wikiLinks = useMemo6(() => {
10440
+ if (!provider || !selectedFile || !activeWorkspaceId) return null;
10441
+ const source = selectedFile;
10442
+ const extension = wikiArticleExtension(defaultNewFileFormat);
10443
+ const lookups = /* @__PURE__ */ new Map();
10444
+ let queue = Promise.resolve();
10445
+ const lookup = (target) => {
10446
+ let result = lookups.get(target);
10447
+ if (!result) {
10448
+ result = queue.then(() => findWikiArticle2(provider, source, target, extension));
10449
+ queue = result.then(
10450
+ () => void 0,
10451
+ () => void 0
10452
+ );
10453
+ lookups.set(target, result);
10454
+ }
10455
+ return result;
10456
+ };
10457
+ return {
10458
+ async suggest(query) {
10459
+ const index = workspaceIndex.index;
10460
+ if (!index) return [];
10461
+ workspaceIndex.ensure();
10462
+ await index.whenListed({ timeoutMs: 5e3 });
10463
+ return suggestWikiArticles(
10464
+ index.getFiles(),
10465
+ index.getDirectories(),
10466
+ source,
10467
+ query,
10468
+ extension
10469
+ );
10470
+ },
10471
+ async exists(target) {
10472
+ return (await lookup(target)).exists;
10473
+ },
10474
+ async resolve(target) {
10475
+ const article = await lookup(target);
10476
+ if (!article.exists) return null;
10477
+ const href = selectedSourceFile !== selectedFile ? `/${article.path}` : article.relativePath;
10478
+ return href.split("/").map(encodeURIComponent).join("/");
10479
+ },
10480
+ async open(target, { create }) {
10481
+ const requestId = navigationRequestRef.current;
10482
+ const navigate = wikiNavigationRef.current;
10483
+ const { path } = await lookup(target);
10484
+ if (requestId !== navigationRequestRef.current || navigate !== wikiNavigationRef.current)
10485
+ return;
10486
+ await navigate(path, "file", create);
10487
+ }
10488
+ };
10489
+ }, [
10490
+ provider,
10491
+ selectedFile,
10492
+ selectedSourceFile,
10493
+ activeWorkspaceId,
10494
+ defaultNewFileFormat,
10495
+ workspaceIndex.index,
10496
+ workspaceIndex.ensure,
10497
+ workspaceIndex.status.generation
10498
+ ]);
10499
+ const handleEnableOutsideInEditing = useCallback19(
10133
10500
  async (path) => {
10134
10501
  if (!provider || !activeWorkspaceId) {
10135
10502
  throw new Error("Open the workspace before enabling Markdown editing.");
@@ -10191,7 +10558,7 @@ function DocBlocksShell({
10191
10558
  return null;
10192
10559
  }
10193
10560
  }, [workspaceSettings.settings]);
10194
- const outsideInActionsForEntry = useCallback18(
10561
+ const outsideInActionsForEntry = useCallback19(
10195
10562
  (entry) => {
10196
10563
  if (entry.kind !== "file" || resolveOutsideInLayout2(entry.path) === null) return [];
10197
10564
  if (catalogHtmlPath !== null && sameProviderPath(entry.path, catalogHtmlPath)) return [];
@@ -10207,7 +10574,7 @@ function DocBlocksShell({
10207
10574
  },
10208
10575
  [catalogHtmlPath, handleEnableOutsideInEditing, selectedFile, selectedOutsideInEditingEnabled]
10209
10576
  );
10210
- const actionsForEntry = useCallback18(
10577
+ const actionsForEntry = useCallback19(
10211
10578
  (entry) => {
10212
10579
  const nativeActions = createNativeFileActions(
10213
10580
  entry,
@@ -10230,7 +10597,7 @@ function DocBlocksShell({
10230
10597
  () => quickOpenOpen && activeWorkspaceId ? loadRecentDocuments(activeWorkspaceId) : [],
10231
10598
  [quickOpenOpen, activeWorkspaceId]
10232
10599
  );
10233
- const handleQuickOpenChoice = useCallback18(
10600
+ const handleQuickOpenChoice = useCallback19(
10234
10601
  (item) => {
10235
10602
  setQuickOpenOpen(false);
10236
10603
  if (item.workspaceId === activeWorkspaceId) {
@@ -10244,19 +10611,19 @@ function DocBlocksShell({
10244
10611
  },
10245
10612
  [activeWorkspaceId, handleSelect, handlePinnedDocumentSelect, pinnedDocumentItems]
10246
10613
  );
10247
- const handleSearchOpen = useCallback18(
10614
+ const handleSearchOpen = useCallback19(
10248
10615
  (request) => {
10249
10616
  void handleSelect(request.path, "file");
10250
10617
  },
10251
10618
  [handleSelect]
10252
10619
  );
10253
- const handleEditorLinkClick = useCallback18(
10620
+ const handleEditorLinkClick = useCallback19(
10254
10621
  (href) => {
10255
10622
  const target = resolveShellEditorLinkTarget(href, selectedSourceFile ?? selectedFile);
10256
10623
  if (!target) {
10257
10624
  showToast(
10258
10625
  "error",
10259
- "DocBlocks only opens HTTP(S) links or Markdown files inside this workspace."
10626
+ "DocBlocks only opens HTTP(S) links or Markdown and HTML files inside this workspace."
10260
10627
  );
10261
10628
  return true;
10262
10629
  }
@@ -10274,14 +10641,14 @@ function DocBlocksShell({
10274
10641
  if (!provider) return;
10275
10642
  try {
10276
10643
  if (!await providerEntryExists(provider, target.path)) {
10277
- showToast("error", "The linked Markdown file was not found in this workspace.");
10644
+ showToast("error", "The linked document was not found in this workspace.");
10278
10645
  return;
10279
10646
  }
10280
10647
  await handleSelect(target.path, "file");
10281
10648
  } catch (error) {
10282
10649
  showToast(
10283
10650
  "error",
10284
- error instanceof Error ? error.message : "Could not open the linked Markdown file."
10651
+ error instanceof Error ? error.message : "Could not open the linked document."
10285
10652
  );
10286
10653
  }
10287
10654
  })();
@@ -10289,7 +10656,7 @@ function DocBlocksShell({
10289
10656
  },
10290
10657
  [handleSelect, provider, selectedFile, selectedSourceFile, showToast]
10291
10658
  );
10292
- const handleTreeMutation = useCallback18(
10659
+ const handleTreeMutation = useCallback19(
10293
10660
  async (change, mutate) => {
10294
10661
  if (!provider) {
10295
10662
  await mutate();
@@ -10373,7 +10740,7 @@ function DocBlocksShell({
10373
10740
  showToast
10374
10741
  ]
10375
10742
  );
10376
- const handleTreeChange = useCallback18(
10743
+ const handleTreeChange = useCallback19(
10377
10744
  async (change) => {
10378
10745
  if (!provider) return;
10379
10746
  notifyWorkspaceChanged(activeWorkspaceId);
@@ -10444,7 +10811,7 @@ function DocBlocksShell({
10444
10811
  setPinnedAvailability
10445
10812
  ]
10446
10813
  );
10447
- const acquirePinnedDocumentProvider = useCallback18(
10814
+ const acquirePinnedDocumentProvider = useCallback19(
10448
10815
  async (document2) => {
10449
10816
  const workspace = await getWorkspace(document2.workspaceId);
10450
10817
  if (!workspace) return { kind: "missing-workspace" };
@@ -10468,7 +10835,7 @@ function DocBlocksShell({
10468
10835
  },
10469
10836
  [activeWorkspaceId, provider]
10470
10837
  );
10471
- const markRelocatedPinnedDocument = useCallback18((document2, newPath) => {
10838
+ const markRelocatedPinnedDocument = useCallback19((document2, newPath) => {
10472
10839
  const relocated = {
10473
10840
  workspaceId: document2.workspaceId,
10474
10841
  workspaceName: document2.workspaceName,
@@ -10487,7 +10854,7 @@ function DocBlocksShell({
10487
10854
  return next;
10488
10855
  });
10489
10856
  }, []);
10490
- const handlePinnedDocumentRename = useCallback18(
10857
+ const handlePinnedDocumentRename = useCallback19(
10491
10858
  async (document2) => {
10492
10859
  if (document2.availability === "missing") {
10493
10860
  await confirmMissingPinnedDocument(document2);
@@ -10555,7 +10922,7 @@ function DocBlocksShell({
10555
10922
  } finally {
10556
10923
  if (access.owned) {
10557
10924
  try {
10558
- await getFileSystemProviderV210(access.provider)?.dispose();
10925
+ await getFileSystemProviderV211(access.provider)?.dispose();
10559
10926
  } catch {
10560
10927
  }
10561
10928
  }
@@ -10574,7 +10941,7 @@ function DocBlocksShell({
10574
10941
  showToast
10575
10942
  ]
10576
10943
  );
10577
- const handlePinnedDocumentDelete = useCallback18(
10944
+ const handlePinnedDocumentDelete = useCallback19(
10578
10945
  async (document2) => {
10579
10946
  if (document2.availability === "missing") {
10580
10947
  await confirmMissingPinnedDocument(document2);
@@ -10624,7 +10991,7 @@ function DocBlocksShell({
10624
10991
  } finally {
10625
10992
  if (access.owned) {
10626
10993
  try {
10627
- await getFileSystemProviderV210(access.provider)?.dispose();
10994
+ await getFileSystemProviderV211(access.provider)?.dispose();
10628
10995
  } catch {
10629
10996
  }
10630
10997
  }
@@ -10642,39 +11009,80 @@ function DocBlocksShell({
10642
11009
  showToast
10643
11010
  ]
10644
11011
  );
10645
- const handleImportFiles = useCallback18(
11012
+ const restoreInputRef = useRef20(null);
11013
+ const restoreInProgress = useRef20(false);
11014
+ const handleRestoreBackup = useCallback19(
11015
+ async (file) => {
11016
+ if (restoreInProgress.current) {
11017
+ showToast("error", "A backup is already being restored. Please wait.");
11018
+ return;
11019
+ }
11020
+ restoreInProgress.current = true;
11021
+ try {
11022
+ await documentSession.flush("backup");
11023
+ const restored = await restoreBrowserBackup(file);
11024
+ setDescriptorRefreshKey((key) => key + 1);
11025
+ if (restored[0]) await handleWorkspaceSelect(restored[0]);
11026
+ showToast(
11027
+ "success",
11028
+ `Restored ${restored.length} workspace(s) as new copies. Your existing workspaces are unchanged.`
11029
+ );
11030
+ } catch (error) {
11031
+ showToast(
11032
+ "error",
11033
+ `Could not restore ${file.name}: ${error instanceof Error ? error.message : "Unknown error"}`
11034
+ );
11035
+ } finally {
11036
+ restoreInProgress.current = false;
11037
+ }
11038
+ },
11039
+ [documentSession, handleWorkspaceSelect, showToast]
11040
+ );
11041
+ const importOrRestore = useCallback19(
11042
+ async (files) => {
11043
+ const backups = hostSupports2("nativeWorkspaces") ? [] : files.filter((file) => /\.zip$/i.test(file.name));
11044
+ const documents = files.filter((file) => !backups.includes(file));
11045
+ const result = provider ? await importDroppedFiles(documents, provider) : null;
11046
+ for (const file of backups) await handleRestoreBackup(file);
11047
+ return result;
11048
+ },
11049
+ [handleRestoreBackup, provider]
11050
+ );
11051
+ const handleImportFiles = useCallback19(
10646
11052
  async (files) => {
10647
11053
  if (!provider) return;
10648
- const result = await importDroppedFiles(files, provider);
11054
+ const result = await importOrRestore(files);
11055
+ if (!result) return;
10649
11056
  notifyWorkspaceChanged(activeWorkspaceId);
10650
11057
  setExplorerKey((k) => k + 1);
10651
11058
  const summary = summariseImport(result);
10652
11059
  if (summary) showToast(summary.kind, summary.message);
10653
11060
  },
10654
- [activeWorkspaceId, notifyWorkspaceChanged, provider, showToast]
11061
+ [activeWorkspaceId, notifyWorkspaceChanged, provider, showToast, importOrRestore]
10655
11062
  );
10656
- const handleImportFilesToEditor = useCallback18(
11063
+ const handleImportFilesToEditor = useCallback19(
10657
11064
  async (files) => {
10658
11065
  if (!provider) return;
10659
- const result = await importDroppedFiles(files, provider);
11066
+ const result = await importOrRestore(files);
11067
+ if (!result) return;
10660
11068
  notifyWorkspaceChanged(activeWorkspaceId);
10661
11069
  setExplorerKey((k) => k + 1);
10662
11070
  const summary = summariseImport(result);
10663
11071
  if (summary) showToast(summary.kind, summary.message);
10664
11072
  const imported = result.imported;
10665
- if (imported.length === 1 && result.failed.length === 0) {
11073
+ if (imported.length === 1 && result.failed.length === 0 && !files.some((file) => /\.zip$/i.test(file.name))) {
10666
11074
  const only = imported[0];
10667
11075
  if (only) void handleSelect(only.path, "file");
10668
11076
  }
10669
11077
  },
10670
- [activeWorkspaceId, notifyWorkspaceChanged, provider, showToast, handleSelect]
11078
+ [activeWorkspaceId, notifyWorkspaceChanged, provider, showToast, handleSelect, importOrRestore]
10671
11079
  );
10672
11080
  const editorDropActive = useEditorFileDrop(
10673
11081
  editorAreaRef,
10674
11082
  provider !== null,
10675
11083
  (files) => void handleImportFilesToEditor(files)
10676
11084
  );
10677
- const handleEditorChange = useCallback18(
11085
+ const handleEditorChange = useCallback19(
10678
11086
  (source) => {
10679
11087
  if (!editorSessionScope) return;
10680
11088
  try {
@@ -10684,7 +11092,7 @@ function DocBlocksShell({
10684
11092
  },
10685
11093
  [documentSession, editorSessionScope]
10686
11094
  );
10687
- const handleRenameWorkspace = useCallback18(async () => {
11095
+ const handleRenameWorkspace = useCallback19(async () => {
10688
11096
  if (!activeWorkspaceId) return;
10689
11097
  const ws = await getWorkspace(activeWorkspaceId);
10690
11098
  if (!ws) return;
@@ -10695,13 +11103,13 @@ function DocBlocksShell({
10695
11103
  confirmLabel: "Rename"
10696
11104
  });
10697
11105
  if (!newName || newName === ws.name) return;
10698
- await saveWorkspace2({ ...ws, name: newName });
11106
+ await saveWorkspace3({ ...ws, name: newName });
10699
11107
  setPinnedDocuments(
10700
11108
  (current) => renamePinnedDocumentWorkspace(current, activeWorkspaceId, newName)
10701
11109
  );
10702
11110
  setDescriptorRefreshKey((key) => key + 1);
10703
11111
  }, [activeWorkspaceId, promptForText]);
10704
- const openTransient = useCallback18(
11112
+ const openTransient = useCallback19(
10705
11113
  async (req, navigationRequestId) => {
10706
11114
  const isCurrent = () => navigationRequestId === navigationRequestRef.current;
10707
11115
  const host = getDocBlocksHost();
@@ -10721,7 +11129,7 @@ function DocBlocksShell({
10721
11129
  throw error;
10722
11130
  }
10723
11131
  if (!isCurrent()) {
10724
- await getFileSystemProviderV210(mem)?.dispose();
11132
+ await getFileSystemProviderV211(mem)?.dispose();
10725
11133
  await revokeAbandonedResource();
10726
11134
  return;
10727
11135
  }
@@ -10769,7 +11177,7 @@ function DocBlocksShell({
10769
11177
  } finally {
10770
11178
  try {
10771
11179
  if (!adoptionStarted) {
10772
- await getFileSystemProviderV210(mem)?.dispose();
11180
+ await getFileSystemProviderV211(mem)?.dispose();
10773
11181
  }
10774
11182
  } finally {
10775
11183
  if (!opened) await revokeAbandonedResource();
@@ -10778,7 +11186,7 @@ function DocBlocksShell({
10778
11186
  },
10779
11187
  [adoptTransientWorkspace]
10780
11188
  );
10781
- useEffect29(() => {
11189
+ useEffect30(() => {
10782
11190
  const host = maybeGetDocBlocksHost4();
10783
11191
  if (!hostSupports2("openRequests") || !host) return;
10784
11192
  return host.onOpenRequest((req) => {
@@ -10803,7 +11211,7 @@ function DocBlocksShell({
10803
11211
  });
10804
11212
  });
10805
11213
  }, [openFromIds, openTransient, workspaceAuthorityBarrier, showToast, closeWelcomeGateway]);
10806
- const openTransientFromHandle = useCallback18(
11214
+ const openTransientFromHandle = useCallback19(
10807
11215
  async (handle) => {
10808
11216
  const name = handle.name;
10809
11217
  const isBundle = /\.(dbk|zip)$/i.test(name);
@@ -10840,12 +11248,12 @@ function DocBlocksShell({
10840
11248
  push: true
10841
11249
  });
10842
11250
  } finally {
10843
- if (!adoptionStarted) await getFileSystemProviderV210(mem)?.dispose();
11251
+ if (!adoptionStarted) await getFileSystemProviderV211(mem)?.dispose();
10844
11252
  }
10845
11253
  },
10846
11254
  [adoptTransientWorkspace]
10847
11255
  );
10848
- useEffect29(() => {
11256
+ useEffect30(() => {
10849
11257
  if (hostSupports2("openRequests") || typeof window === "undefined") return;
10850
11258
  const launchQueue = window.launchQueue;
10851
11259
  if (!launchQueue) return;
@@ -10861,7 +11269,7 @@ function DocBlocksShell({
10861
11269
  });
10862
11270
  }, [openTransientFromHandle, showToast]);
10863
11271
  const updateStatusBarVisible = viewPreferences.showStatusBar === true && selectedFile !== null && mediaProvider !== null;
10864
- const handleDownloadWorkspace = useCallback18(async () => {
11272
+ const handleDownloadWorkspace = useCallback19(async () => {
10865
11273
  if (!provider) return;
10866
11274
  try {
10867
11275
  await documentSession.flush("backup");
@@ -10870,8 +11278,11 @@ function DocBlocksShell({
10870
11278
  import("@bendyline/squisq-formats/container")
10871
11279
  ]);
10872
11280
  const container = new MemoryContentContainer();
10873
- await copyProviderToContainer(provider, container, "");
10874
- const blob = await containerToZip(container);
11281
+ const source = getFileSystemProviderV211(provider);
11282
+ if (!source) throw new Error("This workspace does not support complete backups.");
11283
+ const workspaces = await appendWorkspaceBackup(container, source, []);
11284
+ await finishWorkspaceBackup(container, workspaces);
11285
+ const blob = await containerToZip(container, { compression: "STORE" });
10875
11286
  const url = URL.createObjectURL(blob);
10876
11287
  const a = document.createElement("a");
10877
11288
  a.href = url;
@@ -10880,11 +11291,10 @@ function DocBlocksShell({
10880
11291
  a.click();
10881
11292
  URL.revokeObjectURL(url);
10882
11293
  } catch (err) {
10883
- console.error("Failed to download workspace", err);
10884
- showToast("error", "Failed to download workspace. See console for details.");
11294
+ showToast("error", err instanceof Error ? err.message : "Failed to download workspace.");
10885
11295
  }
10886
11296
  }, [provider, documentSession, showToast]);
10887
- const handleDownloadAllWorkspaces = useCallback18(async () => {
11297
+ const handleDownloadAllWorkspaces = useCallback19(async () => {
10888
11298
  try {
10889
11299
  await documentSession.flush("backup");
10890
11300
  const [{ MemoryContentContainer }, { containerToZip }] = await Promise.all([
@@ -10901,7 +11311,7 @@ function DocBlocksShell({
10901
11311
  return;
10902
11312
  }
10903
11313
  const container = new MemoryContentContainer();
10904
- const usedFolders = /* @__PURE__ */ new Set();
11314
+ let backupWorkspaces = [];
10905
11315
  const skipped = [];
10906
11316
  for (const ws of candidates) {
10907
11317
  let p = null;
@@ -10927,24 +11337,20 @@ function DocBlocksShell({
10927
11337
  skipped.push(ws.name);
10928
11338
  continue;
10929
11339
  }
10930
- const base = (ws.name || "workspace").replace(/[^a-z0-9_\- ]/gi, "_").trim() || "workspace";
10931
- let folder = base;
10932
- let suffix = 2;
10933
- while (usedFolders.has(folder)) {
10934
- folder = `${base} (${suffix++})`;
10935
- }
10936
- usedFolders.add(folder);
10937
11340
  try {
10938
- await copyProviderToContainer(p, container, folder);
11341
+ const source = getFileSystemProviderV211(p);
11342
+ if (!source) throw new Error("This workspace does not support complete backups.");
11343
+ backupWorkspaces = await appendWorkspaceBackup(container, source, backupWorkspaces);
10939
11344
  } finally {
10940
- if (ownsProvider) await getFileSystemProviderV210(p)?.dispose();
11345
+ if (ownsProvider) await getFileSystemProviderV211(p)?.dispose();
10941
11346
  }
10942
11347
  }
10943
- if (usedFolders.size === 0) {
11348
+ if (backupWorkspaces.length === 0) {
10944
11349
  showToast("error", "No workspaces could be opened for download.");
10945
11350
  return;
10946
11351
  }
10947
- const blob = await containerToZip(container);
11352
+ await finishWorkspaceBackup(container, backupWorkspaces);
11353
+ const blob = await containerToZip(container, { compression: "STORE" });
10948
11354
  const url = URL.createObjectURL(blob);
10949
11355
  const a = document.createElement("a");
10950
11356
  a.href = url;
@@ -10954,15 +11360,14 @@ function DocBlocksShell({
10954
11360
  if (skipped.length > 0) {
10955
11361
  await acknowledge({
10956
11362
  title: "Some workspaces were skipped",
10957
- message: `Downloaded ${usedFolders.size} workspace(s). Skipped ${skipped.length} that require re-granting access: ${skipped.join(", ")}.`
11363
+ message: `Downloaded ${backupWorkspaces.length} workspace(s). Skipped ${skipped.length} that require re-granting access: ${skipped.join(", ")}.`
10958
11364
  });
10959
11365
  }
10960
11366
  } catch (err) {
10961
- console.error("Failed to download all workspaces", err);
10962
- showToast("error", "Failed to download all workspaces. See console for details.");
11367
+ showToast("error", err instanceof Error ? err.message : "Failed to download all workspaces.");
10963
11368
  }
10964
11369
  }, [documentSession, acknowledge, showToast]);
10965
- const handleKeepBrowserData = useCallback18(async () => {
11370
+ const handleKeepBrowserData = useCallback19(async () => {
10966
11371
  if (typeof navigator === "undefined") return;
10967
11372
  const storage = navigator.storage;
10968
11373
  if (!storage || typeof storage.persist !== "function") {
@@ -10999,7 +11404,7 @@ function DocBlocksShell({
10999
11404
  });
11000
11405
  }
11001
11406
  }, [acknowledge, showToast]);
11002
- const getBrowserStorageEstimate = useCallback18(async () => {
11407
+ const getBrowserStorageEstimate = useCallback19(async () => {
11003
11408
  if (typeof navigator === "undefined") return null;
11004
11409
  const storage = navigator.storage;
11005
11410
  if (!storage || typeof storage.estimate !== "function") return null;
@@ -11011,7 +11416,7 @@ function DocBlocksShell({
11011
11416
  return null;
11012
11417
  }
11013
11418
  }, []);
11014
- const handleRemoveWorkspace = useCallback18(
11419
+ const handleRemoveWorkspace = useCallback19(
11015
11420
  async (workspaceId) => {
11016
11421
  const ws = await getWorkspace(workspaceId);
11017
11422
  const removingActive = workspaceId === activeWorkspaceId;
@@ -11050,7 +11455,7 @@ function DocBlocksShell({
11050
11455
  } else if (ws?.type === "indexeddb") {
11051
11456
  await (await loadIndexedDbFileSystem()).deleteIndexedDBWorkspaceData(workspaceId);
11052
11457
  }
11053
- await removeWorkspace(workspaceId);
11458
+ await removeWorkspace2(workspaceId);
11054
11459
  forgetRecentDocuments(workspaceId);
11055
11460
  for (const document2 of pinnedDocuments) {
11056
11461
  if (document2.workspaceId === workspaceId) {
@@ -11078,7 +11483,7 @@ function DocBlocksShell({
11078
11483
  rootPath: info.rootPath,
11079
11484
  lastOpened: (/* @__PURE__ */ new Date()).toISOString()
11080
11485
  };
11081
- await saveWorkspace2(descriptor);
11486
+ await saveWorkspace3(descriptor);
11082
11487
  const p = await createHostFileSystemProvider(info.id, info.name, info.rootPath);
11083
11488
  setProvider(p);
11084
11489
  setActiveWorkspaceId(info.id);
@@ -11105,7 +11510,7 @@ function DocBlocksShell({
11105
11510
  transitionAwayFromDocument
11106
11511
  ]
11107
11512
  );
11108
- return /* @__PURE__ */ jsx18(
11513
+ return /* @__PURE__ */ jsx19(
11109
11514
  "div",
11110
11515
  {
11111
11516
  ref: shellRef,
@@ -11114,10 +11519,10 @@ function DocBlocksShell({
11114
11519
  "data-accent": accentColor,
11115
11520
  "data-interface-font": interfaceFont === "fixed" ? "fixed" : void 0,
11116
11521
  "data-document-status": documentSnapshot.status,
11117
- children: /* @__PURE__ */ jsxs16(GitContext.Provider, { value: git, children: [
11522
+ children: /* @__PURE__ */ jsxs17(GitContext.Provider, { value: git, children: [
11118
11523
  promptDialog,
11119
11524
  confirmDialog,
11120
- quickOpenOpen && activeWorkspaceId && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(
11525
+ quickOpenOpen && activeWorkspaceId && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(
11121
11526
  QuickOpenDialog,
11122
11527
  {
11123
11528
  index: workspaceIndex.index,
@@ -11131,8 +11536,8 @@ function DocBlocksShell({
11131
11536
  onClose: () => setQuickOpenOpen(false)
11132
11537
  }
11133
11538
  ) }),
11134
- shortcutsOpen && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(KeyboardShortcutsDialog, { onClose: () => setShortcutsOpen(false) }) }),
11135
- newDocumentDialogOpen && /* @__PURE__ */ jsx18(
11539
+ shortcutsOpen && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(KeyboardShortcutsDialog, { onClose: () => setShortcutsOpen(false) }) }),
11540
+ newDocumentDialogOpen && /* @__PURE__ */ jsx19(
11136
11541
  NewDocumentDialog,
11137
11542
  {
11138
11543
  initialFormat: defaultNewFileFormat,
@@ -11142,8 +11547,8 @@ function DocBlocksShell({
11142
11547
  }
11143
11548
  }
11144
11549
  ),
11145
- workspaceStartupError && /* @__PURE__ */ jsx18("div", { className: "db-save-toast db-save-toast--error", role: "alert", "aria-live": "assertive", children: workspaceStartupError }),
11146
- saveToast && /* @__PURE__ */ jsx18(
11550
+ workspaceStartupError && /* @__PURE__ */ jsx19("div", { className: "db-save-toast db-save-toast--error", role: "alert", "aria-live": "assertive", children: workspaceStartupError }),
11551
+ saveToast && /* @__PURE__ */ jsx19(
11147
11552
  "div",
11148
11553
  {
11149
11554
  className: "db-save-toast db-save-toast--" + saveToast.kind,
@@ -11152,12 +11557,12 @@ function DocBlocksShell({
11152
11557
  children: saveToast.message
11153
11558
  }
11154
11559
  ),
11155
- offlineReadyToast && !saveToast && /* @__PURE__ */ jsx18("div", { className: "db-save-toast db-save-toast--success", role: "status", "aria-live": "polite", children: "DocBlocks is ready to work offline." }),
11156
- documentSnapshot.conflict && /* @__PURE__ */ jsxs16("div", { className: "db-document-conflict", role: "alert", children: [
11157
- /* @__PURE__ */ jsx18("span", { children: documentSnapshot.conflict.recoveredDraft ? "A recovery draft from a previous session differs from the saved file. The saved file may be newer. Choose which version to use." : "This document changed outside DocBlocks. Your unsaved version is still intact." }),
11158
- /* @__PURE__ */ jsx18("div", { className: "db-document-conflict-actions", children: /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => setShowConflictComparison(true), children: "Compare versions" }) })
11560
+ offlineReadyToast && !saveToast && /* @__PURE__ */ jsx19("div", { className: "db-save-toast db-save-toast--success", role: "status", "aria-live": "polite", children: "DocBlocks is ready to work offline." }),
11561
+ documentSnapshot.conflict && /* @__PURE__ */ jsxs17("div", { className: "db-document-conflict", role: "alert", children: [
11562
+ /* @__PURE__ */ jsx19("span", { children: documentSnapshot.conflict.recoveredDraft ? "A recovery draft from a previous session differs from the saved file. The saved file may be newer. Choose which version to use." : "This document changed outside DocBlocks. Your unsaved version is still intact." }),
11563
+ /* @__PURE__ */ jsx19("div", { className: "db-document-conflict-actions", children: /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => setShowConflictComparison(true), children: "Compare versions" }) })
11159
11564
  ] }),
11160
- documentSnapshot.conflict && showConflictComparison && /* @__PURE__ */ jsx18(Suspense2, { fallback: /* @__PURE__ */ jsx18("div", { role: "status", children: "Loading version comparison\u2026" }), children: /* @__PURE__ */ jsx18(
11565
+ documentSnapshot.conflict && showConflictComparison && /* @__PURE__ */ jsx19(Suspense2, { fallback: /* @__PURE__ */ jsx19("div", { role: "status", children: "Loading version comparison\u2026" }), children: /* @__PURE__ */ jsx19(
11161
11566
  DocumentConflictDialog,
11162
11567
  {
11163
11568
  conflict: documentSnapshot.conflict,
@@ -11167,14 +11572,14 @@ function DocBlocksShell({
11167
11572
  onClose: () => setShowConflictComparison(false)
11168
11573
  }
11169
11574
  ) }),
11170
- storageFull && !documentSnapshot.conflict && /* @__PURE__ */ jsxs16("div", { className: "db-storage-full-banner", role: "alert", children: [
11171
- /* @__PURE__ */ jsx18("span", { children: "Browser storage is full -- changes can\u2019t be saved. Free up space or back up your work now." }),
11172
- /* @__PURE__ */ jsxs16("div", { className: "db-storage-full-banner-actions", children: [
11173
- /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => void handleDownloadAllWorkspaces(), children: "Back up all workspaces" }),
11174
- /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => setStorageFull(false), children: "Dismiss" })
11575
+ storageFull && !documentSnapshot.conflict && /* @__PURE__ */ jsxs17("div", { className: "db-storage-full-banner", role: "alert", children: [
11576
+ /* @__PURE__ */ jsx19("span", { children: "Browser storage is full -- changes can\u2019t be saved. Free up space or back up your work now." }),
11577
+ /* @__PURE__ */ jsxs17("div", { className: "db-storage-full-banner-actions", children: [
11578
+ /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => void handleDownloadAllWorkspaces(), children: "Back up all workspaces" }),
11579
+ /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => setStorageFull(false), children: "Dismiss" })
11175
11580
  ] })
11176
11581
  ] }),
11177
- workspaceSettingsOpen && activeWorkspaceDescriptor && /* @__PURE__ */ jsx18(
11582
+ workspaceSettingsOpen && activeWorkspaceDescriptor && /* @__PURE__ */ jsx19(
11178
11583
  WorkspaceSettingsDialog,
11179
11584
  {
11180
11585
  workspace: activeWorkspaceDescriptor,
@@ -11187,8 +11592,8 @@ function DocBlocksShell({
11187
11592
  onClose: () => setWorkspaceSettingsOpen(false)
11188
11593
  }
11189
11594
  ),
11190
- /* @__PURE__ */ jsxs16("div", { className: "db-shell-panes", ref: panesRef, children: [
11191
- /* @__PURE__ */ jsxs16(
11595
+ /* @__PURE__ */ jsxs17("div", { className: "db-shell-panes", ref: panesRef, children: [
11596
+ /* @__PURE__ */ jsxs17(
11192
11597
  "aside",
11193
11598
  {
11194
11599
  ref: sidebarRef,
@@ -11196,8 +11601,8 @@ function DocBlocksShell({
11196
11601
  "aria-label": "Workspace and files",
11197
11602
  style: singlePane ? void 0 : { width: `${sidebarWidth}px` },
11198
11603
  children: [
11199
- /* @__PURE__ */ jsxs16("div", { className: "db-shell-sidebar-header", children: [
11200
- /* @__PURE__ */ jsx18(
11604
+ /* @__PURE__ */ jsxs17("div", { className: "db-shell-sidebar-header", children: [
11605
+ /* @__PURE__ */ jsx19(
11201
11606
  AppMenu,
11202
11607
  {
11203
11608
  logoUrl,
@@ -11219,6 +11624,7 @@ function DocBlocksShell({
11219
11624
  onVersioningPreferenceChange: handleVersioningPreferenceChange,
11220
11625
  onOpenWorkspaceSettings: activeWorkspaceDescriptor ? handleOpenWorkspaceSettings : void 0,
11221
11626
  onDownloadAllWorkspaces: handleDownloadAllWorkspaces,
11627
+ onRestoreBackup: hostSupports2("nativeWorkspaces") ? void 0 : () => restoreInputRef.current?.click(),
11222
11628
  onShowKeyboardShortcuts: singlePane ? void 0 : openKeyboardShortcuts,
11223
11629
  onKeepBrowserData: showBrowserStorageWarning && !browserStoragePersistent ? handleKeepBrowserData : void 0,
11224
11630
  onInstallApp: installPromptEvent ? handleInstallApp : void 0,
@@ -11228,10 +11634,26 @@ function DocBlocksShell({
11228
11634
  appBuildDate,
11229
11635
  ai,
11230
11636
  speech,
11637
+ tray: trayMenu,
11231
11638
  settingsRequest
11232
11639
  }
11233
11640
  ),
11234
- /* @__PURE__ */ jsx18(
11641
+ /* @__PURE__ */ jsx19(
11642
+ "input",
11643
+ {
11644
+ ref: restoreInputRef,
11645
+ type: "file",
11646
+ accept: ".zip,application/zip",
11647
+ "aria-label": "Restore workspace backup",
11648
+ hidden: true,
11649
+ onChange: (event) => {
11650
+ const file = event.currentTarget.files?.[0];
11651
+ event.currentTarget.value = "";
11652
+ if (file) void handleRestoreBackup(file);
11653
+ }
11654
+ }
11655
+ ),
11656
+ /* @__PURE__ */ jsx19(
11235
11657
  WorkspacePicker,
11236
11658
  {
11237
11659
  activeWorkspaceId,
@@ -11243,7 +11665,7 @@ function DocBlocksShell({
11243
11665
  onRemoveWorkspace: (workspace) => void handleRemoveWorkspace(workspace.id)
11244
11666
  }
11245
11667
  ),
11246
- /* @__PURE__ */ jsx18(
11668
+ /* @__PURE__ */ jsx19(
11247
11669
  WorkspaceSettingsButton,
11248
11670
  {
11249
11671
  onSettings: handleOpenWorkspaceSettings,
@@ -11254,26 +11676,26 @@ function DocBlocksShell({
11254
11676
  }
11255
11677
  }
11256
11678
  ),
11257
- canRestoreSplitView && /* @__PURE__ */ jsx18(
11679
+ canRestoreSplitView && /* @__PURE__ */ jsx19(
11258
11680
  "button",
11259
11681
  {
11260
11682
  className: "db-restore-split",
11261
11683
  onClick: () => updateSidebarPreference("pinned"),
11262
11684
  "aria-label": "Restore split view",
11263
11685
  title: "Restore split view",
11264
- children: /* @__PURE__ */ jsx18(SplitViewIcon, {})
11686
+ children: /* @__PURE__ */ jsx19(SplitViewIcon, {})
11265
11687
  }
11266
11688
  ),
11267
- /* @__PURE__ */ jsx18("span", { className: "db-window-drag-grip", "aria-hidden": true })
11689
+ /* @__PURE__ */ jsx19("span", { className: "db-window-drag-grip", "aria-hidden": true })
11268
11690
  ] }),
11269
- git.available && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(
11691
+ git.available && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(
11270
11692
  GitUI,
11271
11693
  {
11272
11694
  onOpenFile: (path) => void handleSelect(path, "file"),
11273
11695
  onWorkspaceCloned: handleWorkspaceCloned
11274
11696
  }
11275
11697
  ) }),
11276
- /* @__PURE__ */ jsx18(
11698
+ /* @__PURE__ */ jsx19(
11277
11699
  FileExplorer,
11278
11700
  {
11279
11701
  loading: workspaceStarting,
@@ -11308,7 +11730,7 @@ function DocBlocksShell({
11308
11730
  },
11309
11731
  explorerKey
11310
11732
  ),
11311
- searchMounted && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(
11733
+ searchMounted && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(
11312
11734
  WorkspaceSearchPanel,
11313
11735
  {
11314
11736
  index: workspaceIndex.index,
@@ -11319,17 +11741,17 @@ function DocBlocksShell({
11319
11741
  onShowFiles: () => setSidebarView("files")
11320
11742
  }
11321
11743
  ) }),
11322
- singlePane && showWelcomeGateway && /* @__PURE__ */ jsxs16("section", { className: "db-mobile-first-run", "aria-labelledby": "db-mobile-first-run-title", children: [
11323
- /* @__PURE__ */ jsx18("p", { className: "db-mobile-first-run-eyebrow", children: "Local-first Markdown editor" }),
11324
- /* @__PURE__ */ jsx18("h1", { id: "db-mobile-first-run-title", children: "Welcome to DocBlocks" }),
11325
- /* @__PURE__ */ jsxs16("p", { children: [
11744
+ singlePane && showWelcomeGateway && /* @__PURE__ */ jsxs17("section", { className: "db-mobile-first-run", "aria-labelledby": "db-mobile-first-run-title", children: [
11745
+ /* @__PURE__ */ jsx19("p", { className: "db-mobile-first-run-eyebrow", children: "Local-first Markdown editor" }),
11746
+ /* @__PURE__ */ jsx19("h1", { id: "db-mobile-first-run-title", children: "Welcome to DocBlocks" }),
11747
+ /* @__PURE__ */ jsxs17("p", { children: [
11326
11748
  WELCOME_SUMMARY,
11327
11749
  " ",
11328
11750
  WELCOME_PRIVACY_NOTE
11329
11751
  ] }),
11330
- /* @__PURE__ */ jsxs16("div", { className: "db-mobile-first-run-actions", children: [
11331
- /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => void handleNewFile(), children: "Create a document" }),
11332
- /* @__PURE__ */ jsx18(
11752
+ /* @__PURE__ */ jsxs17("div", { className: "db-mobile-first-run-actions", children: [
11753
+ /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => void handleNewFile(), children: "Create a document" }),
11754
+ /* @__PURE__ */ jsx19(
11333
11755
  "button",
11334
11756
  {
11335
11757
  type: "button",
@@ -11343,14 +11765,14 @@ function DocBlocksShell({
11343
11765
  )
11344
11766
  ] })
11345
11767
  ] }),
11346
- git.available && git.pendingGrant && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(GitGrantNotice, {}) }),
11347
- /* @__PURE__ */ jsxs16("div", { className: "db-shell-sidebar-footer", children: [
11348
- /* @__PURE__ */ jsx18("a", { href: "https://docblocks.com/docs/", target: "_blank", rel: "noopener noreferrer", children: "Docs" }),
11349
- /* @__PURE__ */ jsx18("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
11350
- /* @__PURE__ */ jsx18("a", { href: "https://docblocks.com/terms/", target: "_blank", rel: "noopener noreferrer", children: "Terms" }),
11351
- /* @__PURE__ */ jsx18("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
11352
- /* @__PURE__ */ jsx18("a", { href: issueReportUrl, target: "_blank", rel: "noopener noreferrer", children: "Report issue" }),
11353
- showBrowserStorageWarning && /* @__PURE__ */ jsx18(Fragment11, { children: /* @__PURE__ */ jsxs16(
11768
+ git.available && git.pendingGrant && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(GitGrantNotice, {}) }),
11769
+ /* @__PURE__ */ jsxs17("div", { className: "db-shell-sidebar-footer", children: [
11770
+ /* @__PURE__ */ jsx19("a", { href: "https://docblocks.com/docs/", target: "_blank", rel: "noopener noreferrer", children: "Docs" }),
11771
+ /* @__PURE__ */ jsx19("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
11772
+ /* @__PURE__ */ jsx19("a", { href: "https://docblocks.com/terms/", target: "_blank", rel: "noopener noreferrer", children: "Terms" }),
11773
+ /* @__PURE__ */ jsx19("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
11774
+ /* @__PURE__ */ jsx19("a", { href: issueReportUrl, target: "_blank", rel: "noopener noreferrer", children: "Report issue" }),
11775
+ showBrowserStorageWarning && /* @__PURE__ */ jsx19(Fragment11, { children: /* @__PURE__ */ jsxs17(
11354
11776
  "button",
11355
11777
  {
11356
11778
  type: "button",
@@ -11359,20 +11781,20 @@ function DocBlocksShell({
11359
11781
  "aria-label": "Back up all workspaces",
11360
11782
  title: "Browsers can clear site data. Download a backup of every workspace.",
11361
11783
  children: [
11362
- /* @__PURE__ */ jsx18(BackupIcon, {}),
11784
+ /* @__PURE__ */ jsx19(BackupIcon, {}),
11363
11785
  "Back up"
11364
11786
  ]
11365
11787
  }
11366
11788
  ) })
11367
11789
  ] }),
11368
- /* @__PURE__ */ jsxs16("div", { className: "db-sidebar-collapse-preview", "aria-hidden": "true", children: [
11369
- /* @__PURE__ */ jsx18("span", { className: "db-sidebar-collapse-preview-icon", children: /* @__PURE__ */ jsx18(CollapseSidebarGlyph, {}) }),
11370
- /* @__PURE__ */ jsx18("span", { className: "db-sidebar-collapse-preview-label", children: "Release to hide files" })
11790
+ /* @__PURE__ */ jsxs17("div", { className: "db-sidebar-collapse-preview", "aria-hidden": "true", children: [
11791
+ /* @__PURE__ */ jsx19("span", { className: "db-sidebar-collapse-preview-icon", children: /* @__PURE__ */ jsx19(CollapseSidebarGlyph, {}) }),
11792
+ /* @__PURE__ */ jsx19("span", { className: "db-sidebar-collapse-preview-label", children: "Release to hide files" })
11371
11793
  ] })
11372
11794
  ]
11373
11795
  }
11374
11796
  ),
11375
- !singlePane && /* @__PURE__ */ jsx18(
11797
+ !singlePane && /* @__PURE__ */ jsx19(
11376
11798
  "div",
11377
11799
  {
11378
11800
  className: "db-shell-sidebar-resizer",
@@ -11382,7 +11804,7 @@ function DocBlocksShell({
11382
11804
  onPointerDown: handleResizerPointerDown
11383
11805
  }
11384
11806
  ),
11385
- singlePane && /* @__PURE__ */ jsx18(
11807
+ singlePane && /* @__PURE__ */ jsx19(
11386
11808
  "div",
11387
11809
  {
11388
11810
  className: "db-shell-scrim",
@@ -11390,7 +11812,7 @@ function DocBlocksShell({
11390
11812
  onPointerDown: () => setMobileShowEditor(true)
11391
11813
  }
11392
11814
  ),
11393
- /* @__PURE__ */ jsxs16(
11815
+ /* @__PURE__ */ jsxs17(
11394
11816
  "main",
11395
11817
  {
11396
11818
  ref: editorAreaRef,
@@ -11401,8 +11823,8 @@ function DocBlocksShell({
11401
11823
  editorDropActive ? "db-shell-editor-area--drop-active" : ""
11402
11824
  ].filter(Boolean).join(" "),
11403
11825
  children: [
11404
- /* @__PURE__ */ jsxs16("div", { ref: welcomeCoveredRef, className: "db-shell-editor-content", children: [
11405
- selectedFile && mediaProvider ? /* @__PURE__ */ jsx18(Fragment11, { children: selectedManagedOutput === "catalog-html" ? /* @__PURE__ */ jsx18(
11826
+ /* @__PURE__ */ jsxs17("div", { ref: welcomeCoveredRef, className: "db-shell-editor-content", children: [
11827
+ selectedFile && mediaProvider ? /* @__PURE__ */ jsx19(Fragment11, { children: selectedManagedOutput === "catalog-html" ? /* @__PURE__ */ jsx19(
11406
11828
  GeneratedCatalogPage,
11407
11829
  {
11408
11830
  html: editorContent,
@@ -11410,17 +11832,17 @@ function DocBlocksShell({
11410
11832
  onOpenSettings: activeWorkspaceDescriptor ? handleOpenWorkspaceSettings : void 0,
11411
11833
  onBack: singlePane ? openDrawer : void 0
11412
11834
  }
11413
- ) : /* @__PURE__ */ jsx18(
11835
+ ) : /* @__PURE__ */ jsx19(
11414
11836
  GeneratedFileFrame,
11415
11837
  {
11416
11838
  kind: selectedManagedOutput,
11417
11839
  onOpenSettings: activeWorkspaceDescriptor ? handleOpenWorkspaceSettings : void 0,
11418
11840
  onBack: singlePane ? openDrawer : void 0,
11419
- children: /* @__PURE__ */ jsx18(
11841
+ children: /* @__PURE__ */ jsx19(
11420
11842
  Suspense2,
11421
11843
  {
11422
- fallback: /* @__PURE__ */ jsx18("div", { className: "db-shell-empty", role: "status", children: "Loading your document\u2026" }),
11423
- children: editorGenerationSettled && mediaProviderCurrent && /* @__PURE__ */ jsx18(EditorErrorBoundary, { resetKey: `${selectedFile}-${editorKey}`, children: /* @__PURE__ */ jsx18(
11844
+ fallback: /* @__PURE__ */ jsx19("div", { className: "db-shell-empty", role: "status", children: "Loading your document\u2026" }),
11845
+ children: editorGenerationSettled && mediaProviderCurrent && /* @__PURE__ */ jsx19(EditorErrorBoundary, { resetKey: `${selectedFile}-${editorKey}`, children: /* @__PURE__ */ jsx19(
11424
11846
  EditorShell,
11425
11847
  {
11426
11848
  initialMarkdown: editorContent,
@@ -11437,6 +11859,7 @@ function DocBlocksShell({
11437
11859
  saveDashboardImageOutput: saveRenderedImageOutput,
11438
11860
  onChange: handleEditorChange,
11439
11861
  onLinkClick: handleEditorLinkClick,
11862
+ wikiLinks,
11440
11863
  colorScheme: resolvedTheme,
11441
11864
  defaultThemeId: workspaceDefaultThemeId,
11442
11865
  defaultThemeLabel: "Workspace default",
@@ -11470,19 +11893,19 @@ function DocBlocksShell({
11470
11893
  versioningAutoSaveIdleMs,
11471
11894
  onSaveVersion,
11472
11895
  statusBarSlotRight,
11473
- sidePanelSlot: ai && aiPanel && selectedImage === void 0 && (selectedOutsideIn === null || selectedOutsideInEditingEnabled) ? /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: aiPanel === "review" ? /* @__PURE__ */ jsx18(AiReviewPanel, { ai, onClose: () => setAiPanel(null) }) : /* @__PURE__ */ jsx18(AiIllustratePanel, { ai, onClose: () => setAiPanel(null) }) }) : void 0,
11474
- toolbarSlotLeft: singlePane ? /* @__PURE__ */ jsx18(
11896
+ sidePanelSlot: ai && aiPanel && selectedImage === void 0 && (selectedOutsideIn === null || selectedOutsideInEditingEnabled) ? /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: aiPanel === "review" ? /* @__PURE__ */ jsx19(AiReviewPanel, { ai, onClose: () => setAiPanel(null) }) : /* @__PURE__ */ jsx19(AiIllustratePanel, { ai, onClose: () => setAiPanel(null) }) }) : void 0,
11897
+ toolbarSlotLeft: singlePane ? /* @__PURE__ */ jsx19(
11475
11898
  "button",
11476
11899
  {
11477
11900
  className: "db-mobile-back",
11478
11901
  onClick: () => setMobileShowEditor(false),
11479
11902
  "aria-label": "Show file list",
11480
- children: /* @__PURE__ */ jsx18("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx18(FolderGlyph, {}) })
11903
+ children: /* @__PURE__ */ jsx19("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx19(FolderGlyph, {}) })
11481
11904
  }
11482
11905
  ) : void 0,
11483
- summarizationDesigner: selectedImage === void 0 && selectedManagedOutput === void 0 && (selectedOutsideIn === null || selectedOutsideInEditingEnabled) ? (onClose) => /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(PresentationMaker, { ai: ai ?? void 0, onClose }) }) : void 0,
11484
- toolbarSlotRight: /* @__PURE__ */ jsxs16(Fragment11, { children: [
11485
- canFind && /* @__PURE__ */ jsx18(
11906
+ summarizationDesigner: selectedImage === void 0 && selectedManagedOutput === void 0 && (selectedOutsideIn === null || selectedOutsideInEditingEnabled) ? (onClose) => /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(PresentationMaker, { ai: ai ?? void 0, onClose }) }) : void 0,
11907
+ toolbarSlotRight: /* @__PURE__ */ jsxs17(Fragment11, { children: [
11908
+ canFind && /* @__PURE__ */ jsx19(
11486
11909
  FindButton,
11487
11910
  {
11488
11911
  active: findMode,
@@ -11490,7 +11913,7 @@ function DocBlocksShell({
11490
11913
  shortcutLabel: findShortcut
11491
11914
  }
11492
11915
  ),
11493
- ai && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(
11916
+ ai && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(
11494
11917
  AiToolbarControl,
11495
11918
  {
11496
11919
  ai,
@@ -11499,8 +11922,8 @@ function DocBlocksShell({
11499
11922
  onOpenPanel: setAiPanel
11500
11923
  }
11501
11924
  ) }),
11502
- speech && /* @__PURE__ */ jsxs16(Suspense2, { fallback: null, children: [
11503
- /* @__PURE__ */ jsx18(
11925
+ speech && /* @__PURE__ */ jsxs17(Suspense2, { fallback: null, children: [
11926
+ /* @__PURE__ */ jsx19(
11504
11927
  TextFromNarration,
11505
11928
  {
11506
11929
  speech,
@@ -11510,27 +11933,27 @@ function DocBlocksShell({
11510
11933
  onOpenSettings: openSettings
11511
11934
  }
11512
11935
  ),
11513
- hostSupports2("speechOutput") ? /* @__PURE__ */ jsx18(
11936
+ hostSupports2("speechOutput") ? /* @__PURE__ */ jsx19(
11514
11937
  SpeechToolbarControl,
11515
11938
  {
11516
11939
  speech,
11517
11940
  onOpenSettings: openSettings,
11518
11941
  readOnly: selectedImage !== void 0 || selectedOutsideIn !== null && !selectedOutsideInEditingEnabled
11519
11942
  }
11520
- ) : /* @__PURE__ */ jsx18(SpeechMenuBridge, {})
11943
+ ) : /* @__PURE__ */ jsx19(SpeechMenuBridge, {})
11521
11944
  ] }),
11522
- canRestoreSplitView && /* @__PURE__ */ jsx18(
11945
+ canRestoreSplitView && /* @__PURE__ */ jsx19(
11523
11946
  "button",
11524
11947
  {
11525
11948
  className: "db-restore-split",
11526
11949
  onClick: () => updateSidebarPreference("pinned"),
11527
11950
  "aria-label": "Restore split view",
11528
11951
  title: "Restore split view",
11529
- children: /* @__PURE__ */ jsx18(SplitViewIcon, {})
11952
+ children: /* @__PURE__ */ jsx19(SplitViewIcon, {})
11530
11953
  }
11531
11954
  ),
11532
- git.repo && /* @__PURE__ */ jsx18(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx18(GitToolbarControl, { selectedFile }) }),
11533
- /* @__PURE__ */ jsx18(
11955
+ git.repo && /* @__PURE__ */ jsx19(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx19(GitToolbarControl, { selectedFile }) }),
11956
+ /* @__PURE__ */ jsx19(
11534
11957
  ExportToolbarControls,
11535
11958
  {
11536
11959
  selectedFile,
@@ -11554,39 +11977,39 @@ function DocBlocksShell({
11554
11977
  }
11555
11978
  )
11556
11979
  }
11557
- ) }) : selectedFolder ? /* @__PURE__ */ jsxs16("div", { className: "db-folder-view", children: [
11558
- singlePane && /* @__PURE__ */ jsxs16("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
11559
- /* @__PURE__ */ jsx18("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx18(FolderGlyph, {}) }),
11980
+ ) }) : selectedFolder ? /* @__PURE__ */ jsxs17("div", { className: "db-folder-view", children: [
11981
+ singlePane && /* @__PURE__ */ jsxs17("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
11982
+ /* @__PURE__ */ jsx19("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx19(FolderGlyph, {}) }),
11560
11983
  "Back to files"
11561
11984
  ] }),
11562
- /* @__PURE__ */ jsxs16("div", { className: "db-folder-view-header", children: [
11563
- /* @__PURE__ */ jsx18("span", { className: "db-folder-view-icon", children: /* @__PURE__ */ jsx18(FolderGlyph, {}) }),
11564
- /* @__PURE__ */ jsx18("span", { className: "db-folder-view-path", children: selectedFolder })
11985
+ /* @__PURE__ */ jsxs17("div", { className: "db-folder-view-header", children: [
11986
+ /* @__PURE__ */ jsx19("span", { className: "db-folder-view-icon", children: /* @__PURE__ */ jsx19(FolderGlyph, {}) }),
11987
+ /* @__PURE__ */ jsx19("span", { className: "db-folder-view-path", children: selectedFolder })
11565
11988
  ] }),
11566
- visibleFolderEntries.length === 0 ? /* @__PURE__ */ jsx18("p", { className: "db-folder-view-empty", children: "This folder is empty." }) : /* @__PURE__ */ jsx18("ul", { className: "db-folder-view-list", children: visibleFolderEntries.map((entry) => /* @__PURE__ */ jsxs16(
11989
+ visibleFolderEntries.length === 0 ? /* @__PURE__ */ jsx19("p", { className: "db-folder-view-empty", children: "This folder is empty." }) : /* @__PURE__ */ jsx19("ul", { className: "db-folder-view-list", children: visibleFolderEntries.map((entry) => /* @__PURE__ */ jsxs17(
11567
11990
  "li",
11568
11991
  {
11569
11992
  className: "db-folder-view-item",
11570
11993
  onClick: () => handleSelect(entry.path, entry.kind),
11571
11994
  children: [
11572
- /* @__PURE__ */ jsx18("span", { className: "db-folder-view-item-icon", children: entry.kind === "directory" ? /* @__PURE__ */ jsx18(FolderGlyph, {}) : /* @__PURE__ */ jsx18(FileGlyph, {}) }),
11995
+ /* @__PURE__ */ jsx19("span", { className: "db-folder-view-item-icon", children: entry.kind === "directory" ? /* @__PURE__ */ jsx19(FolderGlyph, {}) : /* @__PURE__ */ jsx19(FileGlyph, {}) }),
11573
11996
  entry.name
11574
11997
  ]
11575
11998
  },
11576
11999
  entry.path
11577
12000
  )) })
11578
- ] }) : /* @__PURE__ */ jsxs16("div", { className: "db-shell-empty db-shell-empty--workspace", children: [
11579
- singlePane && /* @__PURE__ */ jsxs16("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
11580
- /* @__PURE__ */ jsx18("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx18(FolderGlyph, {}) }),
12001
+ ] }) : /* @__PURE__ */ jsxs17("div", { className: "db-shell-empty db-shell-empty--workspace", children: [
12002
+ singlePane && /* @__PURE__ */ jsxs17("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
12003
+ /* @__PURE__ */ jsx19("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx19(FolderGlyph, {}) }),
11581
12004
  "Back to files"
11582
12005
  ] }),
11583
- /* @__PURE__ */ jsxs16("div", { className: "db-workspace-empty-content", children: [
11584
- /* @__PURE__ */ jsx18("span", { className: "db-workspace-empty-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx18(FileGlyph, {}) }),
11585
- /* @__PURE__ */ jsx18("h1", { children: activeWorkspaceDescriptor?.name ?? "Workspace" }),
11586
- /* @__PURE__ */ jsx18("p", { children: "Choose a Markdown document from the sidebar, or create a new one." }),
11587
- /* @__PURE__ */ jsxs16("div", { className: "db-workspace-empty-actions", children: [
11588
- /* @__PURE__ */ jsx18("button", { type: "button", onClick: () => void handleNewFile(), children: "New document" }),
11589
- /* @__PURE__ */ jsx18(
12006
+ /* @__PURE__ */ jsxs17("div", { className: "db-workspace-empty-content", children: [
12007
+ /* @__PURE__ */ jsx19("span", { className: "db-workspace-empty-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx19(FileGlyph, {}) }),
12008
+ /* @__PURE__ */ jsx19("h1", { children: activeWorkspaceDescriptor?.name ?? "Workspace" }),
12009
+ /* @__PURE__ */ jsx19("p", { children: "Choose a Markdown document from the sidebar, or create a new one." }),
12010
+ /* @__PURE__ */ jsxs17("div", { className: "db-workspace-empty-actions", children: [
12011
+ /* @__PURE__ */ jsx19("button", { type: "button", onClick: () => void handleNewFile(), children: "New document" }),
12012
+ /* @__PURE__ */ jsx19(
11590
12013
  "button",
11591
12014
  {
11592
12015
  type: "button",
@@ -11595,7 +12018,7 @@ function DocBlocksShell({
11595
12018
  children: "New folder"
11596
12019
  }
11597
12020
  ),
11598
- (hostSupports2("workspaceFolderPicker") || typeof globalThis.showDirectoryPicker === "function") && /* @__PURE__ */ jsx18(
12021
+ (hostSupports2("workspaceFolderPicker") || typeof globalThis.showDirectoryPicker === "function") && /* @__PURE__ */ jsx19(
11599
12022
  "button",
11600
12023
  {
11601
12024
  type: "button",
@@ -11605,10 +12028,10 @@ function DocBlocksShell({
11605
12028
  }
11606
12029
  )
11607
12030
  ] }),
11608
- /* @__PURE__ */ jsx18("p", { className: "db-workspace-empty-hint", children: "Use the sidebar backup action to keep a portable copy of this workspace." })
12031
+ showBrowserStorageWarning && /* @__PURE__ */ jsx19("p", { className: "db-workspace-empty-hint", children: "Use the sidebar backup action to keep a portable copy of this workspace." })
11609
12032
  ] })
11610
12033
  ] }),
11611
- /* @__PURE__ */ jsx18(
12034
+ /* @__PURE__ */ jsx19(
11612
12035
  UpdateAvailableNotice,
11613
12036
  {
11614
12037
  available: updateAvailable,
@@ -11618,7 +12041,7 @@ function DocBlocksShell({
11618
12041
  }
11619
12042
  )
11620
12043
  ] }),
11621
- welcomeScreenShown && /* @__PURE__ */ jsx18(
12044
+ welcomeScreenShown && /* @__PURE__ */ jsx19(
11622
12045
  WelcomeScreen,
11623
12046
  {
11624
12047
  onCreateDocument: handleNewFile,