@bendyline/docblocks-react 2.2.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64,7 +64,8 @@ import {
64
64
  fsErrorFromUnknown,
65
65
  getFileSystemProviderV2,
66
66
  moveFileSystemEntry,
67
- parseWorkspacePath
67
+ parseWorkspacePath,
68
+ workspacePathDirname
68
69
  } from "@bendyline/docblocks/filesystem";
69
70
  var DIRECTORY_NOT_FOUND_RETRY_DELAYS_MS = Object.freeze([40, 120]);
70
71
  function normalisePath(path) {
@@ -97,7 +98,12 @@ async function readProviderDirectory(provider, path) {
97
98
  const providerV2 = getFileSystemProviderV2(provider);
98
99
  if (!providerV2) return provider.readDirectory(path);
99
100
  const entries = await providerV2.readDirectory(canonical);
100
- return entries.map((entry) => ({ kind: entry.kind, name: entry.name, path: entry.path }));
101
+ return entries.map((entry) => ({
102
+ kind: entry.kind,
103
+ name: entry.name,
104
+ path: entry.path,
105
+ ...entry.kind === "file" ? { lastModified: entry.lastModified } : {}
106
+ }));
101
107
  };
102
108
  for (let attempt = 0; ; attempt += 1) {
103
109
  try {
@@ -131,7 +137,7 @@ function readIssue(caught, directoryPath) {
131
137
  retryable: error.retryable || error.code === "not-found"
132
138
  });
133
139
  }
134
- function useFileTree(provider) {
140
+ function useFileTree(provider, metadataRefreshKey, metadataRefreshPath) {
135
141
  const [entries, setEntries] = useState([]);
136
142
  const [expanded, setExpanded] = useState(/* @__PURE__ */ new Set());
137
143
  const [selectedPath, setSelectedPath] = useState(null);
@@ -147,7 +153,7 @@ function useFileTree(provider) {
147
153
  const reportRootIssue = useCallback((caught) => {
148
154
  setRootIssue(readIssue(caught, ""));
149
155
  }, []);
150
- const loadRoot = useCallback(async () => {
156
+ const loadRoot = useCallback(async (showLoading = true) => {
151
157
  const sourceProvider = providerRef.current;
152
158
  if (!sourceProvider) {
153
159
  setEntries([]);
@@ -157,7 +163,7 @@ function useFileTree(provider) {
157
163
  const requestId = ++directoryRequestSequenceRef.current;
158
164
  directoryRequestsRef.current.set("", requestId);
159
165
  const isCurrent = () => providerRef.current === sourceProvider && directoryRequestsRef.current.get("") === requestId;
160
- setLoading(true);
166
+ if (showLoading) setLoading(true);
161
167
  try {
162
168
  const root = await readProviderDirectory(sourceProvider, "");
163
169
  if (!isCurrent()) return;
@@ -263,37 +269,76 @@ function useFileTree(provider) {
263
269
  );
264
270
  const refreshRef = useRef(refresh);
265
271
  refreshRef.current = refresh;
272
+ const refreshDirectory = useCallback(
273
+ async (directoryPath) => {
274
+ if (directoryPath === "") await loadRoot(false);
275
+ else await loadChildren(directoryPath);
276
+ },
277
+ [loadChildren, loadRoot]
278
+ );
279
+ const previousMetadataRefreshKeyRef = useRef(metadataRefreshKey);
280
+ useEffect(() => {
281
+ if (Object.is(previousMetadataRefreshKeyRef.current, metadataRefreshKey)) return;
282
+ previousMetadataRefreshKeyRef.current = metadataRefreshKey;
283
+ if (!provider) return;
284
+ if (getFileSystemProviderV2(provider)?.capabilities.watch) return;
285
+ const directoryPath = metadataRefreshPath ? workspacePathDirname(parseWorkspacePath(metadataRefreshPath)) : "";
286
+ void refreshDirectory(directoryPath).catch(reportRootIssue);
287
+ }, [metadataRefreshKey, metadataRefreshPath, provider, refreshDirectory, reportRootIssue]);
266
288
  useEffect(() => {
267
289
  if (!provider) return;
268
290
  const providerV2 = getFileSystemProviderV2(provider);
269
291
  if (!providerV2?.capabilities.watch) return;
270
292
  let disposed = false;
271
293
  let refreshing = false;
272
- let refreshAgain = false;
273
- const requestRefresh = () => {
294
+ let fullRefreshRequested = false;
295
+ const directoriesToRefresh = /* @__PURE__ */ new Set();
296
+ const drainRefreshes = () => {
274
297
  if (disposed) return;
275
- if (refreshing) {
276
- refreshAgain = true;
277
- return;
278
- }
298
+ if (refreshing) return;
279
299
  refreshing = true;
280
300
  void (async () => {
281
301
  try {
282
- do {
283
- refreshAgain = false;
284
- await refreshRef.current();
285
- } while (refreshAgain && !disposed);
302
+ while (!disposed && (fullRefreshRequested || directoriesToRefresh.size > 0)) {
303
+ if (fullRefreshRequested) {
304
+ fullRefreshRequested = false;
305
+ directoriesToRefresh.clear();
306
+ await refreshRef.current();
307
+ continue;
308
+ }
309
+ const pending = [...directoriesToRefresh];
310
+ directoriesToRefresh.clear();
311
+ for (const directoryPath of pending) {
312
+ await refreshDirectory(directoryPath);
313
+ }
314
+ }
286
315
  } catch (caught) {
287
316
  reportRootIssue(caught);
288
317
  } finally {
289
318
  refreshing = false;
290
- if (refreshAgain && !disposed) requestRefresh();
319
+ if (!disposed && (fullRefreshRequested || directoriesToRefresh.size > 0)) {
320
+ drainRefreshes();
321
+ }
291
322
  }
292
323
  })();
293
324
  };
325
+ const requestRefresh = () => {
326
+ if (disposed) return;
327
+ fullRefreshRequested = true;
328
+ directoriesToRefresh.clear();
329
+ drainRefreshes();
330
+ };
331
+ const requestMetadataRefresh = (path) => {
332
+ if (disposed || fullRefreshRequested) return;
333
+ directoriesToRefresh.add(workspacePathDirname(parseWorkspacePath(path)));
334
+ drainRefreshes();
335
+ };
294
336
  const subscription = providerV2.watch(
295
337
  (event) => {
296
- if (event.type === "modified") return;
338
+ if (event.type === "modified") {
339
+ requestMetadataRefresh(event.path);
340
+ return;
341
+ }
297
342
  requestRefresh();
298
343
  },
299
344
  {
@@ -311,7 +356,7 @@ function useFileTree(provider) {
311
356
  disposed = true;
312
357
  void subscription.dispose();
313
358
  };
314
- }, [provider, reportRootIssue]);
359
+ }, [provider, refreshDirectory, reportRootIssue]);
315
360
  useEffect(() => {
316
361
  if (!provider) return;
317
362
  const providerV2 = getFileSystemProviderV2(provider);
@@ -448,6 +493,12 @@ function NewFileIcon() {
448
493
  function NewFolderIcon() {
449
494
  return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder-plus" });
450
495
  }
496
+ function SortByNameIcon() {
497
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-arrow-down-a-z" });
498
+ }
499
+ function SortByLastModifiedIcon() {
500
+ return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-clock" });
501
+ }
451
502
  function FolderIcon() {
452
503
  return /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: "fa-solid fa-folder" });
453
504
  }
@@ -481,8 +532,116 @@ function SplitViewIcon() {
481
532
  );
482
533
  }
483
534
 
535
+ // src/FileExplorer/LastModifiedTime.tsx
536
+ import { useSyncExternalStore } from "react";
537
+
538
+ // src/FileExplorer/last-modified.ts
539
+ var MINUTE_MS = 6e4;
540
+ var HOUR_MS = 60 * MINUTE_MS;
541
+ var DAY_MS = 24 * HOUR_MS;
542
+ var shortDateFormatter = new Intl.DateTimeFormat(void 0, {
543
+ month: "short",
544
+ day: "numeric"
545
+ });
546
+ var shortDateWithYearFormatter = new Intl.DateTimeFormat(void 0, {
547
+ year: "numeric",
548
+ month: "short",
549
+ day: "numeric"
550
+ });
551
+ var fullDateTimeFormatter = new Intl.DateTimeFormat(void 0, {
552
+ dateStyle: "medium",
553
+ timeStyle: "short"
554
+ });
555
+ function plural(value, unit) {
556
+ return `${value} ${unit}${value === 1 ? "" : "s"} ago`;
557
+ }
558
+ function formatFriendlyLastModified(value, now = Date.now()) {
559
+ if (!value) return null;
560
+ const timestamp = Date.parse(value);
561
+ if (!Number.isFinite(timestamp)) return null;
562
+ const date = new Date(timestamp);
563
+ const elapsed = now - timestamp;
564
+ const title = `Last modified ${fullDateTimeFormatter.format(date)}`;
565
+ if (elapsed >= -MINUTE_MS && elapsed < MINUTE_MS) {
566
+ return { shortLabel: "Now", accessibleLabel: "Last modified just now", title };
567
+ }
568
+ if (elapsed >= 0 && elapsed < HOUR_MS) {
569
+ const minutes = Math.max(1, Math.floor(elapsed / MINUTE_MS));
570
+ return {
571
+ shortLabel: `${minutes}m ago`,
572
+ accessibleLabel: `Last modified ${plural(minutes, "minute")}`,
573
+ title
574
+ };
575
+ }
576
+ if (elapsed >= 0 && elapsed < DAY_MS) {
577
+ const hours = Math.max(1, Math.floor(elapsed / HOUR_MS));
578
+ return {
579
+ shortLabel: `${hours}h ago`,
580
+ accessibleLabel: `Last modified ${plural(hours, "hour")}`,
581
+ title
582
+ };
583
+ }
584
+ if (elapsed >= DAY_MS && elapsed < 2 * DAY_MS) {
585
+ return { shortLabel: "Yesterday", accessibleLabel: "Last modified yesterday", title };
586
+ }
587
+ if (elapsed >= 2 * DAY_MS && elapsed < 7 * DAY_MS) {
588
+ const days = Math.floor(elapsed / DAY_MS);
589
+ return {
590
+ shortLabel: `${days}d ago`,
591
+ accessibleLabel: `Last modified ${plural(days, "day")}`,
592
+ title
593
+ };
594
+ }
595
+ const currentYear = new Date(now).getFullYear();
596
+ const shortLabel = date.getFullYear() === currentYear ? shortDateFormatter.format(date) : shortDateWithYearFormatter.format(date);
597
+ return { shortLabel, accessibleLabel: title, title };
598
+ }
599
+
600
+ // src/FileExplorer/LastModifiedTime.tsx
601
+ import { jsx as jsx2 } from "react/jsx-runtime";
602
+ var CLOCK_INTERVAL_MS = 6e4;
603
+ var clockNow = Date.now();
604
+ var clockTimer = null;
605
+ var clockListeners = /* @__PURE__ */ new Set();
606
+ function publishClockTick() {
607
+ clockNow = Date.now();
608
+ for (const listener of clockListeners) listener();
609
+ }
610
+ function subscribeToClock(listener) {
611
+ clockListeners.add(listener);
612
+ if (clockListeners.size === 1) {
613
+ clockNow = Date.now();
614
+ clockTimer = globalThis.setInterval(publishClockTick, CLOCK_INTERVAL_MS);
615
+ }
616
+ return () => {
617
+ clockListeners.delete(listener);
618
+ if (clockListeners.size === 0 && clockTimer !== null) {
619
+ globalThis.clearInterval(clockTimer);
620
+ clockTimer = null;
621
+ }
622
+ };
623
+ }
624
+ function getClockSnapshot() {
625
+ return clockNow;
626
+ }
627
+ function LastModifiedTime({ value }) {
628
+ const now = useSyncExternalStore(subscribeToClock, getClockSnapshot, getClockSnapshot);
629
+ const lastModified = formatFriendlyLastModified(value, now);
630
+ if (!lastModified) return null;
631
+ return /* @__PURE__ */ jsx2(
632
+ "time",
633
+ {
634
+ className: "db-tree-last-modified",
635
+ dateTime: value,
636
+ title: lastModified.title,
637
+ "aria-label": lastModified.accessibleLabel,
638
+ children: lastModified.shortLabel
639
+ }
640
+ );
641
+ }
642
+
484
643
  // src/FileExplorer/FileTreeNode.tsx
485
- import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
644
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
486
645
  function defaultConfirmDelete(message) {
487
646
  if (typeof window === "undefined") return false;
488
647
  return window.confirm(message);
@@ -769,8 +928,8 @@ function FileTreeNode({
769
928
  }
770
929
  },
771
930
  children: [
772
- (isDir || depth === 0) && /* @__PURE__ */ jsx2("span", { className: "db-tree-icon", children: isDir ? icon : null }),
773
- renaming ? /* @__PURE__ */ jsx2(
931
+ (isDir || depth === 0) && /* @__PURE__ */ jsx3("span", { className: "db-tree-icon", children: isDir ? icon : null }),
932
+ renaming ? /* @__PURE__ */ jsx3(
774
933
  "input",
775
934
  {
776
935
  ref: inputRef,
@@ -788,9 +947,10 @@ function FileTreeNode({
788
947
  onClick: (e) => e.stopPropagation()
789
948
  }
790
949
  ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
791
- /* @__PURE__ */ jsx2("span", { className: "db-tree-label", children: entry.name.endsWith(".md") ? entry.name.slice(0, -3) : entry.name }),
792
- badge && /* @__PURE__ */ jsx2("span", { className: `db-git-badge db-git-badge--${badge.kind}`, "aria-hidden": "true", children: badge.glyph }),
793
- /* @__PURE__ */ jsx2(
950
+ /* @__PURE__ */ jsx3("span", { className: "db-tree-label", children: entry.name.endsWith(".md") ? entry.name.slice(0, -3) : entry.name }),
951
+ badge && /* @__PURE__ */ jsx3("span", { className: `db-git-badge db-git-badge--${badge.kind}`, "aria-hidden": "true", children: badge.glyph }),
952
+ entry.kind === "file" && entry.lastModified && /* @__PURE__ */ jsx3(LastModifiedTime, { value: entry.lastModified }),
953
+ /* @__PURE__ */ jsx3(
794
954
  "button",
795
955
  {
796
956
  type: "button",
@@ -803,14 +963,14 @@ function FileTreeNode({
803
963
  "aria-keyshortcuts": "Shift+F10",
804
964
  title: "More actions (Shift+F10)",
805
965
  tabIndex: -1,
806
- children: /* @__PURE__ */ jsx2(MoreIcon, {})
966
+ children: /* @__PURE__ */ jsx3(MoreIcon, {})
807
967
  }
808
968
  )
809
969
  ] })
810
970
  ]
811
971
  }
812
972
  ),
813
- actionError && /* @__PURE__ */ jsx2("div", { className: "db-tree-error", role: "alert", children: actionError }),
973
+ actionError && /* @__PURE__ */ jsx3("div", { className: "db-tree-error", role: "alert", children: actionError }),
814
974
  showContext && createPortal(
815
975
  /* @__PURE__ */ jsxs2(
816
976
  "div",
@@ -822,7 +982,7 @@ function FileTreeNode({
822
982
  "aria-label": `Actions for ${entry.name}`,
823
983
  onKeyDown: handleMenuKeyDown,
824
984
  children: [
825
- /* @__PURE__ */ jsx2(
985
+ /* @__PURE__ */ jsx3(
826
986
  "button",
827
987
  {
828
988
  type: "button",
@@ -833,7 +993,7 @@ function FileTreeNode({
833
993
  children: "Rename"
834
994
  }
835
995
  ),
836
- !isDir && onTogglePin && /* @__PURE__ */ jsx2(
996
+ !isDir && onTogglePin && /* @__PURE__ */ jsx3(
837
997
  "button",
838
998
  {
839
999
  type: "button",
@@ -845,8 +1005,8 @@ function FileTreeNode({
845
1005
  }
846
1006
  ),
847
1007
  gitActions && (gitActions.viewChanges || gitActions.fileHistory) && /* @__PURE__ */ jsxs2(Fragment, { children: [
848
- /* @__PURE__ */ jsx2("div", { className: "db-tree-context-divider", role: "separator" }),
849
- gitActions.viewChanges && /* @__PURE__ */ jsx2(
1008
+ /* @__PURE__ */ jsx3("div", { className: "db-tree-context-divider", role: "separator" }),
1009
+ gitActions.viewChanges && /* @__PURE__ */ jsx3(
850
1010
  "button",
851
1011
  {
852
1012
  type: "button",
@@ -860,7 +1020,7 @@ function FileTreeNode({
860
1020
  children: "View changes"
861
1021
  }
862
1022
  ),
863
- gitActions.fileHistory && /* @__PURE__ */ jsx2(
1023
+ gitActions.fileHistory && /* @__PURE__ */ jsx3(
864
1024
  "button",
865
1025
  {
866
1026
  type: "button",
@@ -874,7 +1034,7 @@ function FileTreeNode({
874
1034
  children: "File history\u2026"
875
1035
  }
876
1036
  ),
877
- gitActions.openOnRemote && /* @__PURE__ */ jsx2(
1037
+ gitActions.openOnRemote && /* @__PURE__ */ jsx3(
878
1038
  "button",
879
1039
  {
880
1040
  type: "button",
@@ -888,9 +1048,9 @@ function FileTreeNode({
888
1048
  children: "Open on remote"
889
1049
  }
890
1050
  ),
891
- /* @__PURE__ */ jsx2("div", { className: "db-tree-context-divider", role: "separator" })
1051
+ /* @__PURE__ */ jsx3("div", { className: "db-tree-context-divider", role: "separator" })
892
1052
  ] }),
893
- /* @__PURE__ */ jsx2(
1053
+ /* @__PURE__ */ jsx3(
894
1054
  "button",
895
1055
  {
896
1056
  type: "button",
@@ -906,7 +1066,7 @@ function FileTreeNode({
906
1066
  ),
907
1067
  document.body
908
1068
  ),
909
- isDir && expanded && renderChildren && /* @__PURE__ */ jsx2("div", { className: "db-tree-children", role: "group", children: renderChildren(entry.path) }),
1069
+ isDir && expanded && renderChildren && /* @__PURE__ */ jsx3("div", { className: "db-tree-children", role: "group", children: renderChildren(entry.path) }),
910
1070
  isDir && expanded && childError
911
1071
  ] });
912
1072
  }
@@ -1096,7 +1256,7 @@ function movePinnedDocumentsToWorkspace(documents, sourceWorkspaceId, destinatio
1096
1256
  }
1097
1257
 
1098
1258
  // src/FileExplorer/PinnedDocuments.tsx
1099
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1259
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1100
1260
  function displayName(document2) {
1101
1261
  const name = pinnedDocumentName(document2);
1102
1262
  return name.endsWith(".md") ? name.slice(0, -3) : name;
@@ -1240,7 +1400,7 @@ function PinnedDocumentRow({
1240
1400
  }));
1241
1401
  }, [showContext]);
1242
1402
  return /* @__PURE__ */ jsxs3("div", { className: `db-pinned-document-row${selected ? " db-pinned-document-row--selected" : ""}`, children: [
1243
- /* @__PURE__ */ jsx3(
1403
+ /* @__PURE__ */ jsx4(
1244
1404
  "button",
1245
1405
  {
1246
1406
  ref: selectRef,
@@ -1259,12 +1419,12 @@ function PinnedDocumentRow({
1259
1419
  }
1260
1420
  },
1261
1421
  children: /* @__PURE__ */ jsxs3("span", { className: "db-pinned-document-heading", children: [
1262
- /* @__PURE__ */ jsx3("span", { className: "db-pinned-document-name", children: displayName(pinnedDocument) }),
1263
- missing && /* @__PURE__ */ jsx3("span", { className: "db-pinned-document-missing", children: "(missing)" })
1422
+ /* @__PURE__ */ jsx4("span", { className: "db-pinned-document-name", children: displayName(pinnedDocument) }),
1423
+ missing && /* @__PURE__ */ jsx4("span", { className: "db-pinned-document-missing", children: "(missing)" })
1264
1424
  ] })
1265
1425
  }
1266
1426
  ),
1267
- hasActions && /* @__PURE__ */ jsx3(
1427
+ hasActions && /* @__PURE__ */ jsx4(
1268
1428
  "button",
1269
1429
  {
1270
1430
  type: "button",
@@ -1275,10 +1435,10 @@ function PinnedDocumentRow({
1275
1435
  title: "More actions",
1276
1436
  onClick: handleMoreClick,
1277
1437
  onContextMenu: handleMoreClick,
1278
- children: /* @__PURE__ */ jsx3(MoreIcon, {})
1438
+ children: /* @__PURE__ */ jsx4(MoreIcon, {})
1279
1439
  }
1280
1440
  ),
1281
- actionError && /* @__PURE__ */ jsx3("div", { className: "db-tree-error db-pinned-document-error", role: "alert", children: actionError }),
1441
+ actionError && /* @__PURE__ */ jsx4("div", { className: "db-tree-error db-pinned-document-error", role: "alert", children: actionError }),
1282
1442
  showContext && createPortal2(
1283
1443
  /* @__PURE__ */ jsxs3(
1284
1444
  "div",
@@ -1290,7 +1450,7 @@ function PinnedDocumentRow({
1290
1450
  "aria-label": `Actions for ${name}`,
1291
1451
  onKeyDown: handleMenuKeyDown,
1292
1452
  children: [
1293
- onRename && /* @__PURE__ */ jsx3(
1453
+ onRename && /* @__PURE__ */ jsx4(
1294
1454
  "button",
1295
1455
  {
1296
1456
  type: "button",
@@ -1301,7 +1461,7 @@ function PinnedDocumentRow({
1301
1461
  children: "Rename"
1302
1462
  }
1303
1463
  ),
1304
- onUnpin && /* @__PURE__ */ jsx3(
1464
+ onUnpin && /* @__PURE__ */ jsx4(
1305
1465
  "button",
1306
1466
  {
1307
1467
  type: "button",
@@ -1312,8 +1472,8 @@ function PinnedDocumentRow({
1312
1472
  children: "Unpin"
1313
1473
  }
1314
1474
  ),
1315
- onDelete && (onRename || onUnpin) && /* @__PURE__ */ jsx3("div", { className: "db-tree-context-divider", role: "separator" }),
1316
- onDelete && /* @__PURE__ */ jsx3(
1475
+ onDelete && (onRename || onUnpin) && /* @__PURE__ */ jsx4("div", { className: "db-tree-context-divider", role: "separator" }),
1476
+ onDelete && /* @__PURE__ */ jsx4(
1317
1477
  "button",
1318
1478
  {
1319
1479
  type: "button",
@@ -1344,10 +1504,10 @@ function PinnedDocuments({
1344
1504
  if (documents.length === 0) return null;
1345
1505
  return /* @__PURE__ */ jsxs3("section", { className: "db-pinned-documents", "aria-labelledby": titleId, children: [
1346
1506
  /* @__PURE__ */ jsxs3("h2", { id: titleId, className: "db-explorer-title db-pinned-documents-title", children: [
1347
- /* @__PURE__ */ jsx3(PinIcon, {}),
1348
- /* @__PURE__ */ jsx3("span", { children: "Pinned" })
1507
+ /* @__PURE__ */ jsx4(PinIcon, {}),
1508
+ /* @__PURE__ */ jsx4("span", { children: "Pinned" })
1349
1509
  ] }),
1350
- /* @__PURE__ */ jsx3("div", { className: "db-pinned-document-list", children: documents.map((document2) => /* @__PURE__ */ jsx3(
1510
+ /* @__PURE__ */ jsx4("div", { className: "db-pinned-document-list", children: documents.map((document2) => /* @__PURE__ */ jsx4(
1351
1511
  PinnedDocumentRow,
1352
1512
  {
1353
1513
  document: document2,
@@ -1372,8 +1532,34 @@ function filterVisibleFileEntries(entries) {
1372
1532
  return entries.filter((entry) => !isHiddenFileEntry(entry));
1373
1533
  }
1374
1534
 
1535
+ // src/FileExplorer/entry-sort.ts
1536
+ function compareText(left, right) {
1537
+ return left < right ? -1 : left > right ? 1 : 0;
1538
+ }
1539
+ function modifiedTime(entry) {
1540
+ if (entry.kind !== "file" || !entry.lastModified) return null;
1541
+ const timestamp = Date.parse(entry.lastModified);
1542
+ return Number.isNaN(timestamp) ? null : timestamp;
1543
+ }
1544
+ function sortFileEntries(entries, mode) {
1545
+ return [...entries].sort((left, right) => {
1546
+ if (left.kind !== right.kind) return left.kind === "directory" ? -1 : 1;
1547
+ if (left.kind === "directory" || right.kind === "directory" || mode === "name") {
1548
+ return compareText(left.name, right.name);
1549
+ }
1550
+ const leftTime = modifiedTime(left);
1551
+ const rightTime = modifiedTime(right);
1552
+ if (leftTime !== null && rightTime !== null && leftTime !== rightTime) {
1553
+ return rightTime - leftTime;
1554
+ }
1555
+ if (leftTime === null && rightTime !== null) return 1;
1556
+ if (leftTime !== null && rightTime === null) return -1;
1557
+ return compareText(left.name, right.name);
1558
+ });
1559
+ }
1560
+
1375
1561
  // src/FileExplorer/FileExplorer.tsx
1376
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1562
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1377
1563
  var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".txt", ".md", ".docx", ".pdf", ".dbk", ".zip"]);
1378
1564
  var INTERNAL_DRAG_TYPE = "application/x-docblocks-entry";
1379
1565
  var NEW_ITEM_ERROR_ID = "db-new-item-error";
@@ -1412,7 +1598,10 @@ function isInternalDrag(dataTransfer) {
1412
1598
  }
1413
1599
  function FileExplorer({
1414
1600
  provider,
1601
+ metadataRefreshKey,
1415
1602
  activeFilePath,
1603
+ sortMode,
1604
+ onSortModeChange,
1416
1605
  activeWorkspaceId,
1417
1606
  pinnedDocuments = [],
1418
1607
  pinnedPaths = [],
@@ -1430,7 +1619,7 @@ function FileExplorer({
1430
1619
  onMoveToWorkspace,
1431
1620
  className
1432
1621
  }) {
1433
- const tree = useFileTree(provider);
1622
+ const tree = useFileTree(provider, metadataRefreshKey, activeFilePath);
1434
1623
  const { childEntries, childIssues } = tree;
1435
1624
  const { reveal } = tree;
1436
1625
  const git = useGitContext();
@@ -1439,6 +1628,15 @@ function FileExplorer({
1439
1628
  [pinnedPaths]
1440
1629
  );
1441
1630
  const [newItemName, setNewItemName] = useState4("");
1631
+ const [uncontrolledSortMode, setUncontrolledSortMode] = useState4("name");
1632
+ const selectedSortMode = sortMode ?? uncontrolledSortMode;
1633
+ const selectSortMode = useCallback4(
1634
+ (mode) => {
1635
+ if (sortMode === void 0) setUncontrolledSortMode(mode);
1636
+ onSortModeChange?.(mode);
1637
+ },
1638
+ [onSortModeChange, sortMode]
1639
+ );
1442
1640
  const [newItemType, setNewItemType] = useState4(null);
1443
1641
  const [newItemCreationPending, setNewItemCreationPending] = useState4(false);
1444
1642
  const newItemCreationPendingRef = useRef4(false);
@@ -1549,6 +1747,7 @@ function FileExplorer({
1549
1747
  const filename = name.endsWith(".md") ? name : `${name}.md`;
1550
1748
  createdPath = `${prefix}${filename}`;
1551
1749
  await tree.createFile(createdPath, "");
1750
+ handleSelect(createdPath);
1552
1751
  } else if (itemType === "directory") {
1553
1752
  await tree.createDirectory(createdPath);
1554
1753
  }
@@ -1562,7 +1761,7 @@ function FileExplorer({
1562
1761
  setNewItemName("");
1563
1762
  setNewItemType(null);
1564
1763
  onTreeChange?.({ type: "create", path: createdPath });
1565
- }, [newItemName, newItemType, tree, onTreeChange]);
1764
+ }, [newItemName, newItemType, tree, handleSelect, onTreeChange]);
1566
1765
  const handleMoveToWorkspace = useCallback4(async () => {
1567
1766
  if (!onMoveToWorkspace || !moveDestinationId || movingToWorkspace) return;
1568
1767
  setMovingToWorkspace(true);
@@ -1700,14 +1899,18 @@ function FileExplorer({
1700
1899
  },
1701
1900
  [git]
1702
1901
  );
1902
+ const orderedVisibleEntries = useCallback4(
1903
+ (entries) => sortFileEntries(filterVisibleFileEntries(entries), selectedSortMode),
1904
+ [selectedSortMode]
1905
+ );
1703
1906
  const visibleRows = useMemo(
1704
1907
  () => flattenVisibleRows({
1705
- roots: tree.entries,
1706
- childrenOf: (dirPath) => getEquivalentPathValue(childEntries, dirPath) ?? [],
1908
+ roots: orderedVisibleEntries(tree.entries),
1909
+ childrenOf: (dirPath) => orderedVisibleEntries(getEquivalentPathValue(childEntries, dirPath) ?? []),
1707
1910
  isExpanded: (dirPath) => hasEquivalentPath2(tree.expanded, dirPath),
1708
- isVisible: (entry) => !isHiddenFileEntry(entry)
1911
+ isVisible: () => true
1709
1912
  }),
1710
- [tree.entries, tree.expanded, childEntries]
1913
+ [tree.entries, tree.expanded, childEntries, orderedVisibleEntries]
1711
1914
  );
1712
1915
  const selectedRowPath = useMemo(() => {
1713
1916
  if (!tree.selectedPath) return null;
@@ -1751,10 +1954,10 @@ function FileExplorer({
1751
1954
  );
1752
1955
  const renderEntries = useCallback4(
1753
1956
  (entries, depth) => {
1754
- const visible = filterVisibleFileEntries(entries);
1957
+ const visible = orderedVisibleEntries(entries);
1755
1958
  return visible.map((entry, index) => {
1756
1959
  const childIssue = entry.kind === "directory" ? getEquivalentPathValue(childIssues, entry.path) : void 0;
1757
- return /* @__PURE__ */ jsx4(
1960
+ return /* @__PURE__ */ jsx5(
1758
1961
  FileTreeNode,
1759
1962
  {
1760
1963
  entry,
@@ -1792,9 +1995,9 @@ function FileExplorer({
1792
1995
  role: "alert",
1793
1996
  "data-directory-path": childIssue.directoryPath,
1794
1997
  children: [
1795
- /* @__PURE__ */ jsx4("span", { children: childIssue.message }),
1998
+ /* @__PURE__ */ jsx5("span", { children: childIssue.message }),
1796
1999
  " ",
1797
- /* @__PURE__ */ jsx4(
2000
+ /* @__PURE__ */ jsx5(
1798
2001
  "button",
1799
2002
  {
1800
2003
  type: "button",
@@ -1829,7 +2032,8 @@ function FileExplorer({
1829
2032
  handleEntryDrop,
1830
2033
  activeRowPath,
1831
2034
  pinnedPathSet,
1832
- onTogglePin
2035
+ onTogglePin,
2036
+ orderedVisibleEntries
1833
2037
  ]
1834
2038
  );
1835
2039
  return /* @__PURE__ */ jsxs4(
@@ -1841,7 +2045,7 @@ function FileExplorer({
1841
2045
  onDragLeave: handleDragLeave,
1842
2046
  onDrop: handleDrop,
1843
2047
  children: [
1844
- onPinnedDocumentSelect && /* @__PURE__ */ jsx4(
2048
+ onPinnedDocumentSelect && /* @__PURE__ */ jsx5(
1845
2049
  PinnedDocuments,
1846
2050
  {
1847
2051
  documents: pinnedDocuments,
@@ -1854,11 +2058,39 @@ function FileExplorer({
1854
2058
  }
1855
2059
  ),
1856
2060
  /* @__PURE__ */ jsxs4("div", { className: "db-explorer-toolbar", children: [
1857
- /* @__PURE__ */ jsx4("span", { className: "db-explorer-title", children: "Files" }),
2061
+ /* @__PURE__ */ jsx5("span", { className: "db-explorer-title", children: "Files" }),
1858
2062
  /* @__PURE__ */ jsxs4("div", { className: "db-explorer-actions", children: [
1859
- /* @__PURE__ */ jsx4(
2063
+ /* @__PURE__ */ jsxs4("div", { className: "db-explorer-sort-actions", role: "group", "aria-label": "File sorting", children: [
2064
+ /* @__PURE__ */ jsx5(
2065
+ "button",
2066
+ {
2067
+ type: "button",
2068
+ className: "db-explorer-btn",
2069
+ onClick: () => selectSortMode("name"),
2070
+ title: "Sort by name",
2071
+ "aria-label": "Sort by name",
2072
+ "aria-pressed": selectedSortMode === "name",
2073
+ children: /* @__PURE__ */ jsx5(SortByNameIcon, {})
2074
+ }
2075
+ ),
2076
+ /* @__PURE__ */ jsx5(
2077
+ "button",
2078
+ {
2079
+ type: "button",
2080
+ className: "db-explorer-btn",
2081
+ onClick: () => selectSortMode("last-modified"),
2082
+ title: "Sort by last modified",
2083
+ "aria-label": "Sort by last modified",
2084
+ "aria-pressed": selectedSortMode === "last-modified",
2085
+ children: /* @__PURE__ */ jsx5(SortByLastModifiedIcon, {})
2086
+ }
2087
+ )
2088
+ ] }),
2089
+ /* @__PURE__ */ jsx5("span", { className: "db-explorer-action-divider", "aria-hidden": "true" }),
2090
+ /* @__PURE__ */ jsx5(
1860
2091
  "button",
1861
2092
  {
2093
+ type: "button",
1862
2094
  className: "db-explorer-btn",
1863
2095
  disabled: newItemCreationPending,
1864
2096
  onClick: () => {
@@ -1867,12 +2099,13 @@ function FileExplorer({
1867
2099
  },
1868
2100
  title: "New Folder",
1869
2101
  "aria-label": "New Folder",
1870
- children: /* @__PURE__ */ jsx4(NewFolderIcon, {})
2102
+ children: /* @__PURE__ */ jsx5(NewFolderIcon, {})
1871
2103
  }
1872
2104
  ),
1873
- /* @__PURE__ */ jsx4(
2105
+ /* @__PURE__ */ jsx5(
1874
2106
  "button",
1875
2107
  {
2108
+ type: "button",
1876
2109
  className: "db-explorer-btn",
1877
2110
  disabled: newItemCreationPending,
1878
2111
  onClick: () => {
@@ -1881,12 +2114,12 @@ function FileExplorer({
1881
2114
  },
1882
2115
  title: "New File",
1883
2116
  "aria-label": "New File",
1884
- children: /* @__PURE__ */ jsx4(NewFileIcon, {})
2117
+ children: /* @__PURE__ */ jsx5(NewFileIcon, {})
1885
2118
  }
1886
2119
  )
1887
2120
  ] })
1888
2121
  ] }),
1889
- onMoveToWorkspace && /* @__PURE__ */ jsx4("div", { className: "db-transient-move", children: !movePanelOpen ? /* @__PURE__ */ jsx4(
2122
+ onMoveToWorkspace && /* @__PURE__ */ jsx5("div", { className: "db-transient-move", children: !movePanelOpen ? /* @__PURE__ */ jsx5(
1890
2123
  "button",
1891
2124
  {
1892
2125
  type: "button",
@@ -1907,8 +2140,8 @@ function FileExplorer({
1907
2140
  void handleMoveToWorkspace();
1908
2141
  },
1909
2142
  children: [
1910
- /* @__PURE__ */ jsx4("label", { className: "db-transient-move-label", htmlFor: "db-transient-move-destination", children: "Move this document into" }),
1911
- /* @__PURE__ */ jsx4(
2143
+ /* @__PURE__ */ jsx5("label", { className: "db-transient-move-label", htmlFor: "db-transient-move-destination", children: "Move this document into" }),
2144
+ /* @__PURE__ */ jsx5(
1912
2145
  "select",
1913
2146
  {
1914
2147
  id: "db-transient-move-destination",
@@ -1919,13 +2152,13 @@ function FileExplorer({
1919
2152
  setMoveDestinationId(event.currentTarget.value);
1920
2153
  setMoveWorkspaceError(null);
1921
2154
  },
1922
- children: moveDestinations.map((destination) => /* @__PURE__ */ jsx4("option", { value: destination.id, children: destination.name }, destination.id))
2155
+ children: moveDestinations.map((destination) => /* @__PURE__ */ jsx5("option", { value: destination.id, children: destination.name }, destination.id))
1923
2156
  }
1924
2157
  ),
1925
- moveDestinations.length === 0 && /* @__PURE__ */ jsx4("p", { className: "db-transient-move-hint", children: "Open or create another workspace first." }),
1926
- moveWorkspaceError && /* @__PURE__ */ jsx4("div", { className: "db-transient-move-error", role: "alert", children: moveWorkspaceError }),
2158
+ moveDestinations.length === 0 && /* @__PURE__ */ jsx5("p", { className: "db-transient-move-hint", children: "Open or create another workspace first." }),
2159
+ moveWorkspaceError && /* @__PURE__ */ jsx5("div", { className: "db-transient-move-error", role: "alert", children: moveWorkspaceError }),
1927
2160
  /* @__PURE__ */ jsxs4("div", { className: "db-transient-move-actions", children: [
1928
- /* @__PURE__ */ jsx4(
2161
+ /* @__PURE__ */ jsx5(
1929
2162
  "button",
1930
2163
  {
1931
2164
  type: "button",
@@ -1938,7 +2171,7 @@ function FileExplorer({
1938
2171
  children: "Cancel"
1939
2172
  }
1940
2173
  ),
1941
- /* @__PURE__ */ jsx4(
2174
+ /* @__PURE__ */ jsx5(
1942
2175
  "button",
1943
2176
  {
1944
2177
  type: "submit",
@@ -1962,7 +2195,7 @@ function FileExplorer({
1962
2195
  void handleNewItemSubmit();
1963
2196
  },
1964
2197
  children: [
1965
- /* @__PURE__ */ jsx4(
2198
+ /* @__PURE__ */ jsx5(
1966
2199
  "input",
1967
2200
  {
1968
2201
  className: "db-new-item-input",
@@ -1986,14 +2219,14 @@ function FileExplorer({
1986
2219
  autoFocus: true
1987
2220
  }
1988
2221
  ),
1989
- newItemType === "file" && /* @__PURE__ */ jsx4("span", { className: "db-new-item-suffix", children: ".md" }),
1990
- /* @__PURE__ */ jsx4("button", { type: "submit", className: "db-new-item-add", disabled: newItemCreationPending, children: newItemCreationPending ? "Adding\u2026" : "Add" })
2222
+ newItemType === "file" && /* @__PURE__ */ jsx5("span", { className: "db-new-item-suffix", children: ".md" }),
2223
+ /* @__PURE__ */ jsx5("button", { type: "submit", className: "db-new-item-add", disabled: newItemCreationPending, children: newItemCreationPending ? "Adding\u2026" : "Add" })
1991
2224
  ]
1992
2225
  }
1993
2226
  ),
1994
- newItemError && /* @__PURE__ */ jsx4("div", { id: NEW_ITEM_ERROR_ID, className: "db-tree-error", role: "alert", children: newItemError })
2227
+ newItemError && /* @__PURE__ */ jsx5("div", { id: NEW_ITEM_ERROR_ID, className: "db-tree-error", role: "alert", children: newItemError })
1995
2228
  ] }),
1996
- moveError && /* @__PURE__ */ jsx4("div", { className: "db-tree-error", role: "alert", children: moveError }),
2229
+ moveError && /* @__PURE__ */ jsx5("div", { className: "db-tree-error", role: "alert", children: moveError }),
1997
2230
  tree.rootIssue && /* @__PURE__ */ jsxs4(
1998
2231
  "div",
1999
2232
  {
@@ -2001,9 +2234,9 @@ function FileExplorer({
2001
2234
  role: "alert",
2002
2235
  "data-directory-path": tree.rootIssue.directoryPath,
2003
2236
  children: [
2004
- /* @__PURE__ */ jsx4("span", { children: tree.rootIssue.message }),
2237
+ /* @__PURE__ */ jsx5("span", { children: tree.rootIssue.message }),
2005
2238
  " ",
2006
- /* @__PURE__ */ jsx4(
2239
+ /* @__PURE__ */ jsx5(
2007
2240
  "button",
2008
2241
  {
2009
2242
  type: "button",
@@ -2015,7 +2248,7 @@ function FileExplorer({
2015
2248
  ]
2016
2249
  }
2017
2250
  ),
2018
- tree.loading ? /* @__PURE__ */ jsx4("div", { className: "db-tree", role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx4("div", { className: "db-tree-loading", children: "Loading..." }) }) : filterVisibleFileEntries(tree.entries).length === 0 ? /* @__PURE__ */ jsx4("div", { className: "db-tree", children: /* @__PURE__ */ jsx4("div", { className: "db-tree-empty", children: "No files yet" }) }) : /* @__PURE__ */ jsx4(
2251
+ tree.loading ? /* @__PURE__ */ jsx5("div", { className: "db-tree", role: "status", "aria-live": "polite", children: /* @__PURE__ */ jsx5("div", { className: "db-tree-loading", children: "Loading..." }) }) : filterVisibleFileEntries(tree.entries).length === 0 ? /* @__PURE__ */ jsx5("div", { className: "db-tree", children: /* @__PURE__ */ jsx5("div", { className: "db-tree-empty", children: "No files yet" }) }) : /* @__PURE__ */ jsx5(
2019
2252
  "div",
2020
2253
  {
2021
2254
  ref: treeRef,
@@ -2037,14 +2270,14 @@ function FileExplorer({
2037
2270
  import { Fragment as Fragment2, useState as useState5, useEffect as useEffect5, useCallback as useCallback5, useRef as useRef5 } from "react";
2038
2271
  import { listWorkspaces, saveWorkspace, touchWorkspace } from "@bendyline/docblocks/workspace";
2039
2272
  import { isElectronHost } from "@bendyline/docblocks/host";
2040
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2273
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2041
2274
  function isNativeFileSystemSupported() {
2042
2275
  return typeof globalThis !== "undefined" && typeof globalThis.showDirectoryPicker === "function";
2043
2276
  }
2044
2277
  function WorkspacePath({ path }) {
2045
2278
  return path.split(/([\\/])/).map((segment, index) => /* @__PURE__ */ jsxs5(Fragment2, { children: [
2046
2279
  segment,
2047
- (segment === "\\" || segment === "/") && /* @__PURE__ */ jsx5("wbr", {})
2280
+ (segment === "\\" || segment === "/") && /* @__PURE__ */ jsx6("wbr", {})
2048
2281
  ] }, index));
2049
2282
  }
2050
2283
  function WorkspacePicker({
@@ -2173,9 +2406,9 @@ function WorkspacePicker({
2173
2406
  "aria-label": `Switch workspace, current: ${activeWorkspaceName}`,
2174
2407
  "aria-expanded": isOpen,
2175
2408
  children: [
2176
- /* @__PURE__ */ jsx5("span", { className: "db-workspace-picker-label", children: activeWorkspaceName }),
2177
- /* @__PURE__ */ jsx5("span", { className: "db-workspace-picker-compact-icon", children: /* @__PURE__ */ jsx5(FolderIcon, {}) }),
2178
- /* @__PURE__ */ jsx5(
2409
+ /* @__PURE__ */ jsx6("span", { className: "db-workspace-picker-label", children: activeWorkspaceName }),
2410
+ /* @__PURE__ */ jsx6("span", { className: "db-workspace-picker-compact-icon", children: /* @__PURE__ */ jsx6(FolderIcon, {}) }),
2411
+ /* @__PURE__ */ jsx6(
2179
2412
  "span",
2180
2413
  {
2181
2414
  className: `db-workspace-picker-caret${isOpen ? " db-workspace-picker-caret--open" : ""}`,
@@ -2186,22 +2419,22 @@ function WorkspacePicker({
2186
2419
  }
2187
2420
  ),
2188
2421
  isOpen && /* @__PURE__ */ jsxs5("div", { className: "db-workspace-dropdown", children: [
2189
- workspaces.map((ws) => /* @__PURE__ */ jsx5(
2422
+ workspaces.map((ws) => /* @__PURE__ */ jsx6(
2190
2423
  "button",
2191
2424
  {
2192
2425
  className: `db-workspace-dropdown-item ${ws.id === activeWorkspaceId ? "db-workspace-dropdown-item--active" : ""}`,
2193
2426
  onClick: () => handleSelect(ws),
2194
2427
  children: /* @__PURE__ */ jsxs5("span", { className: "db-workspace-details", children: [
2195
2428
  /* @__PURE__ */ jsxs5("span", { className: "db-workspace-heading", children: [
2196
- /* @__PURE__ */ jsx5("span", { children: ws.name }),
2197
- (ws.type === "native" || ws.type === "electron-native") && /* @__PURE__ */ jsx5("span", { className: "db-workspace-type", children: "(folder)" })
2429
+ /* @__PURE__ */ jsx6("span", { children: ws.name }),
2430
+ (ws.type === "native" || ws.type === "electron-native") && /* @__PURE__ */ jsx6("span", { className: "db-workspace-type", children: "(folder)" })
2198
2431
  ] }),
2199
- ws.rootPath && /* @__PURE__ */ jsx5("span", { className: "db-workspace-path", title: ws.rootPath, children: /* @__PURE__ */ jsx5(WorkspacePath, { path: ws.rootPath }) })
2432
+ ws.rootPath && /* @__PURE__ */ jsx6("span", { className: "db-workspace-path", title: ws.rootPath, children: /* @__PURE__ */ jsx6(WorkspacePath, { path: ws.rootPath }) })
2200
2433
  ] })
2201
2434
  },
2202
2435
  ws.id
2203
2436
  )),
2204
- /* @__PURE__ */ jsx5("div", { className: "db-workspace-dropdown-divider" }),
2437
+ /* @__PURE__ */ jsx6("div", { className: "db-workspace-dropdown-divider" }),
2205
2438
  !electron && (creatingNew ? /* @__PURE__ */ jsxs5(
2206
2439
  "form",
2207
2440
  {
@@ -2212,8 +2445,8 @@ function WorkspacePicker({
2212
2445
  void handleCreateNew();
2213
2446
  },
2214
2447
  children: [
2215
- /* @__PURE__ */ jsx5("label", { className: "db-workspace-create-label", htmlFor: "db-new-workspace-name", children: "Workspace name" }),
2216
- /* @__PURE__ */ jsx5(
2448
+ /* @__PURE__ */ jsx6("label", { className: "db-workspace-create-label", htmlFor: "db-new-workspace-name", children: "Workspace name" }),
2449
+ /* @__PURE__ */ jsx6(
2217
2450
  "input",
2218
2451
  {
2219
2452
  id: "db-new-workspace-name",
@@ -2233,9 +2466,9 @@ function WorkspacePicker({
2233
2466
  }
2234
2467
  }
2235
2468
  ),
2236
- newWorkspaceError && /* @__PURE__ */ jsx5("p", { id: "db-new-workspace-error", className: "db-workspace-create-error", role: "alert", children: newWorkspaceError }),
2469
+ newWorkspaceError && /* @__PURE__ */ jsx6("p", { id: "db-new-workspace-error", className: "db-workspace-create-error", role: "alert", children: newWorkspaceError }),
2237
2470
  /* @__PURE__ */ jsxs5("div", { className: "db-workspace-create-actions", children: [
2238
- /* @__PURE__ */ jsx5(
2471
+ /* @__PURE__ */ jsx6(
2239
2472
  "button",
2240
2473
  {
2241
2474
  type: "button",
@@ -2245,15 +2478,15 @@ function WorkspacePicker({
2245
2478
  children: "Cancel"
2246
2479
  }
2247
2480
  ),
2248
- /* @__PURE__ */ jsx5("button", { type: "submit", disabled: newWorkspacePending, children: newWorkspacePending ? "Creating\xE2\u20AC\xA6" : "Create" })
2481
+ /* @__PURE__ */ jsx6("button", { type: "submit", disabled: newWorkspacePending, children: newWorkspacePending ? "Creating\xE2\u20AC\xA6" : "Create" })
2249
2482
  ] })
2250
2483
  ]
2251
2484
  }
2252
- ) : /* @__PURE__ */ jsx5("button", { className: "db-workspace-dropdown-item", onClick: handleStartCreateNew, children: /* @__PURE__ */ jsxs5("span", { className: "db-workspace-dropdown-action-label", children: [
2253
- /* @__PURE__ */ jsx5(NewFolderIcon, {}),
2254
- /* @__PURE__ */ jsx5("span", { children: "New Workspace" })
2485
+ ) : /* @__PURE__ */ jsx6("button", { className: "db-workspace-dropdown-item", onClick: handleStartCreateNew, children: /* @__PURE__ */ jsxs5("span", { className: "db-workspace-dropdown-action-label", children: [
2486
+ /* @__PURE__ */ jsx6(NewFolderIcon, {}),
2487
+ /* @__PURE__ */ jsx6("span", { children: "New Workspace" })
2255
2488
  ] }) })),
2256
- (electron || isNativeFileSystemSupported()) && /* @__PURE__ */ jsx5(
2489
+ (electron || isNativeFileSystemSupported()) && /* @__PURE__ */ jsx6(
2257
2490
  "button",
2258
2491
  {
2259
2492
  className: "db-workspace-dropdown-item",
@@ -2264,7 +2497,7 @@ function WorkspacePicker({
2264
2497
  children: "Open Folder..."
2265
2498
  }
2266
2499
  ),
2267
- onCloneRepository && /* @__PURE__ */ jsx5(
2500
+ onCloneRepository && /* @__PURE__ */ jsx6(
2268
2501
  "button",
2269
2502
  {
2270
2503
  className: "db-workspace-dropdown-item",
@@ -2320,7 +2553,7 @@ import {
2320
2553
  // src/AppMenu/AppMenu.tsx
2321
2554
  import { useState as useState6, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect6 } from "react";
2322
2555
  import { isElectronHost as isElectronHost2 } from "@bendyline/docblocks/host";
2323
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2556
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2324
2557
  function formatBytes(bytes) {
2325
2558
  if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
2326
2559
  let value = bytes;
@@ -2409,7 +2642,7 @@ function AppMenu({
2409
2642
  "aria-expanded": isOpen,
2410
2643
  "aria-haspopup": "menu",
2411
2644
  children: [
2412
- logoUrl ? /* @__PURE__ */ jsx6(
2645
+ logoUrl ? /* @__PURE__ */ jsx7(
2413
2646
  "img",
2414
2647
  {
2415
2648
  src: logoUrl,
@@ -2418,8 +2651,8 @@ function AppMenu({
2418
2651
  width: 1266,
2419
2652
  height: 544
2420
2653
  }
2421
- ) : /* @__PURE__ */ jsx6("span", { className: "db-app-menu-label", children: "docblocks" }),
2422
- /* @__PURE__ */ jsx6(
2654
+ ) : /* @__PURE__ */ jsx7("span", { className: "db-app-menu-label", children: "docblocks" }),
2655
+ /* @__PURE__ */ jsx7(
2423
2656
  "span",
2424
2657
  {
2425
2658
  className: `db-app-menu-caret${isOpen ? " db-app-menu-caret--open" : ""}`,
@@ -2437,7 +2670,7 @@ function AppMenu({
2437
2670
  role: "menu",
2438
2671
  onKeyDown: handleMenuKeyDown,
2439
2672
  children: [
2440
- /* @__PURE__ */ jsx6(
2673
+ /* @__PURE__ */ jsx7(
2441
2674
  "button",
2442
2675
  {
2443
2676
  className: "db-app-menu-item",
@@ -2447,7 +2680,7 @@ function AppMenu({
2447
2680
  children: "Settings"
2448
2681
  }
2449
2682
  ),
2450
- onInstallApp && /* @__PURE__ */ jsx6(
2683
+ onInstallApp && /* @__PURE__ */ jsx7(
2451
2684
  "button",
2452
2685
  {
2453
2686
  className: "db-app-menu-item",
@@ -2457,7 +2690,7 @@ function AppMenu({
2457
2690
  children: "Install DocBlocks\u2026"
2458
2691
  }
2459
2692
  ),
2460
- onKeepBrowserData && /* @__PURE__ */ jsx6(
2693
+ onKeepBrowserData && /* @__PURE__ */ jsx7(
2461
2694
  "button",
2462
2695
  {
2463
2696
  className: "db-app-menu-item",
@@ -2467,7 +2700,7 @@ function AppMenu({
2467
2700
  children: "Protect data from browser cleanup"
2468
2701
  }
2469
2702
  ),
2470
- onDownloadAllWorkspaces && /* @__PURE__ */ jsx6(
2703
+ onDownloadAllWorkspaces && /* @__PURE__ */ jsx7(
2471
2704
  "button",
2472
2705
  {
2473
2706
  className: "db-app-menu-item",
@@ -2477,8 +2710,8 @@ function AppMenu({
2477
2710
  children: "Download all workspaces"
2478
2711
  }
2479
2712
  ),
2480
- /* @__PURE__ */ jsx6("div", { className: "db-app-menu-divider" }),
2481
- /* @__PURE__ */ jsx6(
2713
+ /* @__PURE__ */ jsx7("div", { className: "db-app-menu-divider" }),
2714
+ /* @__PURE__ */ jsx7(
2482
2715
  "button",
2483
2716
  {
2484
2717
  className: "db-app-menu-item",
@@ -2493,21 +2726,21 @@ function AppMenu({
2493
2726
  )
2494
2727
  ] }),
2495
2728
  showSettings && /* @__PURE__ */ jsxs6(SettingsDialog, { onClose: () => setShowSettings(false), children: [
2496
- /* @__PURE__ */ jsx6(
2729
+ /* @__PURE__ */ jsx7(
2497
2730
  ThemeSettings,
2498
2731
  {
2499
2732
  value: themePreference,
2500
2733
  onChange: (preference) => onThemeChange?.(preference)
2501
2734
  }
2502
2735
  ),
2503
- /* @__PURE__ */ jsx6(
2736
+ /* @__PURE__ */ jsx7(
2504
2737
  AccentColorSettings,
2505
2738
  {
2506
2739
  value: accentColor,
2507
2740
  onChange: (color) => onAccentColorChange?.(color)
2508
2741
  }
2509
2742
  ),
2510
- /* @__PURE__ */ jsx6(
2743
+ /* @__PURE__ */ jsx7(
2511
2744
  WriteCanvasSettingsControls,
2512
2745
  {
2513
2746
  value: writeCanvasSettings,
@@ -2515,14 +2748,14 @@ function AppMenu({
2515
2748
  }
2516
2749
  ),
2517
2750
  getStorageEstimate && /* @__PURE__ */ jsxs6("fieldset", { className: "db-settings-fieldset", children: [
2518
- /* @__PURE__ */ jsx6("legend", { className: "db-settings-legend", children: "Storage" }),
2519
- /* @__PURE__ */ jsx6("p", { className: "db-settings-hint", children: storageEstimate ? `DocBlocks documents and app data are using ${formatBytes(
2751
+ /* @__PURE__ */ jsx7("legend", { className: "db-settings-legend", children: "Storage" }),
2752
+ /* @__PURE__ */ jsx7("p", { className: "db-settings-hint", children: storageEstimate ? `DocBlocks documents and app data are using ${formatBytes(
2520
2753
  storageEstimate.usage
2521
2754
  )} of the ${formatBytes(
2522
2755
  storageEstimate.quota
2523
2756
  )} this browser allows for the site.` : "Storage usage is not available in this browser." }),
2524
- storagePersistent !== void 0 && /* @__PURE__ */ jsx6("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." }),
2525
- onKeepBrowserData && /* @__PURE__ */ jsx6(
2757
+ storagePersistent !== void 0 && /* @__PURE__ */ jsx7("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." }),
2758
+ onKeepBrowserData && /* @__PURE__ */ jsx7(
2526
2759
  "button",
2527
2760
  {
2528
2761
  type: "button",
@@ -2534,15 +2767,15 @@ function AppMenu({
2534
2767
  )
2535
2768
  ] }),
2536
2769
  onVersioningPreferenceChange && /* @__PURE__ */ jsxs6("fieldset", { className: "db-settings-fieldset", children: [
2537
- /* @__PURE__ */ jsx6("legend", { className: "db-settings-legend", children: "Version history" }),
2770
+ /* @__PURE__ */ jsx7("legend", { className: "db-settings-legend", children: "Version history" }),
2538
2771
  /* @__PURE__ */ jsxs6("p", { className: "db-settings-hint", children: [
2539
2772
  "When on, DocBlocks keeps prior revisions of each document inside a sibling",
2540
2773
  " ",
2541
- /* @__PURE__ */ jsx6("code", { children: "<name>_files/.versions/" }),
2774
+ /* @__PURE__ */ jsx7("code", { children: "<name>_files/.versions/" }),
2542
2775
  " folder. Individual workspaces can override this default in their own settings."
2543
2776
  ] }),
2544
2777
  /* @__PURE__ */ jsxs6("label", { className: "db-settings-radio", children: [
2545
- /* @__PURE__ */ jsx6(
2778
+ /* @__PURE__ */ jsx7(
2546
2779
  "input",
2547
2780
  {
2548
2781
  type: "radio",
@@ -2555,7 +2788,7 @@ function AppMenu({
2555
2788
  "On for all workspaces"
2556
2789
  ] }),
2557
2790
  /* @__PURE__ */ jsxs6("label", { className: "db-settings-radio", children: [
2558
- /* @__PURE__ */ jsx6(
2791
+ /* @__PURE__ */ jsx7(
2559
2792
  "input",
2560
2793
  {
2561
2794
  type: "radio",
@@ -2568,7 +2801,7 @@ function AppMenu({
2568
2801
  "On in browser workspaces, off for local folders"
2569
2802
  ] }),
2570
2803
  /* @__PURE__ */ jsxs6("label", { className: "db-settings-radio", children: [
2571
- /* @__PURE__ */ jsx6(
2804
+ /* @__PURE__ */ jsx7(
2572
2805
  "input",
2573
2806
  {
2574
2807
  type: "radio",
@@ -2584,14 +2817,14 @@ function AppMenu({
2584
2817
  ] }),
2585
2818
  showAbout && /* @__PURE__ */ jsxs6(Dialog, { title: "About DocBlocks", onClose: () => setShowAbout(false), children: [
2586
2819
  /* @__PURE__ */ jsxs6("p", { children: [
2587
- /* @__PURE__ */ jsx6("strong", { children: "DocBlocks" }),
2820
+ /* @__PURE__ */ jsx7("strong", { children: "DocBlocks" }),
2588
2821
  " is a local-first Markdown document editor. Your files stay under your control."
2589
2822
  ] }),
2590
2823
  /* @__PURE__ */ jsxs6("aside", { className: "db-about-beta", children: [
2591
- /* @__PURE__ */ jsx6("strong", { children: "Beta Software." }),
2824
+ /* @__PURE__ */ jsx7("strong", { children: "Beta Software." }),
2592
2825
  " We're still working through initial hiccups and issues. Please bear with us, make sure you keep backups, and",
2593
2826
  " ",
2594
- /* @__PURE__ */ jsx6(
2827
+ /* @__PURE__ */ jsx7(
2595
2828
  "a",
2596
2829
  {
2597
2830
  href: "https://github.com/bendyline/docblocks/issues/new",
@@ -2604,27 +2837,27 @@ function AppMenu({
2604
2837
  "where you find them. Thanks!"
2605
2838
  ] }),
2606
2839
  appVersion && /* @__PURE__ */ jsxs6("p", { className: "db-about-version", children: [
2607
- /* @__PURE__ */ jsx6("span", { children: "Version" }),
2608
- /* @__PURE__ */ jsx6("code", { "aria-label": `DocBlocks version ${appVersion}`, children: appVersion })
2840
+ /* @__PURE__ */ jsx7("span", { children: "Version" }),
2841
+ /* @__PURE__ */ jsx7("code", { "aria-label": `DocBlocks version ${appVersion}`, children: appVersion })
2609
2842
  ] }),
2610
2843
  appBuildDate && /* @__PURE__ */ jsxs6("p", { className: "db-about-version", children: [
2611
- /* @__PURE__ */ jsx6("span", { children: "Build" }),
2612
- /* @__PURE__ */ jsx6("time", { dateTime: appBuildDate, children: appBuildDate })
2844
+ /* @__PURE__ */ jsx7("span", { children: "Build" }),
2845
+ /* @__PURE__ */ jsx7("time", { dateTime: appBuildDate, children: appBuildDate })
2613
2846
  ] }),
2614
2847
  /* @__PURE__ */ jsxs6("p", { children: [
2615
2848
  "Built with",
2616
2849
  " ",
2617
- /* @__PURE__ */ jsx6("a", { href: "https://github.com/bendyline/squisq", target: "_blank", rel: "noopener noreferrer", children: "squisq" }),
2850
+ /* @__PURE__ */ jsx7("a", { href: "https://github.com/bendyline/squisq", target: "_blank", rel: "noopener noreferrer", children: "squisq" }),
2618
2851
  " ",
2619
2852
  "by",
2620
2853
  " ",
2621
- /* @__PURE__ */ jsx6("a", { href: "https://bendyline.com", target: "_blank", rel: "noopener noreferrer", children: "Bendyline" }),
2854
+ /* @__PURE__ */ jsx7("a", { href: "https://bendyline.com", target: "_blank", rel: "noopener noreferrer", children: "Bendyline" }),
2622
2855
  "."
2623
2856
  ] }),
2624
2857
  /* @__PURE__ */ jsxs6("p", { className: "db-dialog-links", children: [
2625
- /* @__PURE__ */ jsx6("a", { href: moreInformationUrl, target: "_blank", rel: "noopener noreferrer", children: "More information..." }),
2626
- /* @__PURE__ */ jsx6("span", { className: "db-dialog-sep", children: "\xB7" }),
2627
- /* @__PURE__ */ jsx6(
2858
+ /* @__PURE__ */ jsx7("a", { href: moreInformationUrl, target: "_blank", rel: "noopener noreferrer", children: "More information..." }),
2859
+ /* @__PURE__ */ jsx7("span", { className: "db-dialog-sep", children: "\xB7" }),
2860
+ /* @__PURE__ */ jsx7(
2628
2861
  "a",
2629
2862
  {
2630
2863
  href: "https://github.com/bendyline/docblocks",
@@ -2633,8 +2866,8 @@ function AppMenu({
2633
2866
  children: "GitHub"
2634
2867
  }
2635
2868
  ),
2636
- /* @__PURE__ */ jsx6("span", { className: "db-dialog-sep", children: "\xB7" }),
2637
- /* @__PURE__ */ jsx6(
2869
+ /* @__PURE__ */ jsx7("span", { className: "db-dialog-sep", children: "\xB7" }),
2870
+ /* @__PURE__ */ jsx7(
2638
2871
  "a",
2639
2872
  {
2640
2873
  href: "https://github.com/bendyline/docblocks/releases",
@@ -2643,8 +2876,8 @@ function AppMenu({
2643
2876
  children: "Release notes"
2644
2877
  }
2645
2878
  ),
2646
- /* @__PURE__ */ jsx6("span", { className: "db-dialog-sep", children: "\xB7" }),
2647
- /* @__PURE__ */ jsx6(
2879
+ /* @__PURE__ */ jsx7("span", { className: "db-dialog-sep", children: "\xB7" }),
2880
+ /* @__PURE__ */ jsx7(
2648
2881
  "a",
2649
2882
  {
2650
2883
  href: "https://github.com/bendyline/docblocks/issues",
@@ -2653,8 +2886,8 @@ function AppMenu({
2653
2886
  children: "Support"
2654
2887
  }
2655
2888
  ),
2656
- /* @__PURE__ */ jsx6("span", { className: "db-dialog-sep", children: "\xB7" }),
2657
- /* @__PURE__ */ jsx6(
2889
+ /* @__PURE__ */ jsx7("span", { className: "db-dialog-sep", children: "\xB7" }),
2890
+ /* @__PURE__ */ jsx7(
2658
2891
  "a",
2659
2892
  {
2660
2893
  href: "https://github.com/bendyline/docblocks/blob/main/LICENSE",
@@ -2663,8 +2896,8 @@ function AppMenu({
2663
2896
  children: "License (MIT)"
2664
2897
  }
2665
2898
  ),
2666
- /* @__PURE__ */ jsx6("span", { className: "db-dialog-sep", children: "\xB7" }),
2667
- /* @__PURE__ */ jsx6(
2899
+ /* @__PURE__ */ jsx7("span", { className: "db-dialog-sep", children: "\xB7" }),
2900
+ /* @__PURE__ */ jsx7(
2668
2901
  "a",
2669
2902
  {
2670
2903
  href: "https://github.com/bendyline/docblocks/blob/main/NOTICE.md",
@@ -2680,7 +2913,7 @@ function AppMenu({
2680
2913
 
2681
2914
  // src/WorkspacePicker/WorkspaceSettingsButton.tsx
2682
2915
  import { useState as useState7, useCallback as useCallback7, useRef as useRef7, useEffect as useEffect7 } from "react";
2683
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2916
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2684
2917
  function WorkspaceSettingsButton({
2685
2918
  onSettings,
2686
2919
  onRename,
@@ -2708,7 +2941,7 @@ function WorkspaceSettingsButton({
2708
2941
  [closeMenu]
2709
2942
  );
2710
2943
  return /* @__PURE__ */ jsxs7("div", { ref: containerRef, className: "db-ws-settings", children: [
2711
- /* @__PURE__ */ jsx7(
2944
+ /* @__PURE__ */ jsx8(
2712
2945
  "button",
2713
2946
  {
2714
2947
  ref: triggerRef,
@@ -2719,7 +2952,7 @@ function WorkspaceSettingsButton({
2719
2952
  "aria-haspopup": "menu",
2720
2953
  "aria-label": "Workspace settings",
2721
2954
  title: "Workspace settings",
2722
- children: /* @__PURE__ */ jsx7(WorkspaceIcon, {})
2955
+ children: /* @__PURE__ */ jsx8(WorkspaceIcon, {})
2723
2956
  }
2724
2957
  ),
2725
2958
  isOpen && /* @__PURE__ */ jsxs7(
@@ -2730,7 +2963,7 @@ function WorkspaceSettingsButton({
2730
2963
  role: "menu",
2731
2964
  onKeyDown: handleMenuKeyDown,
2732
2965
  children: [
2733
- /* @__PURE__ */ jsx7(
2966
+ /* @__PURE__ */ jsx8(
2734
2967
  "button",
2735
2968
  {
2736
2969
  className: "db-ws-settings-item",
@@ -2740,7 +2973,7 @@ function WorkspaceSettingsButton({
2740
2973
  children: "Workspace settings\u2026"
2741
2974
  }
2742
2975
  ),
2743
- /* @__PURE__ */ jsx7(
2976
+ /* @__PURE__ */ jsx8(
2744
2977
  "button",
2745
2978
  {
2746
2979
  className: "db-ws-settings-item",
@@ -2750,7 +2983,7 @@ function WorkspaceSettingsButton({
2750
2983
  children: "Rename workspace"
2751
2984
  }
2752
2985
  ),
2753
- /* @__PURE__ */ jsx7(
2986
+ /* @__PURE__ */ jsx8(
2754
2987
  "button",
2755
2988
  {
2756
2989
  className: "db-ws-settings-item",
@@ -2760,8 +2993,8 @@ function WorkspaceSettingsButton({
2760
2993
  children: "Download workspace"
2761
2994
  }
2762
2995
  ),
2763
- /* @__PURE__ */ jsx7("div", { className: "db-ws-settings-divider" }),
2764
- /* @__PURE__ */ jsx7(
2996
+ /* @__PURE__ */ jsx8("div", { className: "db-ws-settings-divider" }),
2997
+ /* @__PURE__ */ jsx8(
2765
2998
  "button",
2766
2999
  {
2767
3000
  className: "db-ws-settings-item db-ws-settings-item--danger",
@@ -2813,7 +3046,7 @@ function resolveVersioningEnabled(workspace, globalPref) {
2813
3046
  }
2814
3047
 
2815
3048
  // src/WorkspacePicker/WorkspaceSettingsDialog.tsx
2816
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3049
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2817
3050
  var VERSIONING_LABELS = {
2818
3051
  on: "on",
2819
3052
  "browser-only": "on for browser workspaces, off for local folders",
@@ -2834,21 +3067,21 @@ function WorkspaceSettingsDialog({
2834
3067
  const inheritLabel = `Inherit default \u2014 currently ${VERSIONING_LABELS[globalVersioningPreference]} (${inheritedEnabled ? "on" : "off"} for this workspace)`;
2835
3068
  return /* @__PURE__ */ jsxs8(Dialog, { title: "Workspace settings", onClose, children: [
2836
3069
  /* @__PURE__ */ jsxs8("p", { className: "db-settings-hint", children: [
2837
- /* @__PURE__ */ jsx8("strong", { children: workspace.name }),
3070
+ /* @__PURE__ */ jsx9("strong", { children: workspace.name }),
2838
3071
  " \xB7",
2839
3072
  " ",
2840
3073
  isLocal ? "Local folder workspace" : "Browser workspace"
2841
3074
  ] }),
2842
3075
  /* @__PURE__ */ jsxs8("fieldset", { className: "db-settings-fieldset", children: [
2843
- /* @__PURE__ */ jsx8("legend", { className: "db-settings-legend", children: "Version history" }),
3076
+ /* @__PURE__ */ jsx9("legend", { className: "db-settings-legend", children: "Version history" }),
2844
3077
  /* @__PURE__ */ jsxs8("p", { className: "db-settings-hint", children: [
2845
3078
  "Controls whether DocBlocks keeps prior revisions in",
2846
3079
  " ",
2847
- /* @__PURE__ */ jsx8("code", { children: "<name>_files/.versions/" }),
3080
+ /* @__PURE__ */ jsx9("code", { children: "<name>_files/.versions/" }),
2848
3081
  " for documents in this workspace."
2849
3082
  ] }),
2850
3083
  /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
2851
- /* @__PURE__ */ jsx8(
3084
+ /* @__PURE__ */ jsx9(
2852
3085
  "input",
2853
3086
  {
2854
3087
  type: "radio",
@@ -2861,7 +3094,7 @@ function WorkspaceSettingsDialog({
2861
3094
  inheritLabel
2862
3095
  ] }),
2863
3096
  /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
2864
- /* @__PURE__ */ jsx8(
3097
+ /* @__PURE__ */ jsx9(
2865
3098
  "input",
2866
3099
  {
2867
3100
  type: "radio",
@@ -2874,7 +3107,7 @@ function WorkspaceSettingsDialog({
2874
3107
  "On for this workspace"
2875
3108
  ] }),
2876
3109
  /* @__PURE__ */ jsxs8("label", { className: "db-settings-radio", children: [
2877
- /* @__PURE__ */ jsx8(
3110
+ /* @__PURE__ */ jsx9(
2878
3111
  "input",
2879
3112
  {
2880
3113
  type: "radio",
@@ -2891,7 +3124,7 @@ function WorkspaceSettingsDialog({
2891
3124
  }
2892
3125
 
2893
3126
  // src/hooks/useDocumentSession.ts
2894
- import { useState as useState8, useSyncExternalStore } from "react";
3127
+ import { useState as useState8, useSyncExternalStore as useSyncExternalStore2 } from "react";
2895
3128
  import {
2896
3129
  DocumentRecoveryJournal,
2897
3130
  DocumentSession,
@@ -2904,7 +3137,7 @@ function useDocumentSession(autoSaveDelayMs = 500) {
2904
3137
  recoveryJournal: new DocumentRecoveryJournal(getDefaultDocumentRecoveryStorage())
2905
3138
  })
2906
3139
  );
2907
- const snapshot = useSyncExternalStore(
3140
+ const snapshot = useSyncExternalStore2(
2908
3141
  session.subscribe,
2909
3142
  session.getSnapshot,
2910
3143
  session.getSnapshot
@@ -2914,14 +3147,14 @@ function useDocumentSession(autoSaveDelayMs = 500) {
2914
3147
 
2915
3148
  // src/Export/DeferredExportToolbarControls.tsx
2916
3149
  import { lazy, Suspense } from "react";
2917
- import { jsx as jsx9 } from "react/jsx-runtime";
3150
+ import { jsx as jsx10 } from "react/jsx-runtime";
2918
3151
  var ExportToolbarControlsImplementation = lazy(
2919
- () => import("./ExportToolbarControls-773QRE4R.js").then((module) => ({
3152
+ () => import("./ExportToolbarControls-6MO4NX6H.js").then((module) => ({
2920
3153
  default: module.ExportToolbarControls
2921
3154
  }))
2922
3155
  );
2923
3156
  function ExportToolbarControls(props) {
2924
- return /* @__PURE__ */ jsx9(Suspense, { fallback: null, children: /* @__PURE__ */ jsx9(ExportToolbarControlsImplementation, { ...props }) });
3157
+ return /* @__PURE__ */ jsx10(Suspense, { fallback: null, children: /* @__PURE__ */ jsx10(ExportToolbarControlsImplementation, { ...props }) });
2925
3158
  }
2926
3159
 
2927
3160
  // src/Git/useGit.ts
@@ -3586,7 +3819,7 @@ import React2, { useCallback as useCallback11, useEffect as useEffect12, useRef
3586
3819
 
3587
3820
  // src/components/PromptDialog.tsx
3588
3821
  import { useCallback as useCallback10, useEffect as useEffect11, useId as useId2, useRef as useRef10, useState as useState11 } from "react";
3589
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3822
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
3590
3823
  function PromptDialog({ request, onSettle }) {
3591
3824
  const [value, setValue] = useState11(request.initialValue ?? "");
3592
3825
  const inputRef = useRef10(null);
@@ -3599,7 +3832,7 @@ function PromptDialog({ request, onSettle }) {
3599
3832
  useEffect11(() => {
3600
3833
  inputRef.current?.select();
3601
3834
  }, []);
3602
- return /* @__PURE__ */ jsx10(
3835
+ return /* @__PURE__ */ jsx11(
3603
3836
  Dialog,
3604
3837
  {
3605
3838
  title: request.title,
@@ -3607,8 +3840,8 @@ function PromptDialog({ request, onSettle }) {
3607
3840
  initialFocusRef: inputRef,
3608
3841
  closeOnBackdrop: false,
3609
3842
  footer: /* @__PURE__ */ jsxs9(Fragment4, { children: [
3610
- /* @__PURE__ */ jsx10("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
3611
- /* @__PURE__ */ jsx10(
3843
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "db-git-secondary-btn", onClick: cancel, children: "Cancel" }),
3844
+ /* @__PURE__ */ jsx11(
3612
3845
  "button",
3613
3846
  {
3614
3847
  type: "button",
@@ -3620,8 +3853,8 @@ function PromptDialog({ request, onSettle }) {
3620
3853
  )
3621
3854
  ] }),
3622
3855
  children: /* @__PURE__ */ jsxs9("div", { className: "db-git-form-row", children: [
3623
- /* @__PURE__ */ jsx10("label", { className: "db-git-form-label", htmlFor: inputId, children: request.label }),
3624
- /* @__PURE__ */ jsx10(
3856
+ /* @__PURE__ */ jsx11("label", { className: "db-git-form-label", htmlFor: inputId, children: request.label }),
3857
+ /* @__PURE__ */ jsx11(
3625
3858
  "input",
3626
3859
  {
3627
3860
  id: inputId,
@@ -3680,7 +3913,7 @@ import React4, { useCallback as useCallback13, useEffect as useEffect13, useRef
3680
3913
 
3681
3914
  // src/components/ConfirmDialog.tsx
3682
3915
  import { useCallback as useCallback12, useRef as useRef12 } from "react";
3683
- import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3916
+ import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
3684
3917
  function ConfirmDialog({ request, onSettle }) {
3685
3918
  const acknowledge = request.kind === "acknowledge";
3686
3919
  const destructive = request.kind === "confirm" && request.destructive === true;
@@ -3689,15 +3922,15 @@ function ConfirmDialog({ request, onSettle }) {
3689
3922
  const accept = useCallback12(() => onSettle(true), [onSettle]);
3690
3923
  const reject = useCallback12(() => onSettle(false), [onSettle]);
3691
3924
  const close = acknowledge ? accept : reject;
3692
- return /* @__PURE__ */ jsx11(
3925
+ return /* @__PURE__ */ jsx12(
3693
3926
  Dialog,
3694
3927
  {
3695
3928
  title: request.title,
3696
3929
  onClose: close,
3697
3930
  initialFocusRef: destructive ? cancelRef : confirmRef,
3698
3931
  footer: /* @__PURE__ */ jsxs10(Fragment5, { children: [
3699
- !acknowledge && /* @__PURE__ */ jsx11("button", { ref: cancelRef, type: "button", className: "db-git-secondary-btn", onClick: reject, children: request.cancelLabel ?? "Cancel" }),
3700
- /* @__PURE__ */ jsx11(
3932
+ !acknowledge && /* @__PURE__ */ jsx12("button", { ref: cancelRef, type: "button", className: "db-git-secondary-btn", onClick: reject, children: request.cancelLabel ?? "Cancel" }),
3933
+ /* @__PURE__ */ jsx12(
3701
3934
  "button",
3702
3935
  {
3703
3936
  ref: confirmRef,
@@ -3708,7 +3941,7 @@ function ConfirmDialog({ request, onSettle }) {
3708
3941
  }
3709
3942
  )
3710
3943
  ] }),
3711
- children: /* @__PURE__ */ jsx11("p", { className: "db-dialog-message", children: request.message })
3944
+ children: /* @__PURE__ */ jsx12("p", { className: "db-dialog-message", children: request.message })
3712
3945
  }
3713
3946
  );
3714
3947
  }
@@ -3853,6 +4086,7 @@ function loadLastState(storage = localStorage) {
3853
4086
  var WELCOME_GATEWAY_KEY = "docblocks:welcomeGatewayDismissed";
3854
4087
  var SIDEBAR_WIDTH_KEY = "docblocks:sidebarWidth";
3855
4088
  var VIEW_PREFERENCES_KEY = "docblocks:viewPreferences";
4089
+ var FILE_EXPLORER_SORT_MODE_KEY = "docblocks:fileExplorerSortMode";
3856
4090
  var SIDEBAR_WIDTH_DEFAULT = 320;
3857
4091
  var SIDEBAR_WIDTH_MIN = 320;
3858
4092
  var SIDEBAR_WIDTH_MAX = 600;
@@ -3892,6 +4126,19 @@ function saveSidebarWidth(px) {
3892
4126
  } catch {
3893
4127
  }
3894
4128
  }
4129
+ function loadFileExplorerSortMode() {
4130
+ try {
4131
+ return localStorage.getItem(FILE_EXPLORER_SORT_MODE_KEY) === "last-modified" ? "last-modified" : "name";
4132
+ } catch {
4133
+ return "name";
4134
+ }
4135
+ }
4136
+ function saveFileExplorerSortMode(mode) {
4137
+ try {
4138
+ localStorage.setItem(FILE_EXPLORER_SORT_MODE_KEY, mode);
4139
+ } catch {
4140
+ }
4141
+ }
3895
4142
  function loadViewPreferences() {
3896
4143
  try {
3897
4144
  const raw = localStorage.getItem(VIEW_PREFERENCES_KEY);
@@ -3919,7 +4166,7 @@ function saveViewPreferences(preferences) {
3919
4166
 
3920
4167
  // src/DocBlocksShell/UpdateAvailableNotice.tsx
3921
4168
  import { useState as useState14 } from "react";
3922
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4169
+ import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3923
4170
  var UPDATE_PROMPT_ID = "db-update-available-prompt";
3924
4171
  function UpdateAvailableNotice({
3925
4172
  available,
@@ -3928,10 +4175,11 @@ function UpdateAvailableNotice({
3928
4175
  statusBarVisible
3929
4176
  }) {
3930
4177
  const [promptOpen, setPromptOpen] = useState14(false);
4178
+ const [applying, setApplying] = useState14(false);
3931
4179
  if (!available || !onApplyUpdate) return null;
3932
4180
  const promptVisible = promptOpen && !blocked;
3933
4181
  return /* @__PURE__ */ jsxs11(Fragment6, { children: [
3934
- /* @__PURE__ */ jsx12(
4182
+ /* @__PURE__ */ jsx13(
3935
4183
  "button",
3936
4184
  {
3937
4185
  type: "button",
@@ -3952,10 +4200,21 @@ function UpdateAvailableNotice({
3952
4200
  className: `db-update-banner${statusBarVisible ? "" : " db-update-banner--floating"}`,
3953
4201
  role: "alert",
3954
4202
  children: [
3955
- /* @__PURE__ */ jsx12("span", { children: "A new version of DocBlocks is available. Reload to update the editor and site pages." }),
4203
+ /* @__PURE__ */ jsx13("span", { children: "A new version of DocBlocks is available. Reload to update the editor and site pages." }),
3956
4204
  /* @__PURE__ */ jsxs11("div", { className: "db-update-banner-actions", children: [
3957
- /* @__PURE__ */ jsx12("button", { type: "button", onClick: onApplyUpdate, disabled: blocked, children: "Reload" }),
3958
- /* @__PURE__ */ jsx12("button", { type: "button", onClick: () => setPromptOpen(false), children: "Later" })
4205
+ /* @__PURE__ */ jsx13(
4206
+ "button",
4207
+ {
4208
+ type: "button",
4209
+ onClick: () => {
4210
+ setApplying(true);
4211
+ onApplyUpdate();
4212
+ },
4213
+ disabled: blocked || applying,
4214
+ children: applying ? "Updating\u2026" : "Reload"
4215
+ }
4216
+ ),
4217
+ /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setPromptOpen(false), disabled: applying, children: "Later" })
3959
4218
  ] })
3960
4219
  ]
3961
4220
  }
@@ -4338,7 +4597,7 @@ var WELCOME_DOCUMENT_CONTENT = [
4338
4597
  "",
4339
4598
  "## Agents get document tools, not a blank check {[factCard]}",
4340
4599
  "",
4341
- "The local MCP server can inspect, validate, preview, compare, and convert documents. Results remain temporary session artifacts until they are deliberately saved, and filesystem access begins with explicit roots rather than assumed authority.",
4600
+ "The local MCP server converts plain text or Markdown directly and can optionally inspect, preview, or compare documents. Results remain temporary session artifacts until they are deliberately saved, and filesystem access begins with explicit roots rather than assumed authority.",
4342
4601
  "",
4343
4602
  "## Start with three small moves {[list]}",
4344
4603
  "",
@@ -4356,7 +4615,7 @@ var WELCOME_DOCUMENT_CONTENT = [
4356
4615
  ].join("\n");
4357
4616
 
4358
4617
  // src/DocBlocksShell/DocBlocksShell.tsx
4359
- import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
4618
+ import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
4360
4619
  var editorShellModulePromise = null;
4361
4620
  function loadEditorShell() {
4362
4621
  editorShellModulePromise ?? (editorShellModulePromise = (async () => {
@@ -4642,7 +4901,7 @@ function useIsMobile(breakpoint = 768) {
4642
4901
  return isMobile;
4643
4902
  }
4644
4903
  function FolderGlyph() {
4645
- return /* @__PURE__ */ jsx13(
4904
+ return /* @__PURE__ */ jsx14(
4646
4905
  "svg",
4647
4906
  {
4648
4907
  viewBox: "0 0 24 24",
@@ -4651,7 +4910,7 @@ function FolderGlyph() {
4651
4910
  strokeWidth: "1.5",
4652
4911
  strokeLinejoin: "round",
4653
4912
  "aria-hidden": "true",
4654
- children: /* @__PURE__ */ jsx13("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" })
4913
+ children: /* @__PURE__ */ jsx14("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" })
4655
4914
  }
4656
4915
  );
4657
4916
  }
@@ -4666,8 +4925,8 @@ function FileGlyph() {
4666
4925
  strokeLinejoin: "round",
4667
4926
  "aria-hidden": "true",
4668
4927
  children: [
4669
- /* @__PURE__ */ jsx13("path", { d: "M6 3h8l5 5v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" }),
4670
- /* @__PURE__ */ jsx13("path", { d: "M14 3v5h5" })
4928
+ /* @__PURE__ */ jsx14("path", { d: "M6 3h8l5 5v12a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" }),
4929
+ /* @__PURE__ */ jsx14("path", { d: "M14 3v5h5" })
4671
4930
  ]
4672
4931
  }
4673
4932
  );
@@ -5014,6 +5273,11 @@ function DocBlocksShell({
5014
5273
  return pickEmptyDocumentPrompt();
5015
5274
  }, [editorKey]);
5016
5275
  const [explorerKey, setExplorerKey] = useState15(0);
5276
+ const [fileExplorerSortMode, setFileExplorerSortMode] = useState15(loadFileExplorerSortMode);
5277
+ const handleFileExplorerSortModeChange = useCallback15((mode) => {
5278
+ setFileExplorerSortMode(mode);
5279
+ saveFileExplorerSortMode(mode);
5280
+ }, []);
5017
5281
  const [documentLinkEpoch, setDocumentLinkEpoch] = useState15(0);
5018
5282
  const [initialView, setInitialView] = useState15("wysiwyg");
5019
5283
  const [initialSharedMode, setInitialSharedMode] = useState15(null);
@@ -7400,7 +7664,7 @@ function DocBlocksShell({
7400
7664
  setPinnedAvailability,
7401
7665
  transitionAwayFromDocument
7402
7666
  ]);
7403
- return /* @__PURE__ */ jsx13(
7667
+ return /* @__PURE__ */ jsx14(
7404
7668
  "div",
7405
7669
  {
7406
7670
  className: `db-shell${effectiveCompact ? " db-shell--mobile" : ""}`,
@@ -7410,8 +7674,8 @@ function DocBlocksShell({
7410
7674
  children: /* @__PURE__ */ jsxs12(GitContext.Provider, { value: git, children: [
7411
7675
  promptDialog,
7412
7676
  confirmDialog,
7413
- workspaceStartupError && /* @__PURE__ */ jsx13("div", { className: "db-save-toast db-save-toast--error", role: "alert", "aria-live": "assertive", children: workspaceStartupError }),
7414
- saveToast && /* @__PURE__ */ jsx13(
7677
+ workspaceStartupError && /* @__PURE__ */ jsx14("div", { className: "db-save-toast db-save-toast--error", role: "alert", "aria-live": "assertive", children: workspaceStartupError }),
7678
+ saveToast && /* @__PURE__ */ jsx14(
7415
7679
  "div",
7416
7680
  {
7417
7681
  className: "db-save-toast db-save-toast--" + saveToast.kind,
@@ -7420,22 +7684,22 @@ function DocBlocksShell({
7420
7684
  children: saveToast.message
7421
7685
  }
7422
7686
  ),
7423
- offlineReadyToast && !saveToast && /* @__PURE__ */ jsx13("div", { className: "db-save-toast db-save-toast--success", role: "status", "aria-live": "polite", children: "DocBlocks is ready to work offline." }),
7687
+ offlineReadyToast && !saveToast && /* @__PURE__ */ jsx14("div", { className: "db-save-toast db-save-toast--success", role: "status", "aria-live": "polite", children: "DocBlocks is ready to work offline." }),
7424
7688
  documentSnapshot.conflict && /* @__PURE__ */ jsxs12("div", { className: "db-document-conflict", role: "alert", children: [
7425
- /* @__PURE__ */ jsx13("span", { children: "This document changed outside DocBlocks. Your unsaved version is still intact." }),
7689
+ /* @__PURE__ */ jsx14("span", { children: "This document changed outside DocBlocks. Your unsaved version is still intact." }),
7426
7690
  /* @__PURE__ */ jsxs12("div", { className: "db-document-conflict-actions", children: [
7427
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => void handleKeepLocalDocument(), children: "Keep mine" }),
7428
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => void handleUseExternalDocument(), children: "Reload external" })
7691
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => void handleKeepLocalDocument(), children: "Keep mine" }),
7692
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => void handleUseExternalDocument(), children: "Reload external" })
7429
7693
  ] })
7430
7694
  ] }),
7431
7695
  storageFull && !documentSnapshot.conflict && /* @__PURE__ */ jsxs12("div", { className: "db-storage-full-banner", role: "alert", children: [
7432
- /* @__PURE__ */ jsx13("span", { children: "Browser storage is full -- changes can\u2019t be saved. Free up space or back up your work now." }),
7696
+ /* @__PURE__ */ jsx14("span", { children: "Browser storage is full -- changes can\u2019t be saved. Free up space or back up your work now." }),
7433
7697
  /* @__PURE__ */ jsxs12("div", { className: "db-storage-full-banner-actions", children: [
7434
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => void handleDownloadAllWorkspaces(), children: "Download all workspaces" }),
7435
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => setStorageFull(false), children: "Dismiss" })
7698
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => void handleDownloadAllWorkspaces(), children: "Download all workspaces" }),
7699
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => setStorageFull(false), children: "Dismiss" })
7436
7700
  ] })
7437
7701
  ] }),
7438
- workspaceSettingsOpen && activeWorkspaceDescriptor && /* @__PURE__ */ jsx13(
7702
+ workspaceSettingsOpen && activeWorkspaceDescriptor && /* @__PURE__ */ jsx14(
7439
7703
  WorkspaceSettingsDialog,
7440
7704
  {
7441
7705
  workspace: activeWorkspaceDescriptor,
@@ -7454,7 +7718,7 @@ function DocBlocksShell({
7454
7718
  style: effectiveCompact ? void 0 : { width: `${sidebarWidth}px` },
7455
7719
  children: [
7456
7720
  /* @__PURE__ */ jsxs12("div", { className: "db-shell-sidebar-header", children: [
7457
- /* @__PURE__ */ jsx13(
7721
+ /* @__PURE__ */ jsx14(
7458
7722
  AppMenu,
7459
7723
  {
7460
7724
  logoUrl,
@@ -7475,7 +7739,7 @@ function DocBlocksShell({
7475
7739
  appBuildDate
7476
7740
  }
7477
7741
  ),
7478
- /* @__PURE__ */ jsx13(
7742
+ /* @__PURE__ */ jsx14(
7479
7743
  WorkspacePicker,
7480
7744
  {
7481
7745
  activeWorkspaceId,
@@ -7485,7 +7749,7 @@ function DocBlocksShell({
7485
7749
  onCloneRepository: git.available ? () => git.openDialog({ kind: "clone" }) : void 0
7486
7750
  }
7487
7751
  ),
7488
- /* @__PURE__ */ jsx13(
7752
+ /* @__PURE__ */ jsx14(
7489
7753
  WorkspaceSettingsButton,
7490
7754
  {
7491
7755
  onSettings: handleOpenWorkspaceSettings,
@@ -7494,31 +7758,34 @@ function DocBlocksShell({
7494
7758
  onRemove: handleRemoveWorkspace
7495
7759
  }
7496
7760
  ),
7497
- compactLayout && !isMobile && /* @__PURE__ */ jsx13(
7761
+ compactLayout && !isMobile && /* @__PURE__ */ jsx14(
7498
7762
  "button",
7499
7763
  {
7500
7764
  className: "db-restore-split",
7501
7765
  onClick: () => setCompactLayout(false),
7502
7766
  "aria-label": "Restore split view",
7503
7767
  title: "Restore split view",
7504
- children: /* @__PURE__ */ jsx13(SplitViewIcon, {})
7768
+ children: /* @__PURE__ */ jsx14(SplitViewIcon, {})
7505
7769
  }
7506
7770
  ),
7507
- /* @__PURE__ */ jsx13("span", { className: "db-window-drag-grip", "aria-hidden": true })
7771
+ /* @__PURE__ */ jsx14("span", { className: "db-window-drag-grip", "aria-hidden": true })
7508
7772
  ] }),
7509
- git.available && /* @__PURE__ */ jsx13(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx13(
7773
+ git.available && /* @__PURE__ */ jsx14(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx14(
7510
7774
  GitUI,
7511
7775
  {
7512
7776
  onOpenFile: (path) => void handleSelect(path, "file"),
7513
7777
  onWorkspaceCloned: handleWorkspaceCloned
7514
7778
  }
7515
7779
  ) }),
7516
- /* @__PURE__ */ jsx13(
7780
+ /* @__PURE__ */ jsx14(
7517
7781
  FileExplorer,
7518
7782
  {
7519
7783
  provider,
7784
+ metadataRefreshKey: `${documentSnapshot.targetKey ?? ""}:${documentSnapshot.persistedRevision}`,
7520
7785
  activeWorkspaceId,
7521
7786
  activeFilePath: selectedFile,
7787
+ sortMode: fileExplorerSortMode,
7788
+ onSortModeChange: handleFileExplorerSortModeChange,
7522
7789
  pinnedDocuments: pinnedDocumentItems,
7523
7790
  pinnedPaths: activeWorkspacePinnedPaths,
7524
7791
  onPinnedDocumentSelect: (document2) => void handlePinnedDocumentSelect(document2),
@@ -7542,11 +7809,11 @@ function DocBlocksShell({
7542
7809
  className: "db-mobile-first-run",
7543
7810
  "aria-labelledby": "db-mobile-first-run-title",
7544
7811
  children: [
7545
- /* @__PURE__ */ jsx13("p", { className: "db-mobile-first-run-eyebrow", children: "Local-first Markdown editor" }),
7546
- /* @__PURE__ */ jsx13("h1", { id: "db-mobile-first-run-title", children: "Welcome to DocBlocks" }),
7547
- /* @__PURE__ */ jsx13("p", { children: "Write visually, keep plain Markdown underneath, and export the same document in useful formats. Your browser workspace stays on this device." }),
7812
+ /* @__PURE__ */ jsx14("p", { className: "db-mobile-first-run-eyebrow", children: "Local-first Markdown editor" }),
7813
+ /* @__PURE__ */ jsx14("h1", { id: "db-mobile-first-run-title", children: "Welcome to DocBlocks" }),
7814
+ /* @__PURE__ */ jsx14("p", { children: "Write visually, keep plain Markdown underneath, and export the same document in useful formats. Your browser workspace stays on this device." }),
7548
7815
  /* @__PURE__ */ jsxs12("div", { className: "db-mobile-first-run-actions", children: [
7549
- /* @__PURE__ */ jsx13(
7816
+ /* @__PURE__ */ jsx14(
7550
7817
  "button",
7551
7818
  {
7552
7819
  type: "button",
@@ -7557,7 +7824,7 @@ function DocBlocksShell({
7557
7824
  children: "Tour the welcome document"
7558
7825
  }
7559
7826
  ),
7560
- /* @__PURE__ */ jsx13(
7827
+ /* @__PURE__ */ jsx14(
7561
7828
  "button",
7562
7829
  {
7563
7830
  type: "button",
@@ -7571,14 +7838,14 @@ function DocBlocksShell({
7571
7838
  }
7572
7839
  ),
7573
7840
  /* @__PURE__ */ jsxs12("div", { className: "db-shell-sidebar-footer", children: [
7574
- /* @__PURE__ */ jsx13("a", { href: "https://docblocks.com/docs/", target: "_blank", rel: "noopener noreferrer", children: "Docs" }),
7575
- /* @__PURE__ */ jsx13("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7576
- /* @__PURE__ */ jsx13("a", { href: "https://docblocks.com/terms/", target: "_blank", rel: "noopener noreferrer", children: "Terms" }),
7577
- /* @__PURE__ */ jsx13("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7578
- /* @__PURE__ */ jsx13("a", { href: issueReportUrl, target: "_blank", rel: "noopener noreferrer", children: "Report issue" }),
7841
+ /* @__PURE__ */ jsx14("a", { href: "https://docblocks.com/docs/", target: "_blank", rel: "noopener noreferrer", children: "Docs" }),
7842
+ /* @__PURE__ */ jsx14("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7843
+ /* @__PURE__ */ jsx14("a", { href: "https://docblocks.com/terms/", target: "_blank", rel: "noopener noreferrer", children: "Terms" }),
7844
+ /* @__PURE__ */ jsx14("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7845
+ /* @__PURE__ */ jsx14("a", { href: issueReportUrl, target: "_blank", rel: "noopener noreferrer", children: "Report issue" }),
7579
7846
  showBrowserStorageWarning && /* @__PURE__ */ jsxs12(Fragment7, { children: [
7580
- /* @__PURE__ */ jsx13("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7581
- /* @__PURE__ */ jsx13(
7847
+ /* @__PURE__ */ jsx14("span", { className: "db-shell-sidebar-footer-separator", "aria-hidden": "true", children: "\u2022" }),
7848
+ /* @__PURE__ */ jsx14(
7582
7849
  "button",
7583
7850
  {
7584
7851
  type: "button",
@@ -7593,7 +7860,7 @@ function DocBlocksShell({
7593
7860
  ]
7594
7861
  }
7595
7862
  ),
7596
- !effectiveCompact && /* @__PURE__ */ jsx13(
7863
+ !effectiveCompact && /* @__PURE__ */ jsx14(
7597
7864
  "div",
7598
7865
  {
7599
7866
  className: "db-shell-sidebar-resizer",
@@ -7617,11 +7884,11 @@ function DocBlocksShell({
7617
7884
  },
7618
7885
  children: [
7619
7886
  selectedFile && mediaProvider ? /* @__PURE__ */ jsxs12(Fragment7, { children: [
7620
- /* @__PURE__ */ jsx13(
7887
+ /* @__PURE__ */ jsx14(
7621
7888
  Suspense2,
7622
7889
  {
7623
- fallback: /* @__PURE__ */ jsx13("div", { className: "db-shell-empty", role: "status", children: "Loading your document\u2026" }),
7624
- children: /* @__PURE__ */ jsx13(
7890
+ fallback: /* @__PURE__ */ jsx14("div", { className: "db-shell-empty", role: "status", children: "Loading your document\u2026" }),
7891
+ children: /* @__PURE__ */ jsx14(
7625
7892
  EditorShell,
7626
7893
  {
7627
7894
  initialMarkdown: editorContent,
@@ -7647,32 +7914,33 @@ function DocBlocksShell({
7647
7914
  versioningAutoSaveIdleMs,
7648
7915
  onSaveVersion,
7649
7916
  statusBarSlotRight,
7650
- toolbarSlotLeft: effectiveCompact ? /* @__PURE__ */ jsx13(
7917
+ toolbarSlotLeft: effectiveCompact ? /* @__PURE__ */ jsx14(
7651
7918
  "button",
7652
7919
  {
7653
7920
  className: "db-mobile-back",
7654
7921
  onClick: () => setMobileShowEditor(false),
7655
7922
  "aria-label": "Show file list",
7656
- children: /* @__PURE__ */ jsx13("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx13(FolderGlyph, {}) })
7923
+ children: /* @__PURE__ */ jsx14("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx14(FolderGlyph, {}) })
7657
7924
  }
7658
7925
  ) : void 0,
7659
7926
  toolbarSlotRight: /* @__PURE__ */ jsxs12(Fragment7, { children: [
7660
- compactLayout && !isMobile && /* @__PURE__ */ jsx13(
7927
+ compactLayout && !isMobile && /* @__PURE__ */ jsx14(
7661
7928
  "button",
7662
7929
  {
7663
7930
  className: "db-restore-split",
7664
7931
  onClick: () => setCompactLayout(false),
7665
7932
  "aria-label": "Restore split view",
7666
7933
  title: "Restore split view",
7667
- children: /* @__PURE__ */ jsx13(SplitViewIcon, {})
7934
+ children: /* @__PURE__ */ jsx14(SplitViewIcon, {})
7668
7935
  }
7669
7936
  ),
7670
- git.repo && /* @__PURE__ */ jsx13(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx13(GitToolbarControl, { selectedFile }) }),
7671
- /* @__PURE__ */ jsx13(
7937
+ git.repo && /* @__PURE__ */ jsx14(Suspense2, { fallback: null, children: /* @__PURE__ */ jsx14(GitToolbarControl, { selectedFile }) }),
7938
+ /* @__PURE__ */ jsx14(
7672
7939
  ExportToolbarControls,
7673
7940
  {
7674
7941
  selectedFile,
7675
7942
  mediaContainer: mediaContainerRef.current,
7943
+ mediaProvider,
7676
7944
  destinationAdapter: exportDestinationAdapter,
7677
7945
  colorScheme: resolvedTheme,
7678
7946
  videoExportPalette: DOCBLOCKS_VIDEO_EXPORT_PALETTE,
@@ -7689,12 +7957,12 @@ function DocBlocksShell({
7689
7957
  showWelcomeGateway && !isMobile && /* @__PURE__ */ jsxs12("div", { className: "db-welcome-gateway", role: "note", "aria-label": "Welcome tip", children: [
7690
7958
  /* @__PURE__ */ jsxs12("span", { className: "db-welcome-gateway-text", children: [
7691
7959
  "You\u2019re watching this welcome doc in ",
7692
- /* @__PURE__ */ jsx13("strong", { children: "Slideshow" }),
7960
+ /* @__PURE__ */ jsx14("strong", { children: "Slideshow" }),
7693
7961
  " ",
7694
7962
  "view\u2014 it\u2019s a regular markdown file, and so is everything you\u2019ll write."
7695
7963
  ] }),
7696
- /* @__PURE__ */ jsx13("button", { className: "db-welcome-gateway-cta", onClick: handleStartWriting, children: "Start writing" }),
7697
- /* @__PURE__ */ jsx13(
7964
+ /* @__PURE__ */ jsx14("button", { className: "db-welcome-gateway-cta", onClick: handleStartWriting, children: "Start writing" }),
7965
+ /* @__PURE__ */ jsx14(
7698
7966
  "button",
7699
7967
  {
7700
7968
  className: "db-welcome-gateway-dismiss",
@@ -7707,20 +7975,20 @@ function DocBlocksShell({
7707
7975
  ] })
7708
7976
  ] }) : selectedFolder ? /* @__PURE__ */ jsxs12("div", { className: "db-folder-view", children: [
7709
7977
  effectiveCompact && /* @__PURE__ */ jsxs12("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
7710
- /* @__PURE__ */ jsx13("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx13(FolderGlyph, {}) }),
7978
+ /* @__PURE__ */ jsx14("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx14(FolderGlyph, {}) }),
7711
7979
  "Back to files"
7712
7980
  ] }),
7713
7981
  /* @__PURE__ */ jsxs12("div", { className: "db-folder-view-header", children: [
7714
- /* @__PURE__ */ jsx13("span", { className: "db-folder-view-icon", children: /* @__PURE__ */ jsx13(FolderGlyph, {}) }),
7715
- /* @__PURE__ */ jsx13("span", { className: "db-folder-view-path", children: selectedFolder })
7982
+ /* @__PURE__ */ jsx14("span", { className: "db-folder-view-icon", children: /* @__PURE__ */ jsx14(FolderGlyph, {}) }),
7983
+ /* @__PURE__ */ jsx14("span", { className: "db-folder-view-path", children: selectedFolder })
7716
7984
  ] }),
7717
- visibleFolderEntries.length === 0 ? /* @__PURE__ */ jsx13("p", { className: "db-folder-view-empty", children: "This folder is empty." }) : /* @__PURE__ */ jsx13("ul", { className: "db-folder-view-list", children: visibleFolderEntries.map((entry) => /* @__PURE__ */ jsxs12(
7985
+ visibleFolderEntries.length === 0 ? /* @__PURE__ */ jsx14("p", { className: "db-folder-view-empty", children: "This folder is empty." }) : /* @__PURE__ */ jsx14("ul", { className: "db-folder-view-list", children: visibleFolderEntries.map((entry) => /* @__PURE__ */ jsxs12(
7718
7986
  "li",
7719
7987
  {
7720
7988
  className: "db-folder-view-item",
7721
7989
  onClick: () => handleSelect(entry.path, entry.kind),
7722
7990
  children: [
7723
- /* @__PURE__ */ jsx13("span", { className: "db-folder-view-item-icon", children: entry.kind === "directory" ? /* @__PURE__ */ jsx13(FolderGlyph, {}) : /* @__PURE__ */ jsx13(FileGlyph, {}) }),
7991
+ /* @__PURE__ */ jsx14("span", { className: "db-folder-view-item-icon", children: entry.kind === "directory" ? /* @__PURE__ */ jsx14(FolderGlyph, {}) : /* @__PURE__ */ jsx14(FileGlyph, {}) }),
7724
7992
  entry.name
7725
7993
  ]
7726
7994
  },
@@ -7728,16 +7996,16 @@ function DocBlocksShell({
7728
7996
  )) })
7729
7997
  ] }) : /* @__PURE__ */ jsxs12("div", { className: "db-shell-empty db-shell-empty--workspace", children: [
7730
7998
  effectiveCompact && /* @__PURE__ */ jsxs12("button", { className: "db-mobile-back", onClick: () => setMobileShowEditor(false), children: [
7731
- /* @__PURE__ */ jsx13("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx13(FolderGlyph, {}) }),
7999
+ /* @__PURE__ */ jsx14("span", { className: "db-mobile-files-icon", children: /* @__PURE__ */ jsx14(FolderGlyph, {}) }),
7732
8000
  "Back to files"
7733
8001
  ] }),
7734
8002
  /* @__PURE__ */ jsxs12("div", { className: "db-workspace-empty-content", children: [
7735
- /* @__PURE__ */ jsx13("span", { className: "db-workspace-empty-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx13(FileGlyph, {}) }),
7736
- /* @__PURE__ */ jsx13("h1", { children: activeWorkspaceDescriptor?.name ?? "Workspace" }),
7737
- /* @__PURE__ */ jsx13("p", { children: "Choose a Markdown document from the sidebar, or create a new one." }),
8003
+ /* @__PURE__ */ jsx14("span", { className: "db-workspace-empty-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx14(FileGlyph, {}) }),
8004
+ /* @__PURE__ */ jsx14("h1", { children: activeWorkspaceDescriptor?.name ?? "Workspace" }),
8005
+ /* @__PURE__ */ jsx14("p", { children: "Choose a Markdown document from the sidebar, or create a new one." }),
7738
8006
  /* @__PURE__ */ jsxs12("div", { className: "db-workspace-empty-actions", children: [
7739
- /* @__PURE__ */ jsx13("button", { type: "button", onClick: () => void handleNewFile(), children: "New document" }),
7740
- /* @__PURE__ */ jsx13(
8007
+ /* @__PURE__ */ jsx14("button", { type: "button", onClick: () => void handleNewFile(), children: "New document" }),
8008
+ /* @__PURE__ */ jsx14(
7741
8009
  "button",
7742
8010
  {
7743
8011
  type: "button",
@@ -7746,7 +8014,7 @@ function DocBlocksShell({
7746
8014
  children: "New folder"
7747
8015
  }
7748
8016
  ),
7749
- (isElectronHost3() || typeof globalThis.showDirectoryPicker === "function") && /* @__PURE__ */ jsx13(
8017
+ (isElectronHost3() || typeof globalThis.showDirectoryPicker === "function") && /* @__PURE__ */ jsx14(
7750
8018
  "button",
7751
8019
  {
7752
8020
  type: "button",
@@ -7756,10 +8024,10 @@ function DocBlocksShell({
7756
8024
  }
7757
8025
  )
7758
8026
  ] }),
7759
- /* @__PURE__ */ jsx13("p", { className: "db-workspace-empty-hint", children: "Browser workspaces stay on this device. Use the sidebar backup action to keep a portable copy." })
8027
+ /* @__PURE__ */ jsx14("p", { className: "db-workspace-empty-hint", children: "Browser workspaces stay on this device. Use the sidebar backup action to keep a portable copy." })
7760
8028
  ] })
7761
8029
  ] }),
7762
- /* @__PURE__ */ jsx13(
8030
+ /* @__PURE__ */ jsx14(
7763
8031
  UpdateAvailableNotice,
7764
8032
  {
7765
8033
  available: updateAvailable,