@tutti-os/workspace-file-manager 0.0.230 → 0.0.232

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
@@ -21,24 +21,24 @@ import {
21
21
  splitWorkspaceFileName,
22
22
  workspaceFileSearchEntryToEntry,
23
23
  writeWorkspaceFileManagerArrangeMode
24
- } from "./chunk-KCYTFKPY.js";
24
+ } from "./chunk-GYODJICC.js";
25
25
  import {
26
26
  createWorkspaceFileManagerI18nRuntime,
27
27
  resolveRevealInFolderLabel,
28
28
  workspaceFileManagerI18nNamespace,
29
29
  workspaceFileManagerI18nResources
30
- } from "./chunk-EI4ICYY4.js";
30
+ } from "./chunk-MWVVTZD5.js";
31
31
 
32
32
  // src/ui/WorkspaceFileManager.tsx
33
33
  import {
34
- useCallback as useCallback6,
34
+ useCallback as useCallback7,
35
35
  useEffect as useEffect7,
36
36
  useLayoutEffect as useLayoutEffect5,
37
- useMemo as useMemo3,
37
+ useMemo as useMemo5,
38
38
  useRef as useRef7,
39
39
  useState as useState8
40
40
  } from "react";
41
- import { cn as cn7 } from "@tutti-os/ui-system";
41
+ import { cn as cn8 } from "@tutti-os/ui-system";
42
42
 
43
43
  // src/ui/WorkspaceFileManagerContextMenuContainer.tsx
44
44
  import { useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState as useState2 } from "react";
@@ -1127,6 +1127,12 @@ function WorkspaceFileManagerUnsupportedDialog({
1127
1127
  );
1128
1128
  }
1129
1129
 
1130
+ // src/ui/WorkspaceFileManagerPanelsContainer.tsx
1131
+ import {
1132
+ useCallback as useCallback4,
1133
+ useMemo as useMemo4
1134
+ } from "react";
1135
+
1130
1136
  // src/ui/WorkspaceFileManagerPanels.tsx
1131
1137
  import {
1132
1138
  ArrowRightIcon as ArrowRightIcon2,
@@ -1135,11 +1141,51 @@ import {
1135
1141
  LoadingIcon as LoadingIcon2,
1136
1142
  ScrollArea,
1137
1143
  VideoFileIcon as VideoFileIcon2,
1138
- cn as cn4
1144
+ cn as cn5
1139
1145
  } from "@tutti-os/ui-system";
1140
1146
  import { resolveWorkspaceFileVisualKind as resolveWorkspaceFileVisualKind2 } from "@tutti-os/workspace-file-preview";
1141
1147
  import { WorkspaceFilePreviewSurface as SharedWorkspaceFilePreviewSurface } from "@tutti-os/workspace-file-preview/react";
1148
+
1149
+ // src/ui/WorkspaceFileManagerPreviewActionBar.tsx
1150
+ import { Button as Button2, cn as cn2 } from "@tutti-os/ui-system";
1151
+ import { jsx as jsx4 } from "react/jsx-runtime";
1152
+ function WorkspaceFileManagerPreviewActionBar({
1153
+ actions,
1154
+ className,
1155
+ label
1156
+ }) {
1157
+ if (actions.length === 0) {
1158
+ return null;
1159
+ }
1160
+ return /* @__PURE__ */ jsx4(
1161
+ "div",
1162
+ {
1163
+ "aria-label": label,
1164
+ className: cn2("flex flex-none items-center gap-1", className),
1165
+ role: "group",
1166
+ children: actions.map((action) => /* @__PURE__ */ jsx4(
1167
+ Button2,
1168
+ {
1169
+ "aria-label": action.label,
1170
+ className: "size-7 min-w-7 rounded-sm p-0 text-[var(--text-primary)]",
1171
+ "data-testid": `workspace-file-manager-preview-action-${action.id}`,
1172
+ disabled: action.disabled,
1173
+ size: "icon-sm",
1174
+ title: action.label,
1175
+ type: "button",
1176
+ variant: "ghost",
1177
+ onClick: action.onSelect,
1178
+ children: action.icon
1179
+ },
1180
+ action.id
1181
+ ))
1182
+ }
1183
+ );
1184
+ }
1185
+
1186
+ // src/ui/WorkspaceFileManagerPanels.tsx
1142
1187
  import {
1188
+ memo,
1143
1189
  useCallback as useCallback2,
1144
1190
  useEffect as useEffect4,
1145
1191
  useLayoutEffect as useLayoutEffect4,
@@ -1149,7 +1195,7 @@ import {
1149
1195
  } from "react";
1150
1196
 
1151
1197
  // src/ui/WorkspaceFileManagerIconGrid.tsx
1152
- import { cn as cn3 } from "@tutti-os/ui-system";
1198
+ import { cn as cn4 } from "@tutti-os/ui-system";
1153
1199
  import { useLayoutEffect as useLayoutEffect3, useRef as useRef4 } from "react";
1154
1200
 
1155
1201
  // src/ui/WorkspaceFileEntryIcon.tsx
@@ -1161,14 +1207,14 @@ import {
1161
1207
  ImageFileIcon,
1162
1208
  LoadingIcon,
1163
1209
  VideoFileIcon,
1164
- cn as cn2
1210
+ cn as cn3
1165
1211
  } from "@tutti-os/ui-system";
1166
1212
  import { useEffect as useEffect3, useRef as useRef3 } from "react";
1167
1213
  import {
1168
1214
  resolveWorkspaceFileExtension,
1169
1215
  resolveWorkspaceFileVisualKind
1170
1216
  } from "@tutti-os/workspace-file-preview";
1171
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1217
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1172
1218
  function WorkspaceFileEntryIcon({
1173
1219
  entry,
1174
1220
  frameClassName,
@@ -1245,25 +1291,25 @@ function WorkspaceFileEntryIcon({
1245
1291
  reportViewportLeave();
1246
1292
  };
1247
1293
  }, [cacheKey, entry, isEnteringDirectory, onViewportLeave, onViewportEnter]);
1248
- return /* @__PURE__ */ jsx4(
1294
+ return /* @__PURE__ */ jsx5(
1249
1295
  "span",
1250
1296
  {
1251
1297
  ref: frameRef,
1252
- className: cn2(
1298
+ className: cn3(
1253
1299
  "grid flex-none place-items-center overflow-hidden",
1254
1300
  frameClassName,
1255
1301
  isEnteringDirectory ? "text-[var(--text-tertiary)]" : entryIconColorClassName(visualKind, isAppBundle)
1256
1302
  ),
1257
- children: isEnteringDirectory ? /* @__PURE__ */ jsx4(
1303
+ children: isEnteringDirectory ? /* @__PURE__ */ jsx5(
1258
1304
  LoadingIcon,
1259
1305
  {
1260
- className: cn2(loadingIconClassName ?? iconClassName, "animate-spin")
1306
+ className: cn3(loadingIconClassName ?? iconClassName, "animate-spin")
1261
1307
  }
1262
- ) : iconUrl ? /* @__PURE__ */ jsx4(
1308
+ ) : iconUrl ? /* @__PURE__ */ jsx5(
1263
1309
  "img",
1264
1310
  {
1265
1311
  alt: "",
1266
- className: cn2(
1312
+ className: cn3(
1267
1313
  iconClassName,
1268
1314
  visualKind === "image" ? "rounded-[6px] border border-[var(--border-1)] bg-[var(--transparency-block)] object-contain" : "rounded-[4px] object-contain"
1269
1315
  ),
@@ -1272,7 +1318,7 @@ function WorkspaceFileEntryIcon({
1272
1318
  loading: "lazy",
1273
1319
  src: iconUrl
1274
1320
  }
1275
- ) : /* @__PURE__ */ jsx4(
1321
+ ) : /* @__PURE__ */ jsx5(
1276
1322
  DefaultEntryIcon,
1277
1323
  {
1278
1324
  entry,
@@ -1303,13 +1349,13 @@ function DefaultEntryIcon({
1303
1349
  }) {
1304
1350
  const vectorIconClassName = vectorFallbackIconClassName(iconClassName);
1305
1351
  if (isWorkspaceApplicationBundle(entry)) {
1306
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1352
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1307
1353
  }
1308
1354
  if (shouldUseWorkspaceFileArchiveIcon(entry)) {
1309
- return /* @__PURE__ */ jsx4(WorkspaceArchiveFallbackIcon, { className: iconClassName });
1355
+ return /* @__PURE__ */ jsx5(WorkspaceArchiveFallbackIcon, { className: iconClassName });
1310
1356
  }
1311
1357
  if (shouldUseWorkspaceFileExtensionDocumentIcon(entry)) {
1312
- return /* @__PURE__ */ jsx4(
1358
+ return /* @__PURE__ */ jsx5(
1313
1359
  ExtensionDocumentIcon,
1314
1360
  {
1315
1361
  entry,
@@ -1319,20 +1365,20 @@ function DefaultEntryIcon({
1319
1365
  }
1320
1366
  switch (visualKind) {
1321
1367
  case "directory":
1322
- return /* @__PURE__ */ jsx4(WorkspaceFolderFallbackIcon, { className: iconClassName });
1368
+ return /* @__PURE__ */ jsx5(WorkspaceFolderFallbackIcon, { className: iconClassName });
1323
1369
  case "image":
1324
- return /* @__PURE__ */ jsx4(WorkspaceImageFallbackIcon, { className: iconClassName });
1370
+ return /* @__PURE__ */ jsx5(WorkspaceImageFallbackIcon, { className: iconClassName });
1325
1371
  case "video":
1326
- return /* @__PURE__ */ jsx4(VideoFileIcon, { className: vectorIconClassName });
1372
+ return /* @__PURE__ */ jsx5(VideoFileIcon, { className: vectorIconClassName });
1327
1373
  case "markdown":
1328
1374
  case "document":
1329
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1375
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1330
1376
  case "code":
1331
- return /* @__PURE__ */ jsx4(FileCodeIcon, { className: vectorIconClassName });
1377
+ return /* @__PURE__ */ jsx5(FileCodeIcon, { className: vectorIconClassName });
1332
1378
  case "binary":
1333
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1379
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1334
1380
  default:
1335
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1381
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1336
1382
  }
1337
1383
  }
1338
1384
  function vectorFallbackIconClassName(iconClassName) {
@@ -1341,17 +1387,17 @@ function vectorFallbackIconClassName(iconClassName) {
1341
1387
  function WorkspaceFolderFallbackIcon({
1342
1388
  className
1343
1389
  }) {
1344
- return /* @__PURE__ */ jsx4(FolderFilledIcon, { className: vectorFallbackIconClassName(className) });
1390
+ return /* @__PURE__ */ jsx5(FolderFilledIcon, { className: vectorFallbackIconClassName(className) });
1345
1391
  }
1346
1392
  function WorkspaceArchiveFallbackIcon({
1347
1393
  className
1348
1394
  }) {
1349
- return /* @__PURE__ */ jsx4(FileArchiveIcon, { className: vectorFallbackIconClassName(className) });
1395
+ return /* @__PURE__ */ jsx5(FileArchiveIcon, { className: vectorFallbackIconClassName(className) });
1350
1396
  }
1351
1397
  function WorkspaceImageFallbackIcon({
1352
1398
  className
1353
1399
  }) {
1354
- return /* @__PURE__ */ jsx4(ImageFileIcon, { className: vectorFallbackIconClassName(className) });
1400
+ return /* @__PURE__ */ jsx5(ImageFileIcon, { className: vectorFallbackIconClassName(className) });
1355
1401
  }
1356
1402
  function ExtensionDocumentIcon({
1357
1403
  entry,
@@ -1363,11 +1409,11 @@ function ExtensionDocumentIcon({
1363
1409
  "span",
1364
1410
  {
1365
1411
  "aria-hidden": "true",
1366
- className: cn2("relative inline-block overflow-visible", iconClassName),
1412
+ className: cn3("relative inline-block overflow-visible", iconClassName),
1367
1413
  children: [
1368
- /* @__PURE__ */ jsx4("span", { className: "absolute inset-[5%] rounded-[6px] border border-black/10 bg-linear-to-br from-white via-[#f8f8f8] to-[#ececec]" }),
1369
- /* @__PURE__ */ jsx4("span", { className: "absolute top-[5%] right-[5%] h-[28%] w-[28%] overflow-hidden rounded-tr-[6px]", children: /* @__PURE__ */ jsx4("span", { className: "absolute top-0 right-0 h-full w-full origin-top-right -skew-x-3 rounded-bl-[4px] border-b border-l border-black/10 bg-linear-to-br from-white to-[#d9d9d9]" }) }),
1370
- showExtension ? /* @__PURE__ */ jsx4("span", { className: "absolute right-[12%] bottom-[14%] left-[12%] truncate text-center text-[10px] leading-none font-semibold tracking-wide text-[#7a7a7a]", children: extension }) : null
1414
+ /* @__PURE__ */ jsx5("span", { className: "absolute inset-[5%] rounded-[6px] border border-black/10 bg-linear-to-br from-white via-[#f8f8f8] to-[#ececec]" }),
1415
+ /* @__PURE__ */ jsx5("span", { className: "absolute top-[5%] right-[5%] h-[28%] w-[28%] overflow-hidden rounded-tr-[6px]", children: /* @__PURE__ */ jsx5("span", { className: "absolute top-0 right-0 h-full w-full origin-top-right -skew-x-3 rounded-bl-[4px] border-b border-l border-black/10 bg-linear-to-br from-white to-[#d9d9d9]" }) }),
1416
+ showExtension ? /* @__PURE__ */ jsx5("span", { className: "absolute right-[12%] bottom-[14%] left-[12%] truncate text-center text-[10px] leading-none font-semibold tracking-wide text-[#7a7a7a]", children: extension }) : null
1371
1417
  ]
1372
1418
  }
1373
1419
  );
@@ -1396,7 +1442,7 @@ function workspaceFileManagerIconGridFrameClassName() {
1396
1442
  }
1397
1443
 
1398
1444
  // src/ui/WorkspaceFileManagerIconGrid.tsx
1399
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1445
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1400
1446
  function WorkspaceFileManagerIconGrid({
1401
1447
  contextMenuEntryPath,
1402
1448
  copy,
@@ -1421,14 +1467,14 @@ function WorkspaceFileManagerIconGrid({
1421
1467
  onEntryClick,
1422
1468
  onEntryPointerDown
1423
1469
  }) {
1424
- return /* @__PURE__ */ jsx5(
1470
+ return /* @__PURE__ */ jsx6(
1425
1471
  "div",
1426
1472
  {
1427
1473
  className: "grid auto-rows-min content-start items-start gap-x-2 gap-y-6 px-4 py-4",
1428
1474
  style: {
1429
1475
  gridTemplateColumns: `repeat(auto-fill, minmax(${workspaceFileManagerIconGridLayout.tileMinWidthPx}px, 1fr))`
1430
1476
  },
1431
- children: entries.map((entry) => /* @__PURE__ */ jsx5(
1477
+ children: entries.map((entry) => /* @__PURE__ */ jsx6(
1432
1478
  WorkspaceFileManagerIconTile,
1433
1479
  {
1434
1480
  contextMenuActive: contextMenuEntryPath === entry.path,
@@ -1494,7 +1540,7 @@ function WorkspaceFileManagerIconTile({
1494
1540
  inline: "nearest"
1495
1541
  });
1496
1542
  }, [selected]);
1497
- const tileClassName = cn3(
1543
+ const tileClassName = cn4(
1498
1544
  "flex min-w-0 max-w-[148px] flex-col items-center gap-1.5 rounded-md border border-transparent px-2 py-2 text-center transition-colors",
1499
1545
  isInlineRenaming ? "cursor-default" : "cursor-pointer hover:bg-transparency-hover active:bg-[var(--transparency-active)]",
1500
1546
  moveDragActive && "cursor-grabbing",
@@ -1502,7 +1548,7 @@ function WorkspaceFileManagerIconTile({
1502
1548
  moveDragSource && "opacity-55",
1503
1549
  moveDragTarget && "bg-[var(--accent-bg)] text-[var(--text-primary)] outline outline-1 -outline-offset-1 outline-[var(--border-focus)]"
1504
1550
  );
1505
- const iconGraphic = /* @__PURE__ */ jsx5(
1551
+ const iconGraphic = /* @__PURE__ */ jsx6(
1506
1552
  WorkspaceFileEntryIcon,
1507
1553
  {
1508
1554
  entry,
@@ -1524,7 +1570,7 @@ function WorkspaceFileManagerIconTile({
1524
1570
  "data-workspace-file-entry-path": entry.path,
1525
1571
  children: [
1526
1572
  iconGraphic,
1527
- /* @__PURE__ */ jsx5(
1573
+ /* @__PURE__ */ jsx6(
1528
1574
  IconTileRenameInput,
1529
1575
  {
1530
1576
  copy,
@@ -1568,8 +1614,8 @@ function WorkspaceFileManagerIconTile({
1568
1614
  children: [
1569
1615
  iconGraphic,
1570
1616
  /* @__PURE__ */ jsxs4("span", { className: "line-clamp-2 w-full break-all text-[13px] leading-[18px] text-[var(--text-primary)]", children: [
1571
- /* @__PURE__ */ jsx5("span", { children: nameParts.start }),
1572
- nameParts.end ? /* @__PURE__ */ jsx5("span", { children: nameParts.end }) : null
1617
+ /* @__PURE__ */ jsx6("span", { children: nameParts.start }),
1618
+ nameParts.end ? /* @__PURE__ */ jsx6("span", { children: nameParts.end }) : null
1573
1619
  ] })
1574
1620
  ]
1575
1621
  }
@@ -1601,12 +1647,12 @@ function IconTileRenameInput({
1601
1647
  }, [hasFileExtension, nameParts.start.length]);
1602
1648
  const validationMessage = inlineRenameValidation === "required" ? copy.t("createNameRequired") : inlineRenameValidation === "invalid" ? copy.t("createNameInvalid") : null;
1603
1649
  return /* @__PURE__ */ jsxs4("span", { className: "flex w-full min-w-0 flex-col gap-0.5", children: [
1604
- /* @__PURE__ */ jsx5(
1650
+ /* @__PURE__ */ jsx6(
1605
1651
  "input",
1606
1652
  {
1607
1653
  "aria-invalid": inlineRenameValidation !== null,
1608
1654
  "aria-label": copy.t("renameLabel"),
1609
- className: cn3(
1655
+ className: cn4(
1610
1656
  "w-full min-w-0 rounded-[4px] border border-transparent bg-[var(--transparency-block)] px-1 py-0.5 text-center text-xs text-[var(--text-primary)] outline-none",
1611
1657
  inlineRenameValidation !== null && "border-[var(--state-danger)]"
1612
1658
  ),
@@ -1632,7 +1678,7 @@ function IconTileRenameInput({
1632
1678
  }
1633
1679
  }
1634
1680
  ),
1635
- validationMessage ? /* @__PURE__ */ jsx5("span", { className: "text-[10px] leading-3 text-[var(--state-danger)]", children: validationMessage }) : null
1681
+ validationMessage ? /* @__PURE__ */ jsx6("span", { className: "text-[10px] leading-3 text-[var(--state-danger)]", children: validationMessage }) : null
1636
1682
  ] });
1637
1683
  }
1638
1684
 
@@ -1725,7 +1771,7 @@ function resolveWorkspaceFileManagerPreservedNameColumnWidth(width) {
1725
1771
  }
1726
1772
 
1727
1773
  // src/ui/WorkspaceFileManagerPanels.tsx
1728
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1774
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1729
1775
  var workspaceFileManagerTableGridStyle = {
1730
1776
  gridTemplateColumns: workspaceFileManagerTableGridTemplate
1731
1777
  };
@@ -1758,6 +1804,7 @@ function WorkspaceFileManagerPanels({
1758
1804
  isRenaming,
1759
1805
  layoutMode,
1760
1806
  pendingDirectoryPath,
1807
+ previewActions,
1761
1808
  previewState,
1762
1809
  onEntryIconViewportLeave,
1763
1810
  onEntryIconViewportEnter,
@@ -2115,22 +2162,22 @@ function WorkspaceFileManagerPanels({
2115
2162
  },
2116
2163
  [clampPreviewPaneWidth, previewPaneMaxWidth, previewPaneWidth]
2117
2164
  );
2118
- const tablePanel = /* @__PURE__ */ jsx6(
2165
+ const tablePanel = /* @__PURE__ */ jsx7(
2119
2166
  "div",
2120
2167
  {
2121
- className: cn4(
2168
+ className: cn5(
2122
2169
  "flex min-h-0 min-w-0 flex-col overflow-hidden",
2123
2170
  useStackedPreview ? "h-[44%] max-h-[48%] flex-none" : "flex-1"
2124
2171
  ),
2125
2172
  onContextMenu: handleTablePanelContextMenu,
2126
- children: state.error ? /* @__PURE__ */ jsx6(FeedbackState, { message: state.error }) : state.isLoading && state.entries.length === 0 ? /* @__PURE__ */ jsx6(FeedbackState, { message: copy.t("loading") }) : state.entries.length === 0 ? /* @__PURE__ */ jsx6(
2173
+ children: state.error ? /* @__PURE__ */ jsx7(FeedbackState, { message: state.error }) : state.isLoading && state.entries.length === 0 ? /* @__PURE__ */ jsx7(FeedbackState, { message: copy.t("loading") }) : state.entries.length === 0 ? /* @__PURE__ */ jsx7(
2127
2174
  FeedbackState,
2128
2175
  {
2129
2176
  message: copy.t(
2130
2177
  state.isSearchMode ? "noSearchResults" : "emptyDirectory"
2131
2178
  )
2132
2179
  }
2133
- ) : /* @__PURE__ */ jsx6(ScrollArea, { className: "min-h-0 flex-1 [&_[data-orientation=vertical][data-slot=scroll-area-scrollbar]]:opacity-100", children: layoutMode === "icon" ? /* @__PURE__ */ jsx6(
2180
+ ) : /* @__PURE__ */ jsx7(ScrollArea, { className: "min-h-0 flex-1 [&_[data-orientation=vertical][data-slot=scroll-area-scrollbar]]:opacity-100", children: layoutMode === "icon" ? /* @__PURE__ */ jsx7(
2134
2181
  WorkspaceFileManagerIconGrid,
2135
2182
  {
2136
2183
  contextMenuEntryPath,
@@ -2160,16 +2207,16 @@ function WorkspaceFileManagerPanels({
2160
2207
  /* @__PURE__ */ jsxs5(
2161
2208
  "div",
2162
2209
  {
2163
- className: cn4(
2210
+ className: cn5(
2164
2211
  "grid h-9 min-h-9 items-center gap-x-6 border-b border-[var(--border-1)] text-xs font-normal text-[var(--text-secondary)]",
2165
2212
  tableGridClassName
2166
2213
  ),
2167
2214
  style: tableGridStyle,
2168
2215
  children: [
2169
- /* @__PURE__ */ jsx6(
2216
+ /* @__PURE__ */ jsx7(
2170
2217
  "span",
2171
2218
  {
2172
- className: cn4(
2219
+ className: cn5(
2173
2220
  "min-w-0 overflow-hidden",
2174
2221
  tableCellPaddingClassName
2175
2222
  ),
@@ -2177,17 +2224,17 @@ function WorkspaceFileManagerPanels({
2177
2224
  children: copy.t("nameLabel")
2178
2225
  }
2179
2226
  ),
2180
- /* @__PURE__ */ jsx6(
2227
+ /* @__PURE__ */ jsx7(
2181
2228
  "span",
2182
2229
  {
2183
- className: cn4("min-w-0 truncate", tableCellPaddingClassName),
2230
+ className: cn5("min-w-0 truncate", tableCellPaddingClassName),
2184
2231
  children: dateColumnLabel
2185
2232
  }
2186
2233
  ),
2187
- /* @__PURE__ */ jsx6(
2234
+ /* @__PURE__ */ jsx7(
2188
2235
  "span",
2189
2236
  {
2190
- className: cn4("min-w-0 truncate", tableCellPaddingClassName),
2237
+ className: cn5("min-w-0 truncate", tableCellPaddingClassName),
2191
2238
  children: copy.t("sizeLabel")
2192
2239
  }
2193
2240
  )
@@ -2195,7 +2242,7 @@ function WorkspaceFileManagerPanels({
2195
2242
  }
2196
2243
  ),
2197
2244
  treeRows.map(
2198
- (row) => row.kind === "feedback" ? /* @__PURE__ */ jsx6(
2245
+ (row) => row.kind === "feedback" ? /* @__PURE__ */ jsx7(
2199
2246
  TreeFeedbackRow,
2200
2247
  {
2201
2248
  copy,
@@ -2207,7 +2254,7 @@ function WorkspaceFileManagerPanels({
2207
2254
  tableCellPaddingClassName
2208
2255
  },
2209
2256
  row.key
2210
- ) : /* @__PURE__ */ jsx6(
2257
+ ) : /* @__PURE__ */ jsx7(
2211
2258
  EntryRow,
2212
2259
  {
2213
2260
  contextMenuActive: contextMenuEntryPath === row.entry.path,
@@ -2251,21 +2298,22 @@ function WorkspaceFileManagerPanels({
2251
2298
  ] }) })
2252
2299
  }
2253
2300
  );
2254
- const previewPanel = showPreviewPanel ? /* @__PURE__ */ jsx6(
2301
+ const previewPanel = showPreviewPanel ? /* @__PURE__ */ jsx7(
2255
2302
  "aside",
2256
2303
  {
2257
- className: cn4(
2304
+ className: cn5(
2258
2305
  "relative flex h-full min-h-0 flex-col gap-[14px] overflow-auto p-4",
2259
2306
  useStackedPreview ? "max-h-[44%] flex-none" : "flex-none",
2260
2307
  previewPaneClassName
2261
2308
  ),
2262
2309
  style: useStackedPreview ? void 0 : { width: previewPaneWidth },
2263
- children: /* @__PURE__ */ jsx6(
2310
+ children: /* @__PURE__ */ jsx7(
2264
2311
  PreviewPane,
2265
2312
  {
2266
2313
  copy,
2267
2314
  dateLocale,
2268
2315
  entry: selectedEntry,
2316
+ previewActions,
2269
2317
  previewState
2270
2318
  }
2271
2319
  )
@@ -2280,13 +2328,13 @@ function WorkspaceFileManagerPanels({
2280
2328
  /* @__PURE__ */ jsxs5(
2281
2329
  "div",
2282
2330
  {
2283
- className: cn4(
2331
+ className: cn5(
2284
2332
  "h-full min-h-0 min-w-0 bg-transparent",
2285
2333
  useStackedPreview ? "flex flex-col gap-3" : "flex"
2286
2334
  ),
2287
2335
  children: [
2288
2336
  tablePanel,
2289
- showPreviewPanel && !useStackedPreview ? /* @__PURE__ */ jsx6(
2337
+ showPreviewPanel && !useStackedPreview ? /* @__PURE__ */ jsx7(
2290
2338
  "div",
2291
2339
  {
2292
2340
  "aria-label": copy.t("resizePreviewPanel"),
@@ -2311,7 +2359,7 @@ function WorkspaceFileManagerPanels({
2311
2359
  ]
2312
2360
  }
2313
2361
  ),
2314
- moveDragPreview ? /* @__PURE__ */ jsx6(
2362
+ moveDragPreview ? /* @__PURE__ */ jsx7(
2315
2363
  MoveDragPreview,
2316
2364
  {
2317
2365
  iconUrlByCacheKey,
@@ -2366,7 +2414,7 @@ function resolveWorkspaceFileManagerDateColumnLabel(copy, arrangeMode) {
2366
2414
  return copy.t("modifiedLabel");
2367
2415
  }
2368
2416
  }
2369
- function EntryRow({
2417
+ var EntryRow = memo(function EntryRow2({
2370
2418
  arrangeMode,
2371
2419
  canMove,
2372
2420
  contextMenuActive,
@@ -2413,7 +2461,7 @@ function EntryRow({
2413
2461
  inline: "nearest"
2414
2462
  });
2415
2463
  }, [selected]);
2416
- const rowClassName = cn4(
2464
+ const rowClassName = cn5(
2417
2465
  "relative grid min-h-10 w-full items-center gap-x-6 border-b border-[var(--border-1)] px-0 text-left transition-colors",
2418
2466
  gridClassName,
2419
2467
  isInlineRenaming ? "cursor-default" : "cursor-pointer hover:bg-transparency-hover active:bg-[var(--transparency-active)]",
@@ -2431,15 +2479,15 @@ function EntryRow({
2431
2479
  onContextMenu(event, entry);
2432
2480
  }
2433
2481
  };
2434
- const nameCell = /* @__PURE__ */ jsx6(
2482
+ const nameCell = /* @__PURE__ */ jsx7(
2435
2483
  "span",
2436
2484
  {
2437
- className: cn4(
2485
+ className: cn5(
2438
2486
  "min-w-0 overflow-hidden",
2439
2487
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2440
2488
  tableCellPaddingClassName
2441
2489
  ),
2442
- children: /* @__PURE__ */ jsx6(
2490
+ children: /* @__PURE__ */ jsx7(
2443
2491
  EntryNameCell,
2444
2492
  {
2445
2493
  copy,
@@ -2464,10 +2512,10 @@ function EntryRow({
2464
2512
  )
2465
2513
  }
2466
2514
  );
2467
- const modifiedCell = /* @__PURE__ */ jsx6(
2515
+ const modifiedCell = /* @__PURE__ */ jsx7(
2468
2516
  "span",
2469
2517
  {
2470
- className: cn4(
2518
+ className: cn5(
2471
2519
  "truncate text-xs text-[var(--text-secondary)]",
2472
2520
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2473
2521
  tableCellPaddingClassName
@@ -2478,10 +2526,10 @@ function EntryRow({
2478
2526
  )
2479
2527
  }
2480
2528
  );
2481
- const sizeCell = /* @__PURE__ */ jsx6(
2529
+ const sizeCell = /* @__PURE__ */ jsx7(
2482
2530
  "span",
2483
2531
  {
2484
- className: cn4(
2532
+ className: cn5(
2485
2533
  "truncate text-xs text-[var(--text-secondary)]",
2486
2534
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2487
2535
  tableCellPaddingClassName
@@ -2519,7 +2567,7 @@ function EntryRow({
2519
2567
  onDragStart?.(entry, event.dataTransfer);
2520
2568
  },
2521
2569
  children: [
2522
- /* @__PURE__ */ jsx6(
2570
+ /* @__PURE__ */ jsx7(
2523
2571
  "button",
2524
2572
  {
2525
2573
  "aria-label": entry.name,
@@ -2540,7 +2588,7 @@ function EntryRow({
2540
2588
  ]
2541
2589
  }
2542
2590
  );
2543
- }
2591
+ });
2544
2592
  function MoveDragPreview({
2545
2593
  iconUrlByCacheKey,
2546
2594
  preview,
@@ -2558,7 +2606,7 @@ function MoveDragPreview({
2558
2606
  transform: "translate(12px, 12px)"
2559
2607
  },
2560
2608
  children: [
2561
- /* @__PURE__ */ jsx6(
2609
+ /* @__PURE__ */ jsx7(
2562
2610
  WorkspaceFileEntryIcon,
2563
2611
  {
2564
2612
  entry: preview.entry,
@@ -2569,7 +2617,7 @@ function MoveDragPreview({
2569
2617
  onViewportEnter: onEntryIconViewportEnter
2570
2618
  }
2571
2619
  ),
2572
- /* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate", children: preview.entry.name })
2620
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: preview.entry.name })
2573
2621
  ]
2574
2622
  }
2575
2623
  );
@@ -2622,26 +2670,26 @@ function TreeFeedbackRow({
2622
2670
  return /* @__PURE__ */ jsxs5(
2623
2671
  "div",
2624
2672
  {
2625
- className: cn4(
2673
+ className: cn5(
2626
2674
  "grid min-h-9 w-full items-center gap-x-6 border-b border-[var(--border-1)] text-left text-xs text-[var(--text-tertiary)]",
2627
2675
  gridClassName
2628
2676
  ),
2629
2677
  style: gridStyle,
2630
2678
  children: [
2631
- /* @__PURE__ */ jsx6("span", { className: tableCellPaddingClassName, children: /* @__PURE__ */ jsxs5(
2679
+ /* @__PURE__ */ jsx7("span", { className: tableCellPaddingClassName, children: /* @__PURE__ */ jsxs5(
2632
2680
  "span",
2633
2681
  {
2634
2682
  className: "flex min-w-0 items-center gap-2",
2635
2683
  style: workspaceFileManagerTreeIndentStyle(depth),
2636
2684
  children: [
2637
- /* @__PURE__ */ jsx6("span", { className: "size-5 shrink-0" }),
2638
- status === "loading" ? /* @__PURE__ */ jsx6(LoadingIcon2, { className: "size-3.5 shrink-0 animate-spin" }) : null,
2639
- /* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate", children: resolvedMessage })
2685
+ /* @__PURE__ */ jsx7("span", { className: "size-5 shrink-0" }),
2686
+ status === "loading" ? /* @__PURE__ */ jsx7(LoadingIcon2, { className: "size-3.5 shrink-0 animate-spin" }) : null,
2687
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: resolvedMessage })
2640
2688
  ]
2641
2689
  }
2642
2690
  ) }),
2643
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true" }),
2644
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true" })
2691
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true" }),
2692
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true" })
2645
2693
  ]
2646
2694
  }
2647
2695
  );
@@ -2655,9 +2703,9 @@ function DirectoryDisclosureButton({
2655
2703
  onToggle
2656
2704
  }) {
2657
2705
  if (!show) {
2658
- return /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", className: "size-5 shrink-0" });
2706
+ return /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "size-5 shrink-0" });
2659
2707
  }
2660
- return /* @__PURE__ */ jsx6(
2708
+ return /* @__PURE__ */ jsx7(
2661
2709
  "button",
2662
2710
  {
2663
2711
  "aria-label": copy.t(
@@ -2675,10 +2723,10 @@ function DirectoryDisclosureButton({
2675
2723
  onPointerDown: (event) => {
2676
2724
  event.stopPropagation();
2677
2725
  },
2678
- children: isLoading ? /* @__PURE__ */ jsx6(LoadingIcon2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx6(
2726
+ children: isLoading ? /* @__PURE__ */ jsx7(LoadingIcon2, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx7(
2679
2727
  ArrowRightIcon2,
2680
2728
  {
2681
- className: cn4(
2729
+ className: cn5(
2682
2730
  "size-3.5 transition-transform",
2683
2731
  expanded && "rotate-90"
2684
2732
  )
@@ -2753,7 +2801,7 @@ function EntryNameCell({
2753
2801
  className: "flex min-w-0 items-center gap-1.5",
2754
2802
  style: workspaceFileManagerTreeIndentStyle(treeDepth),
2755
2803
  children: [
2756
- /* @__PURE__ */ jsx6(
2804
+ /* @__PURE__ */ jsx7(
2757
2805
  DirectoryDisclosureButton,
2758
2806
  {
2759
2807
  copy,
@@ -2764,7 +2812,7 @@ function EntryNameCell({
2764
2812
  onToggle: onToggleDirectoryExpanded
2765
2813
  }
2766
2814
  ),
2767
- /* @__PURE__ */ jsx6(
2815
+ /* @__PURE__ */ jsx7(
2768
2816
  WorkspaceFileEntryIcon,
2769
2817
  {
2770
2818
  entry,
@@ -2776,12 +2824,12 @@ function EntryNameCell({
2776
2824
  }
2777
2825
  ),
2778
2826
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
2779
- /* @__PURE__ */ jsx6(
2827
+ /* @__PURE__ */ jsx7(
2780
2828
  "input",
2781
2829
  {
2782
2830
  "aria-invalid": inlineRenameValidation !== null,
2783
2831
  "aria-label": copy.t("renameLabel"),
2784
- className: cn4(
2832
+ className: cn5(
2785
2833
  "min-w-0 max-w-full rounded-md border border-transparent bg-[var(--transparency-block)] px-1.5 py-0.5 text-sm text-[var(--text-primary)] outline-none transition-colors duration-200 selection:bg-[var(--transparency-active)] selection:text-[var(--text-primary)] hover:bg-[var(--transparency-hover)] focus:bg-[var(--transparency-hover)] focus-visible:border-transparent focus-visible:bg-[var(--transparency-hover)] disabled:bg-[var(--transparency-block)] disabled:text-[var(--text-disabled)] disabled:opacity-100",
2786
2834
  inlineRenameValidation !== null && "border-[var(--state-danger)]"
2787
2835
  ),
@@ -2823,7 +2871,7 @@ function EntryNameCell({
2823
2871
  }
2824
2872
  }
2825
2873
  ),
2826
- validationMessage ? /* @__PURE__ */ jsx6("span", { className: "text-xs text-[var(--state-danger)]", children: validationMessage }) : null
2874
+ validationMessage ? /* @__PURE__ */ jsx7("span", { className: "text-xs text-[var(--state-danger)]", children: validationMessage }) : null
2827
2875
  ] })
2828
2876
  ]
2829
2877
  }
@@ -2835,7 +2883,7 @@ function EntryNameCell({
2835
2883
  className: "flex min-w-0 items-center gap-1.5",
2836
2884
  style: workspaceFileManagerTreeIndentStyle(treeDepth),
2837
2885
  children: [
2838
- /* @__PURE__ */ jsx6(
2886
+ /* @__PURE__ */ jsx7(
2839
2887
  DirectoryDisclosureButton,
2840
2888
  {
2841
2889
  copy,
@@ -2846,7 +2894,7 @@ function EntryNameCell({
2846
2894
  onToggle: onToggleDirectoryExpanded
2847
2895
  }
2848
2896
  ),
2849
- /* @__PURE__ */ jsx6(
2897
+ /* @__PURE__ */ jsx7(
2850
2898
  WorkspaceFileEntryIcon,
2851
2899
  {
2852
2900
  entry,
@@ -2860,10 +2908,10 @@ function EntryNameCell({
2860
2908
  ),
2861
2909
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
2862
2910
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 max-w-full overflow-hidden whitespace-nowrap text-sm", children: [
2863
- /* @__PURE__ */ jsx6("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: nameParts.start }),
2864
- nameParts.end ? /* @__PURE__ */ jsx6("span", { className: "flex-none overflow-hidden text-ellipsis", children: nameParts.end }) : null
2911
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: nameParts.start }),
2912
+ nameParts.end ? /* @__PURE__ */ jsx7("span", { className: "flex-none overflow-hidden text-ellipsis", children: nameParts.end }) : null
2865
2913
  ] }),
2866
- contextLabel ? /* @__PURE__ */ jsx6("span", { className: "block max-w-full truncate text-[11px] leading-3 text-[var(--text-tertiary)]", children: contextLabel }) : null
2914
+ contextLabel ? /* @__PURE__ */ jsx7("span", { className: "block max-w-full truncate text-[11px] leading-3 text-[var(--text-tertiary)]", children: contextLabel }) : null
2867
2915
  ] })
2868
2916
  ]
2869
2917
  }
@@ -2875,47 +2923,55 @@ function EntryIcon({
2875
2923
  }) {
2876
2924
  switch (visualKind) {
2877
2925
  case "directory":
2878
- return /* @__PURE__ */ jsx6(WorkspaceFolderFallbackIcon, { className });
2926
+ return /* @__PURE__ */ jsx7(WorkspaceFolderFallbackIcon, { className });
2879
2927
  case "image":
2880
- return /* @__PURE__ */ jsx6(WorkspaceImageFallbackIcon, { className });
2928
+ return /* @__PURE__ */ jsx7(WorkspaceImageFallbackIcon, { className });
2881
2929
  case "video":
2882
- return /* @__PURE__ */ jsx6(VideoFileIcon2, { className });
2930
+ return /* @__PURE__ */ jsx7(VideoFileIcon2, { className });
2883
2931
  case "markdown":
2884
2932
  case "document":
2885
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2933
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2886
2934
  case "code":
2887
- return /* @__PURE__ */ jsx6(FileCodeIcon2, { className });
2935
+ return /* @__PURE__ */ jsx7(FileCodeIcon2, { className });
2888
2936
  case "binary":
2889
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2937
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2890
2938
  default:
2891
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2939
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2892
2940
  }
2893
2941
  }
2894
2942
  function PreviewPane({
2895
2943
  copy,
2896
2944
  dateLocale,
2897
2945
  entry,
2946
+ previewActions,
2898
2947
  previewState
2899
2948
  }) {
2900
2949
  if (!entry || previewState.status === "empty") {
2901
- return /* @__PURE__ */ jsx6("div", { className: "grid h-full min-h-[180px] place-items-center overflow-hidden p-8 text-center text-sm leading-5 text-[var(--text-tertiary)]", children: /* @__PURE__ */ jsx6("span", { className: "max-w-[24ch] [overflow-wrap:anywhere]", children: copy.t("previewEmptyLabel") }) });
2950
+ return /* @__PURE__ */ jsx7("div", { className: "grid h-full min-h-[180px] place-items-center overflow-hidden p-8 text-center text-sm leading-5 text-[var(--text-tertiary)]", children: /* @__PURE__ */ jsx7("span", { className: "max-w-[24ch] [overflow-wrap:anywhere]", children: copy.t("previewEmptyLabel") }) });
2902
2951
  }
2903
2952
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2904
- /* @__PURE__ */ jsx6(PreviewSurface, { copy, previewState }),
2953
+ /* @__PURE__ */ jsx7(PreviewSurface, { copy, previewState }),
2954
+ /* @__PURE__ */ jsx7(
2955
+ WorkspaceFileManagerPreviewActionBar,
2956
+ {
2957
+ actions: previewActions ?? [],
2958
+ label: copy.t("previewActionsLabel")
2959
+ }
2960
+ ),
2905
2961
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-col gap-[14px]", children: [
2906
2962
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-col gap-[3px]", children: [
2907
- /* @__PURE__ */ jsx6("strong", { className: "min-w-0 truncate text-[15px] font-semibold text-[var(--text-primary)]", children: entry.name }),
2908
- /* @__PURE__ */ jsx6("p", { className: "min-w-0 truncate text-xs text-[var(--text-secondary)]", children: entry.path })
2963
+ /* @__PURE__ */ jsx7("strong", { className: "min-w-0 truncate text-[15px] font-semibold text-[var(--text-primary)]", children: entry.name }),
2964
+ /* @__PURE__ */ jsx7("p", { className: "min-w-0 truncate text-xs text-[var(--text-secondary)]", children: entry.path })
2909
2965
  ] }),
2910
2966
  /* @__PURE__ */ jsxs5("dl", { className: "border-t border-[var(--border-1)]", children: [
2911
- /* @__PURE__ */ jsx6(
2967
+ /* @__PURE__ */ jsx7(
2912
2968
  PreviewDetail,
2913
2969
  {
2914
2970
  label: copy.t("modifiedLabel"),
2915
2971
  value: formatWorkspaceFileModifiedTime(entry.mtimeMs, dateLocale)
2916
2972
  }
2917
2973
  ),
2918
- /* @__PURE__ */ jsx6(
2974
+ /* @__PURE__ */ jsx7(
2919
2975
  PreviewDetail,
2920
2976
  {
2921
2977
  label: copy.t("sizeLabel"),
@@ -2930,15 +2986,15 @@ function PreviewSurface({
2930
2986
  copy,
2931
2987
  previewState
2932
2988
  }) {
2933
- return /* @__PURE__ */ jsx6(
2989
+ return /* @__PURE__ */ jsx7(
2934
2990
  SharedWorkspaceFilePreviewSurface,
2935
2991
  {
2936
2992
  directoryMessage: copy.t("previewDirectoryLabel"),
2937
2993
  emptyMessage: copy.t("previewEmptyLabel"),
2938
2994
  imageAlt: (entry) => entry.name,
2939
- loadingIndicator: /* @__PURE__ */ jsx6("span", { className: "mx-auto grid size-11 place-items-center rounded-lg bg-[var(--transparency-block)]", children: /* @__PURE__ */ jsx6(LoadingIcon2, { className: "size-4 animate-spin" }) }),
2995
+ loadingIndicator: /* @__PURE__ */ jsx7("span", { className: "mx-auto grid size-11 place-items-center rounded-lg bg-[var(--transparency-block)]", children: /* @__PURE__ */ jsx7(LoadingIcon2, { className: "size-4 animate-spin" }) }),
2940
2996
  loadingMessage: copy.t("previewLoadingLabel"),
2941
- renderIcon: (entry) => /* @__PURE__ */ jsx6(
2997
+ renderIcon: (entry) => /* @__PURE__ */ jsx7(
2942
2998
  EntryIcon,
2943
2999
  {
2944
3000
  className: "mx-auto size-7",
@@ -2966,153 +3022,784 @@ function PreviewDetail({
2966
3022
  className: "@max-[600px]/workspace-file-manager:grid-cols-1 @max-[600px]/workspace-file-manager:gap-0.5 grid gap-2.5 border-b border-[var(--border-1)] py-2.5 text-xs",
2967
3023
  style: workspaceFileManagerPreviewDetailGridStyle,
2968
3024
  children: [
2969
- /* @__PURE__ */ jsx6("dt", { className: "truncate text-[var(--text-secondary)]", children: label }),
2970
- /* @__PURE__ */ jsx6("dd", { className: "@max-[600px]/workspace-file-manager:text-left truncate text-right text-[var(--text-primary)]", children: value })
3025
+ /* @__PURE__ */ jsx7("dt", { className: "truncate text-[var(--text-secondary)]", children: label }),
3026
+ /* @__PURE__ */ jsx7("dd", { className: "@max-[600px]/workspace-file-manager:text-left truncate text-right text-[var(--text-primary)]", children: value })
2971
3027
  ]
2972
3028
  }
2973
3029
  );
2974
3030
  }
2975
3031
  function FeedbackState({ message }) {
2976
- return /* @__PURE__ */ jsx6("div", { className: "grid min-h-0 flex-1 place-items-center p-6 text-center text-sm text-[var(--text-tertiary)]", children: /* @__PURE__ */ jsx6("span", { className: "max-w-[34ch] [overflow-wrap:anywhere]", children: message }) });
3032
+ return /* @__PURE__ */ jsx7("div", { className: "grid min-h-0 flex-1 place-items-center p-6 text-center text-sm text-[var(--text-tertiary)]", children: /* @__PURE__ */ jsx7("span", { className: "max-w-[34ch] [overflow-wrap:anywhere]", children: message }) });
2977
3033
  }
2978
3034
 
2979
- // src/ui/WorkspaceFileManagerToolbar.tsx
2980
- import {
2981
- ArrowLeftIcon as ArrowLeftIcon2,
2982
- ArrowRightIcon as ArrowRightIcon3,
2983
- Button as Button2,
2984
- ChevronDownIcon,
2985
- CloseIcon,
2986
- DropdownMenu,
2987
- DropdownMenuContent,
2988
- DropdownMenuRadioGroup,
2989
- DropdownMenuRadioItem,
2990
- DropdownMenuSeparator,
2991
- DropdownMenuTrigger,
2992
- Input as Input2,
2993
- LoadingIcon as LoadingIcon3,
2994
- RefreshIcon,
2995
- SearchIcon,
2996
- Tooltip,
2997
- TooltipContent,
2998
- TooltipProvider,
2999
- TooltipTrigger,
3000
- ViewGridLinedIcon,
3001
- ViewListLinedIcon,
3002
- cn as cn5
3003
- } from "@tutti-os/ui-system";
3004
- import { useEffect as useEffect5, useRef as useRef6, useState as useState4 } from "react";
3005
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3006
- function WorkspaceFileManagerToolbar({
3007
- breadcrumbs,
3008
- canSearch,
3009
- canGoBack,
3010
- canGoForward,
3011
- copy,
3012
- currentDirectoryPath,
3013
- isBusy,
3014
- isLoading,
3015
- isMutating,
3016
- isSearching,
3017
- arrangeMode,
3018
- layoutMode,
3019
- renderToolbarTrailingActions,
3020
- searchQuery,
3021
- onGoBack,
3022
- onGoForward,
3023
- onArrangeModeChange,
3024
- onLayoutModeChange,
3025
- onLoadDirectory,
3026
- onRefresh,
3027
- onSearchClear,
3028
- onSearchQueryChange
3029
- }) {
3030
- const [refreshAnimationKey, setRefreshAnimationKey] = useState4(0);
3031
- const handleRefresh = () => {
3032
- setRefreshAnimationKey((currentKey) => currentKey + 1);
3033
- onRefresh();
3035
+ // src/ui/useWorkspaceFileEntryIconUrls.ts
3036
+ import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo2, useState as useState4 } from "react";
3037
+
3038
+ // src/ui/workspaceFileEntryIconUrlQueue.ts
3039
+ var defaultMaxConcurrentIconRequests = 3;
3040
+ var imageThumbnailCacheKeyPrefix = "image-thumbnail:";
3041
+ function shouldKeepResolvedIconAfterViewportLeave(cacheKey) {
3042
+ return cacheKey.startsWith(imageThumbnailCacheKeyPrefix);
3043
+ }
3044
+ function createWorkspaceFileEntryIconUrlQueue(options) {
3045
+ const maxConcurrent = Math.max(
3046
+ 1,
3047
+ Math.floor(options.maxConcurrent ?? defaultMaxConcurrentIconRequests)
3048
+ );
3049
+ const iconUrlByCacheKey = /* @__PURE__ */ new Map();
3050
+ const inFlightCacheKeys = /* @__PURE__ */ new Set();
3051
+ const listeners = /* @__PURE__ */ new Set();
3052
+ const queuedEntries = /* @__PURE__ */ new Map();
3053
+ const visibleReferenceCountByCacheKey = /* @__PURE__ */ new Map();
3054
+ let activeCount = 0;
3055
+ let disposed = false;
3056
+ let retainedCacheKeys = null;
3057
+ const policyOptions = {
3058
+ includeImageThumbnails: options.includeImageThumbnails
3034
3059
  };
3035
- return /* @__PURE__ */ jsxs6("header", { className: "@max-[600px]/workspace-file-manager:flex-nowrap flex h-10 min-h-10 w-full min-w-0 items-center gap-2 border-b border-[var(--border-1)] px-2 py-1", children: [
3036
- /* @__PURE__ */ jsx7(
3037
- ToolbarIconButton,
3038
- {
3039
- ariaLabel: copy.t("backLabel"),
3040
- disabled: !canGoBack || isLoading || isBusy,
3041
- title: copy.t("backLabel"),
3042
- onClick: onGoBack,
3043
- children: /* @__PURE__ */ jsx7(ArrowLeftIcon2, { className: "size-4" })
3044
- }
3045
- ),
3046
- /* @__PURE__ */ jsx7(
3047
- ToolbarIconButton,
3048
- {
3049
- ariaLabel: copy.t("forwardLabel"),
3050
- disabled: !canGoForward || isLoading || isBusy,
3051
- title: copy.t("forwardLabel"),
3052
- onClick: onGoForward,
3053
- children: /* @__PURE__ */ jsx7(ArrowRightIcon3, { className: "size-4" })
3060
+ function publish() {
3061
+ for (const listener of listeners) {
3062
+ listener();
3063
+ }
3064
+ }
3065
+ function isRetained(cacheKey) {
3066
+ return retainedCacheKeys === null || retainedCacheKeys.has(cacheKey);
3067
+ }
3068
+ function isVisible(cacheKey) {
3069
+ return (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) > 0;
3070
+ }
3071
+ function shouldResolveCacheKey(cacheKey) {
3072
+ return isVisible(cacheKey) || shouldKeepResolvedIconAfterViewportLeave(cacheKey);
3073
+ }
3074
+ function retainVisibleReference(cacheKey) {
3075
+ visibleReferenceCountByCacheKey.set(
3076
+ cacheKey,
3077
+ (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) + 1
3078
+ );
3079
+ }
3080
+ function releaseVisibleReference(cacheKey) {
3081
+ const nextCount = (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) - 1;
3082
+ if (nextCount > 0) {
3083
+ visibleReferenceCountByCacheKey.set(cacheKey, nextCount);
3084
+ return;
3085
+ }
3086
+ visibleReferenceCountByCacheKey.delete(cacheKey);
3087
+ if (shouldKeepResolvedIconAfterViewportLeave(cacheKey)) {
3088
+ return;
3089
+ }
3090
+ queuedEntries.delete(cacheKey);
3091
+ const hadCachedIcon = iconUrlByCacheKey.delete(cacheKey);
3092
+ if (hadCachedIcon) {
3093
+ publish();
3094
+ }
3095
+ }
3096
+ function drain() {
3097
+ if (disposed || !options.resolveEntryIconUrl) {
3098
+ return;
3099
+ }
3100
+ while (activeCount < maxConcurrent && queuedEntries.size > 0) {
3101
+ const next = queuedEntries.entries().next().value;
3102
+ if (!next) {
3103
+ return;
3054
3104
  }
3055
- ),
3056
- /* @__PURE__ */ jsx7(
3057
- "nav",
3058
- {
3059
- "aria-label": currentDirectoryPath,
3060
- className: "@max-[600px]/workspace-file-manager:flex-auto flex min-w-0 flex-1 overflow-hidden pr-2",
3061
- "data-workspace-file-manager-path": "",
3062
- children: /* @__PURE__ */ jsx7("ol", { className: "flex min-w-0 max-w-full flex-1 items-center gap-2 overflow-hidden", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsx7(
3063
- BreadcrumbButton,
3064
- {
3065
- active: index === breadcrumbs.length - 1,
3066
- label: crumb.label,
3067
- showSeparator: index > 0,
3068
- onClick: () => {
3069
- onLoadDirectory(crumb.path);
3070
- }
3071
- },
3072
- crumb.path
3073
- )) })
3105
+ const [cacheKey, entry] = next;
3106
+ queuedEntries.delete(cacheKey);
3107
+ if (!shouldResolveCacheKey(cacheKey) || iconUrlByCacheKey.has(cacheKey) || inFlightCacheKeys.has(cacheKey)) {
3108
+ continue;
3074
3109
  }
3075
- ),
3076
- /* @__PURE__ */ jsxs6("div", { className: "@max-[600px]/workspace-file-manager:justify-end flex flex-none items-center gap-1.5", children: [
3077
- /* @__PURE__ */ jsx7(
3078
- LayoutModeToggle,
3079
- {
3080
- arrangeMode,
3081
- copy,
3082
- layoutMode,
3083
- onArrangeModeChange,
3084
- onLayoutModeChange
3110
+ activeCount += 1;
3111
+ inFlightCacheKeys.add(cacheKey);
3112
+ void options.resolveEntryIconUrl(entry).then((iconUrl) => {
3113
+ const shouldStore = !disposed && isRetained(cacheKey) && shouldResolveCacheKey(cacheKey);
3114
+ if (shouldStore) {
3115
+ iconUrlByCacheKey.set(cacheKey, iconUrl?.trim() || null);
3116
+ publish();
3085
3117
  }
3086
- ),
3087
- /* @__PURE__ */ jsxs6(
3088
- ToolbarActionButton,
3089
- {
3090
- disabled: isLoading || isMutating || isBusy,
3091
- onClick: handleRefresh,
3092
- children: [
3093
- isLoading ? /* @__PURE__ */ jsx7(LoadingIcon3, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx7(
3094
- RefreshIcon,
3095
- {
3096
- className: cn5(
3097
- "size-4",
3098
- refreshAnimationKey > 0 && "motion-safe:animate-[spin_520ms_cubic-bezier(0.4,0,0.2,1)_1_reverse]"
3099
- )
3100
- },
3101
- refreshAnimationKey
3102
- ),
3103
- /* @__PURE__ */ jsx7("span", { className: "@max-[600px]/workspace-file-manager:hidden", children: copy.t("refreshLabel") })
3104
- ]
3118
+ }).catch(() => {
3119
+ const shouldStore = !disposed && isRetained(cacheKey) && shouldResolveCacheKey(cacheKey);
3120
+ if (shouldStore) {
3121
+ iconUrlByCacheKey.set(cacheKey, null);
3122
+ publish();
3105
3123
  }
3106
- ),
3107
- renderToolbarTrailingActions?.({
3108
- currentDirectoryPath,
3109
- isBusy,
3110
- isLoading,
3111
- isMutating
3112
- }) ?? null,
3113
- /* @__PURE__ */ jsx7(
3114
- WorkspaceFileManagerToolbarSearch,
3115
- {
3124
+ }).finally(() => {
3125
+ activeCount -= 1;
3126
+ inFlightCacheKeys.delete(cacheKey);
3127
+ drain();
3128
+ });
3129
+ }
3130
+ }
3131
+ return {
3132
+ activate() {
3133
+ disposed = false;
3134
+ },
3135
+ dispose() {
3136
+ disposed = true;
3137
+ queuedEntries.clear();
3138
+ visibleReferenceCountByCacheKey.clear();
3139
+ listeners.clear();
3140
+ },
3141
+ leaveViewport(entry) {
3142
+ if (disposed || !shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)) {
3143
+ return;
3144
+ }
3145
+ const cacheKey = resolveWorkspaceFileEntryIconCacheKey(entry);
3146
+ releaseVisibleReference(cacheKey);
3147
+ },
3148
+ enterViewport(entry) {
3149
+ if (disposed || !options.resolveEntryIconUrl || !shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)) {
3150
+ return;
3151
+ }
3152
+ const cacheKey = resolveWorkspaceFileEntryIconCacheKey(entry);
3153
+ retainVisibleReference(cacheKey);
3154
+ if (iconUrlByCacheKey.has(cacheKey) || inFlightCacheKeys.has(cacheKey) || queuedEntries.has(cacheKey)) {
3155
+ return;
3156
+ }
3157
+ queuedEntries.set(cacheKey, entry);
3158
+ drain();
3159
+ },
3160
+ retainEntries(entries) {
3161
+ retainedCacheKeys = new Set(
3162
+ entries.filter(
3163
+ (entry) => shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)
3164
+ ).map((entry) => resolveWorkspaceFileEntryIconCacheKey(entry))
3165
+ );
3166
+ let changed = false;
3167
+ for (const cacheKey of iconUrlByCacheKey.keys()) {
3168
+ if (!isRetained(cacheKey)) {
3169
+ iconUrlByCacheKey.delete(cacheKey);
3170
+ changed = true;
3171
+ continue;
3172
+ }
3173
+ if (iconUrlByCacheKey.get(cacheKey) === null) {
3174
+ iconUrlByCacheKey.delete(cacheKey);
3175
+ changed = true;
3176
+ }
3177
+ }
3178
+ for (const cacheKey of queuedEntries.keys()) {
3179
+ if (!isRetained(cacheKey)) {
3180
+ queuedEntries.delete(cacheKey);
3181
+ }
3182
+ }
3183
+ for (const cacheKey of visibleReferenceCountByCacheKey.keys()) {
3184
+ if (!isRetained(cacheKey)) {
3185
+ visibleReferenceCountByCacheKey.delete(cacheKey);
3186
+ }
3187
+ }
3188
+ if (changed) {
3189
+ publish();
3190
+ }
3191
+ },
3192
+ snapshot() {
3193
+ return new Map(iconUrlByCacheKey);
3194
+ },
3195
+ subscribe(listener) {
3196
+ if (disposed) {
3197
+ return () => {
3198
+ };
3199
+ }
3200
+ listeners.add(listener);
3201
+ return () => {
3202
+ listeners.delete(listener);
3203
+ };
3204
+ }
3205
+ };
3206
+ }
3207
+
3208
+ // src/ui/useWorkspaceFileEntryIconUrls.ts
3209
+ function buildIconTargetSignature(entries, options) {
3210
+ return entries.filter((entry) => shouldResolveWorkspaceFileEntryIcon(entry, options)).map((entry) => resolveWorkspaceFileEntryIconCacheKey(entry)).join("\0");
3211
+ }
3212
+ function useWorkspaceFileEntryIconUrls(input) {
3213
+ const { entries, includeImageThumbnails, resolveEntryIconUrl } = input;
3214
+ const queue = useMemo2(
3215
+ () => createWorkspaceFileEntryIconUrlQueue({
3216
+ includeImageThumbnails,
3217
+ resolveEntryIconUrl
3218
+ }),
3219
+ [includeImageThumbnails, resolveEntryIconUrl]
3220
+ );
3221
+ const [iconUrlByCacheKey, setIconUrlByCacheKey] = useState4(() => queue.snapshot());
3222
+ const iconTargetSignature = useMemo2(
3223
+ () => buildIconTargetSignature(entries, { includeImageThumbnails }),
3224
+ [entries, includeImageThumbnails]
3225
+ );
3226
+ useEffect5(() => {
3227
+ queue.activate();
3228
+ return () => {
3229
+ queue.dispose();
3230
+ };
3231
+ }, [queue]);
3232
+ useEffect5(() => {
3233
+ queue.retainEntries(entries);
3234
+ }, [entries, iconTargetSignature, queue]);
3235
+ useEffect5(() => {
3236
+ setIconUrlByCacheKey(queue.snapshot());
3237
+ return queue.subscribe(() => {
3238
+ setIconUrlByCacheKey(queue.snapshot());
3239
+ });
3240
+ }, [queue]);
3241
+ const reportEntryIconViewportEnter = useCallback3(
3242
+ (entry) => {
3243
+ queue.enterViewport(entry);
3244
+ },
3245
+ [queue]
3246
+ );
3247
+ const reportEntryIconViewportLeave = useCallback3(
3248
+ (entry) => {
3249
+ queue.leaveViewport(entry);
3250
+ },
3251
+ [queue]
3252
+ );
3253
+ return {
3254
+ iconUrlByCacheKey,
3255
+ reportEntryIconViewportEnter,
3256
+ reportEntryIconViewportLeave
3257
+ };
3258
+ }
3259
+
3260
+ // src/ui/useWorkspaceFileManagerPreviewActions.tsx
3261
+ import { useMemo as useMemo3 } from "react";
3262
+ import {
3263
+ CopyIcon,
3264
+ DownloadIcon,
3265
+ EyeIcon,
3266
+ ShareLinedIcon
3267
+ } from "@tutti-os/ui-system/icons";
3268
+
3269
+ // src/ui/workspaceFileManagerPreviewActionTypes.ts
3270
+ var workspaceFileManagerPreviewActionOrder = [
3271
+ "copy",
3272
+ "open",
3273
+ "download",
3274
+ "share"
3275
+ ];
3276
+
3277
+ // src/ui/useWorkspaceFileManagerPreviewActions.tsx
3278
+ import { jsx as jsx8 } from "react/jsx-runtime";
3279
+ var previewActionIconClassName = "size-4";
3280
+ function useWorkspaceFileManagerPreviewActions({
3281
+ config,
3282
+ copy,
3283
+ entry,
3284
+ onCopyEntry,
3285
+ session,
3286
+ state
3287
+ }) {
3288
+ const { busyAction, capabilities, isLoading, isMutating } = state;
3289
+ const isExternalLocation = isWorkspaceFileExternalLocation(
3290
+ findWorkspaceFileLocationById(
3291
+ state.locationSections,
3292
+ state.selectedLocationId
3293
+ )
3294
+ );
3295
+ return useMemo3(() => {
3296
+ if (!config || !entry) {
3297
+ return [];
3298
+ }
3299
+ const disabled = busyAction !== null || isLoading || isMutating;
3300
+ const order = config.order ?? workspaceFileManagerPreviewActionOrder;
3301
+ const actions = [];
3302
+ for (const id of order) {
3303
+ const action = buildPreviewAction({
3304
+ config,
3305
+ copy,
3306
+ disabled,
3307
+ entry,
3308
+ id,
3309
+ isExternalLocation,
3310
+ onCopyEntry,
3311
+ session,
3312
+ canCopy: capabilities.canCopy
3313
+ });
3314
+ if (action) {
3315
+ actions.push(action);
3316
+ }
3317
+ }
3318
+ return actions;
3319
+ }, [
3320
+ busyAction,
3321
+ capabilities.canCopy,
3322
+ config,
3323
+ copy,
3324
+ entry,
3325
+ isExternalLocation,
3326
+ isLoading,
3327
+ isMutating,
3328
+ onCopyEntry,
3329
+ session
3330
+ ]);
3331
+ }
3332
+ function buildPreviewAction({
3333
+ canCopy,
3334
+ config,
3335
+ copy,
3336
+ disabled,
3337
+ entry,
3338
+ id,
3339
+ isExternalLocation,
3340
+ onCopyEntry,
3341
+ session
3342
+ }) {
3343
+ switch (id) {
3344
+ case "copy": {
3345
+ if (config.copy !== true || !canCopy || isExternalLocation) {
3346
+ return null;
3347
+ }
3348
+ return {
3349
+ disabled,
3350
+ icon: /* @__PURE__ */ jsx8(CopyIcon, { className: previewActionIconClassName }),
3351
+ id,
3352
+ label: copy.t("copyLabel"),
3353
+ onSelect: () => {
3354
+ void (async () => {
3355
+ if (await session.copyToClipboard(entry)) {
3356
+ await onCopyEntry?.();
3357
+ }
3358
+ })();
3359
+ }
3360
+ };
3361
+ }
3362
+ case "open": {
3363
+ if (config.open !== true) {
3364
+ return null;
3365
+ }
3366
+ return {
3367
+ disabled,
3368
+ icon: /* @__PURE__ */ jsx8(EyeIcon, { className: previewActionIconClassName }),
3369
+ id,
3370
+ label: copy.t("openLabel"),
3371
+ onSelect: () => {
3372
+ void session.openEntry(entry);
3373
+ }
3374
+ };
3375
+ }
3376
+ case "download": {
3377
+ const onDownload = config.onDownload;
3378
+ if (!onDownload) {
3379
+ return null;
3380
+ }
3381
+ return {
3382
+ disabled,
3383
+ icon: /* @__PURE__ */ jsx8(DownloadIcon, { className: previewActionIconClassName }),
3384
+ id,
3385
+ label: copy.t("downloadLabel"),
3386
+ onSelect: () => {
3387
+ void onDownload(entry);
3388
+ }
3389
+ };
3390
+ }
3391
+ case "share": {
3392
+ const onShare = config.onShare;
3393
+ if (!onShare) {
3394
+ return null;
3395
+ }
3396
+ return {
3397
+ disabled,
3398
+ icon: /* @__PURE__ */ jsx8(ShareLinedIcon, { className: previewActionIconClassName }),
3399
+ id,
3400
+ label: copy.t("shareLabel"),
3401
+ onSelect: () => {
3402
+ void onShare(entry);
3403
+ }
3404
+ };
3405
+ }
3406
+ default:
3407
+ return null;
3408
+ }
3409
+ }
3410
+
3411
+ // src/ui/workspaceFileManagerVisibleTree.ts
3412
+ function buildWorkspaceFileManagerVisibleTreeRows(input) {
3413
+ return appendWorkspaceFileManagerVisibleTreeRows({
3414
+ ...input,
3415
+ depth: 0
3416
+ });
3417
+ }
3418
+ function collectWorkspaceFileManagerVisibleTreeEntries(rows) {
3419
+ return rows.flatMap((row) => row.kind === "entry" ? [row.entry] : []);
3420
+ }
3421
+ function appendWorkspaceFileManagerVisibleTreeRows(input) {
3422
+ const sortedEntries = sortWorkspaceFileEntriesForArrangeMode(
3423
+ input.entries,
3424
+ input.arrangeMode
3425
+ );
3426
+ const rows = [];
3427
+ for (const entry of sortedEntries) {
3428
+ const expandable = entry.kind === "directory" && entry.hasChildren;
3429
+ const expanded = expandable ? input.expandedDirectoryPaths[entry.path] === true : false;
3430
+ const childState = input.directoryExpansionByPath[entry.path];
3431
+ rows.push({
3432
+ depth: input.depth,
3433
+ entry,
3434
+ expanded,
3435
+ expandable,
3436
+ kind: "entry",
3437
+ loadingChildren: childState?.isLoading ?? false
3438
+ });
3439
+ if (!expanded) {
3440
+ continue;
3441
+ }
3442
+ if (!childState || childState.isLoading) {
3443
+ rows.push({
3444
+ depth: input.depth + 1,
3445
+ key: `${entry.path}:loading`,
3446
+ kind: "feedback",
3447
+ parentPath: entry.path,
3448
+ status: "loading"
3449
+ });
3450
+ continue;
3451
+ }
3452
+ if (childState.error) {
3453
+ rows.push({
3454
+ depth: input.depth + 1,
3455
+ key: `${entry.path}:error`,
3456
+ kind: "feedback",
3457
+ message: childState.error,
3458
+ parentPath: entry.path,
3459
+ status: "error"
3460
+ });
3461
+ continue;
3462
+ }
3463
+ if (childState.loaded && childState.entries.length === 0) {
3464
+ rows.push({
3465
+ depth: input.depth + 1,
3466
+ key: `${entry.path}:empty`,
3467
+ kind: "feedback",
3468
+ parentPath: entry.path,
3469
+ status: "empty"
3470
+ });
3471
+ continue;
3472
+ }
3473
+ rows.push(
3474
+ ...appendWorkspaceFileManagerVisibleTreeRows({
3475
+ ...input,
3476
+ depth: input.depth + 1,
3477
+ entries: childState.entries
3478
+ })
3479
+ );
3480
+ }
3481
+ return rows;
3482
+ }
3483
+
3484
+ // src/ui/WorkspaceFileManagerPanelsContainer.tsx
3485
+ import { jsx as jsx9 } from "react/jsx-runtime";
3486
+ function WorkspaceFileManagerPanelsContainer({
3487
+ arrangeMode,
3488
+ dateLocale,
3489
+ entryDragMode,
3490
+ i18n,
3491
+ layoutMode,
3492
+ onCopyEntry,
3493
+ onDirectoryExpanded,
3494
+ onEntryDragStart,
3495
+ onOpenContextMenu,
3496
+ previewActions,
3497
+ resolveEntryIconUrl,
3498
+ session,
3499
+ showPreviewPanel
3500
+ }) {
3501
+ const { state, view } = useWorkspaceFileManagerPanelsView(session);
3502
+ const arrangedEntries = useMemo4(
3503
+ () => sortWorkspaceFileEntriesForArrangeMode(state.entries, arrangeMode),
3504
+ [arrangeMode, state.entries]
3505
+ );
3506
+ const searchEntries = useMemo4(
3507
+ () => view.searchEntries.map(workspaceFileSearchEntryToEntry),
3508
+ [view.searchEntries]
3509
+ );
3510
+ const searchEntryContextByPath = useMemo4(() => {
3511
+ const contextByPath = /* @__PURE__ */ new Map();
3512
+ for (const entry of view.searchEntries) {
3513
+ contextByPath.set(entry.path, entry.directoryPath);
3514
+ }
3515
+ return contextByPath;
3516
+ }, [view.searchEntries]);
3517
+ const treeRows = useMemo4(
3518
+ () => buildWorkspaceFileManagerVisibleTreeRows({
3519
+ arrangeMode,
3520
+ directoryExpansionByPath: state.directoryExpansionByPath,
3521
+ entries: arrangedEntries,
3522
+ expandedDirectoryPaths: state.expandedDirectoryPaths
3523
+ }),
3524
+ [
3525
+ arrangeMode,
3526
+ arrangedEntries,
3527
+ state.directoryExpansionByPath,
3528
+ state.expandedDirectoryPaths
3529
+ ]
3530
+ );
3531
+ const searchTreeRows = useMemo4(
3532
+ () => searchEntries.map((entry) => ({
3533
+ depth: 0,
3534
+ entry,
3535
+ expanded: false,
3536
+ expandable: false,
3537
+ kind: "entry",
3538
+ loadingChildren: false
3539
+ })),
3540
+ [searchEntries]
3541
+ );
3542
+ const displayedEntries = view.isSearchMode ? searchEntries : arrangedEntries;
3543
+ const displayedTreeRows = view.isSearchMode ? searchTreeRows : treeRows;
3544
+ const visibleTreeEntries = useMemo4(
3545
+ () => collectWorkspaceFileManagerVisibleTreeEntries(displayedTreeRows),
3546
+ [displayedTreeRows]
3547
+ );
3548
+ const panelState = useMemo4(
3549
+ () => ({
3550
+ entries: displayedEntries,
3551
+ error: view.isSearchMode ? view.searchError : state.error,
3552
+ isLoading: view.isSearchMode ? view.isSearching : state.isLoading,
3553
+ isSearchMode: view.isSearchMode
3554
+ }),
3555
+ [
3556
+ displayedEntries,
3557
+ state.error,
3558
+ state.isLoading,
3559
+ view.isSearchMode,
3560
+ view.isSearching,
3561
+ view.searchError
3562
+ ]
3563
+ );
3564
+ const {
3565
+ iconUrlByCacheKey,
3566
+ reportEntryIconViewportEnter,
3567
+ reportEntryIconViewportLeave
3568
+ } = useWorkspaceFileEntryIconUrls({
3569
+ entries: layoutMode === "list" ? visibleTreeEntries : displayedEntries,
3570
+ includeImageThumbnails: true,
3571
+ resolveEntryIconUrl
3572
+ });
3573
+ const resolvedPreviewActions = useWorkspaceFileManagerPreviewActions({
3574
+ config: previewActions,
3575
+ copy: i18n,
3576
+ entry: view.selectedEntry,
3577
+ onCopyEntry,
3578
+ session,
3579
+ state
3580
+ });
3581
+ const handleBlankContextMenu = useCallback4(
3582
+ (event) => {
3583
+ onOpenContextMenu(event, null);
3584
+ },
3585
+ [onOpenContextMenu]
3586
+ );
3587
+ const handleCancelInlineRename = useCallback4(() => {
3588
+ session.cancelInlineRename();
3589
+ }, [session]);
3590
+ const handleClearInlineRenameValidation = useCallback4(() => {
3591
+ session.clearInlineRenameValidation();
3592
+ }, [session]);
3593
+ const handleConfirmInlineRename = useCallback4(
3594
+ (newName) => session.confirmInlineRename(newName),
3595
+ [session]
3596
+ );
3597
+ const handleMoveEntry = useCallback4(
3598
+ (entry, targetDirectoryPath) => {
3599
+ void session.moveEntry(entry, targetDirectoryPath);
3600
+ },
3601
+ [session]
3602
+ );
3603
+ const handleOpenEntry = useCallback4(
3604
+ (entry) => {
3605
+ if (entry.kind === "directory") {
3606
+ onDirectoryExpanded?.(entry.path);
3607
+ }
3608
+ void session.openEntry(entry);
3609
+ },
3610
+ [onDirectoryExpanded, session]
3611
+ );
3612
+ const handleSelect = useCallback4(
3613
+ (path) => {
3614
+ session.select(path);
3615
+ },
3616
+ [session]
3617
+ );
3618
+ const handleToggleDirectoryExpanded = useCallback4(
3619
+ (entry, expanded) => {
3620
+ if (!expanded) {
3621
+ onDirectoryExpanded?.(entry.path);
3622
+ }
3623
+ void session.toggleDirectoryExpanded(entry);
3624
+ },
3625
+ [onDirectoryExpanded, session]
3626
+ );
3627
+ return /* @__PURE__ */ jsx9(
3628
+ WorkspaceFileManagerPanels,
3629
+ {
3630
+ arrangeMode,
3631
+ canMove: view.isSearchMode ? false : view.canMove,
3632
+ contextMenuEntryPath: view.contextMenuEntryPath,
3633
+ copy: i18n,
3634
+ dateLocale,
3635
+ entryContextByPath: view.isSearchMode ? searchEntryContextByPath : null,
3636
+ entryDragMode,
3637
+ iconUrlByCacheKey,
3638
+ inlineRenameEntryPath: view.inlineRenameEntryPath,
3639
+ inlineRenameValidation: view.inlineRenameValidation,
3640
+ isRenaming: view.isRenaming,
3641
+ layoutMode,
3642
+ pendingDirectoryPath: view.pendingDirectoryPath,
3643
+ previewActions: resolvedPreviewActions,
3644
+ previewState: view.previewState,
3645
+ selectedEntry: view.selectedEntry,
3646
+ selectedPath: view.selectedPath,
3647
+ showPreviewPanel,
3648
+ state: panelState,
3649
+ treeRows: displayedTreeRows,
3650
+ onBlankContextMenu: handleBlankContextMenu,
3651
+ onCancelInlineRename: handleCancelInlineRename,
3652
+ onClearInlineRenameValidation: handleClearInlineRenameValidation,
3653
+ onConfirmInlineRename: handleConfirmInlineRename,
3654
+ onEntryContextMenu: onOpenContextMenu,
3655
+ onEntryDragStart,
3656
+ onEntryIconViewportEnter: reportEntryIconViewportEnter,
3657
+ onEntryIconViewportLeave: reportEntryIconViewportLeave,
3658
+ onMoveEntry: handleMoveEntry,
3659
+ onOpenEntry: handleOpenEntry,
3660
+ onSelect: handleSelect,
3661
+ onToggleDirectoryExpanded: handleToggleDirectoryExpanded
3662
+ }
3663
+ );
3664
+ }
3665
+
3666
+ // src/ui/WorkspaceFileManagerToolbar.tsx
3667
+ import {
3668
+ ArrowLeftIcon as ArrowLeftIcon2,
3669
+ ArrowRightIcon as ArrowRightIcon3,
3670
+ Button as Button3,
3671
+ ChevronDownIcon,
3672
+ CloseIcon,
3673
+ DropdownMenu,
3674
+ DropdownMenuContent,
3675
+ DropdownMenuRadioGroup,
3676
+ DropdownMenuRadioItem,
3677
+ DropdownMenuSeparator,
3678
+ DropdownMenuTrigger,
3679
+ Input as Input2,
3680
+ LoadingIcon as LoadingIcon3,
3681
+ RefreshIcon,
3682
+ SearchIcon,
3683
+ Tooltip,
3684
+ TooltipContent,
3685
+ TooltipProvider,
3686
+ TooltipTrigger,
3687
+ ViewGridLinedIcon,
3688
+ ViewListLinedIcon,
3689
+ cn as cn6
3690
+ } from "@tutti-os/ui-system";
3691
+ import { useEffect as useEffect6, useRef as useRef6, useState as useState5 } from "react";
3692
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
3693
+ function WorkspaceFileManagerToolbar({
3694
+ breadcrumbs,
3695
+ canSearch,
3696
+ canGoBack,
3697
+ canGoForward,
3698
+ copy,
3699
+ currentDirectoryPath,
3700
+ isBusy,
3701
+ isLoading,
3702
+ isMutating,
3703
+ isSearching,
3704
+ arrangeMode,
3705
+ layoutMode,
3706
+ renderToolbarTrailingActions,
3707
+ searchQuery,
3708
+ onGoBack,
3709
+ onGoForward,
3710
+ onArrangeModeChange,
3711
+ onLayoutModeChange,
3712
+ onLoadDirectory,
3713
+ onRefresh,
3714
+ onSearchClear,
3715
+ onSearchQueryChange
3716
+ }) {
3717
+ const [refreshAnimationKey, setRefreshAnimationKey] = useState5(0);
3718
+ const handleRefresh = () => {
3719
+ setRefreshAnimationKey((currentKey) => currentKey + 1);
3720
+ onRefresh();
3721
+ };
3722
+ return /* @__PURE__ */ jsxs6("header", { className: "@max-[600px]/workspace-file-manager:flex-nowrap flex h-10 min-h-10 w-full min-w-0 items-center gap-2 border-b border-[var(--border-1)] px-2 py-1", children: [
3723
+ /* @__PURE__ */ jsx10(
3724
+ ToolbarIconButton,
3725
+ {
3726
+ ariaLabel: copy.t("backLabel"),
3727
+ disabled: !canGoBack || isLoading || isBusy,
3728
+ title: copy.t("backLabel"),
3729
+ onClick: onGoBack,
3730
+ children: /* @__PURE__ */ jsx10(ArrowLeftIcon2, { className: "size-4" })
3731
+ }
3732
+ ),
3733
+ /* @__PURE__ */ jsx10(
3734
+ ToolbarIconButton,
3735
+ {
3736
+ ariaLabel: copy.t("forwardLabel"),
3737
+ disabled: !canGoForward || isLoading || isBusy,
3738
+ title: copy.t("forwardLabel"),
3739
+ onClick: onGoForward,
3740
+ children: /* @__PURE__ */ jsx10(ArrowRightIcon3, { className: "size-4" })
3741
+ }
3742
+ ),
3743
+ /* @__PURE__ */ jsx10(
3744
+ "nav",
3745
+ {
3746
+ "aria-label": currentDirectoryPath,
3747
+ className: "@max-[600px]/workspace-file-manager:flex-auto flex min-w-0 flex-1 overflow-hidden pr-2",
3748
+ "data-workspace-file-manager-path": "",
3749
+ children: /* @__PURE__ */ jsx10("ol", { className: "flex min-w-0 max-w-full flex-1 items-center gap-2 overflow-hidden", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsx10(
3750
+ BreadcrumbButton,
3751
+ {
3752
+ active: index === breadcrumbs.length - 1,
3753
+ label: crumb.label,
3754
+ showSeparator: index > 0,
3755
+ onClick: () => {
3756
+ onLoadDirectory(crumb.path);
3757
+ }
3758
+ },
3759
+ crumb.path
3760
+ )) })
3761
+ }
3762
+ ),
3763
+ /* @__PURE__ */ jsxs6("div", { className: "@max-[600px]/workspace-file-manager:justify-end flex flex-none items-center gap-1.5", children: [
3764
+ /* @__PURE__ */ jsx10(
3765
+ LayoutModeToggle,
3766
+ {
3767
+ arrangeMode,
3768
+ copy,
3769
+ layoutMode,
3770
+ onArrangeModeChange,
3771
+ onLayoutModeChange
3772
+ }
3773
+ ),
3774
+ /* @__PURE__ */ jsxs6(
3775
+ ToolbarActionButton,
3776
+ {
3777
+ disabled: isLoading || isMutating || isBusy,
3778
+ onClick: handleRefresh,
3779
+ children: [
3780
+ isLoading ? /* @__PURE__ */ jsx10(LoadingIcon3, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx10(
3781
+ RefreshIcon,
3782
+ {
3783
+ className: cn6(
3784
+ "size-4",
3785
+ refreshAnimationKey > 0 && "motion-safe:animate-[spin_520ms_cubic-bezier(0.4,0,0.2,1)_1_reverse]"
3786
+ )
3787
+ },
3788
+ refreshAnimationKey
3789
+ ),
3790
+ /* @__PURE__ */ jsx10("span", { className: "@max-[600px]/workspace-file-manager:hidden", children: copy.t("refreshLabel") })
3791
+ ]
3792
+ }
3793
+ ),
3794
+ renderToolbarTrailingActions?.({
3795
+ currentDirectoryPath,
3796
+ isBusy,
3797
+ isLoading,
3798
+ isMutating
3799
+ }) ?? null,
3800
+ /* @__PURE__ */ jsx10(
3801
+ WorkspaceFileManagerToolbarSearch,
3802
+ {
3116
3803
  canSearch,
3117
3804
  copy,
3118
3805
  isSearching,
@@ -3132,23 +3819,23 @@ function WorkspaceFileManagerToolbarSearch({
3132
3819
  onClear,
3133
3820
  onSearchQueryChange
3134
3821
  }) {
3135
- const [expanded, setExpanded] = useState4(searchQuery.trim().length > 0);
3136
- const [inputValue, setInputValue] = useState4(searchQuery);
3822
+ const [expanded, setExpanded] = useState5(searchQuery.trim().length > 0);
3823
+ const [inputValue, setInputValue] = useState5(searchQuery);
3137
3824
  const inputRef = useRef6(null);
3138
3825
  const isComposingRef = useRef6(false);
3139
3826
  const active = expanded || searchQuery.trim().length > 0;
3140
- useEffect5(() => {
3827
+ useEffect6(() => {
3141
3828
  if (searchQuery.trim().length > 0) {
3142
3829
  setExpanded(true);
3143
3830
  }
3144
3831
  }, [searchQuery]);
3145
- useEffect5(() => {
3832
+ useEffect6(() => {
3146
3833
  if (!expanded) {
3147
3834
  return;
3148
3835
  }
3149
3836
  inputRef.current?.focus();
3150
3837
  }, [expanded]);
3151
- useEffect5(() => {
3838
+ useEffect6(() => {
3152
3839
  if (isComposingRef.current) {
3153
3840
  return;
3154
3841
  }
@@ -3165,7 +3852,7 @@ function WorkspaceFileManagerToolbarSearch({
3165
3852
  setExpanded(false);
3166
3853
  };
3167
3854
  if (!active) {
3168
- return /* @__PURE__ */ jsx7(
3855
+ return /* @__PURE__ */ jsx10(
3169
3856
  ToolbarIconButton,
3170
3857
  {
3171
3858
  ariaLabel: copy.t("searchPlaceholder"),
@@ -3174,21 +3861,21 @@ function WorkspaceFileManagerToolbarSearch({
3174
3861
  onClick: () => {
3175
3862
  setExpanded(true);
3176
3863
  },
3177
- children: /* @__PURE__ */ jsx7(SearchIcon, { className: "size-4" })
3864
+ children: /* @__PURE__ */ jsx10(SearchIcon, { className: "size-4" })
3178
3865
  }
3179
3866
  );
3180
3867
  }
3181
3868
  return /* @__PURE__ */ jsxs6(
3182
3869
  "div",
3183
3870
  {
3184
- className: cn5(
3871
+ className: cn6(
3185
3872
  "relative h-7 w-[min(220px,34vw)] flex-none overflow-hidden rounded-md bg-[var(--transparency-block)] transition-[width,opacity,background-color] duration-200 ease-out",
3186
3873
  "@max-[600px]/workspace-file-manager:w-[min(170px,42vw)]",
3187
3874
  !canSearch && "opacity-60"
3188
3875
  ),
3189
3876
  children: [
3190
- /* @__PURE__ */ jsx7(SearchIcon, { className: "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
3191
- /* @__PURE__ */ jsx7(
3877
+ /* @__PURE__ */ jsx10(SearchIcon, { className: "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
3878
+ /* @__PURE__ */ jsx10(
3192
3879
  Input2,
3193
3880
  {
3194
3881
  "aria-label": copy.t("searchPlaceholder"),
@@ -3227,14 +3914,14 @@ function WorkspaceFileManagerToolbarSearch({
3227
3914
  }
3228
3915
  }
3229
3916
  ),
3230
- /* @__PURE__ */ jsx7(
3917
+ /* @__PURE__ */ jsx10(
3231
3918
  "button",
3232
3919
  {
3233
3920
  "aria-label": copy.t("clearSearchLabel"),
3234
3921
  className: "absolute top-1/2 right-1 grid size-5 -translate-y-1/2 place-items-center rounded-[4px] text-[var(--text-tertiary)] transition-colors hover:bg-[var(--transparency-hover)] hover:text-[var(--text-primary)]",
3235
3922
  type: "button",
3236
3923
  onClick: handleClear,
3237
- children: isSearching ? /* @__PURE__ */ jsx7(LoadingIcon3, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx7(CloseIcon, { className: "size-3" })
3924
+ children: isSearching ? /* @__PURE__ */ jsx10(LoadingIcon3, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx10(CloseIcon, { className: "size-3" })
3238
3925
  }
3239
3926
  )
3240
3927
  ]
@@ -3259,13 +3946,13 @@ function LayoutModeToggle({
3259
3946
  { label: copy.t("arrangeCreatedLabel"), mode: "created" },
3260
3947
  { label: copy.t("sizeLabel"), mode: "size" }
3261
3948
  ];
3262
- return /* @__PURE__ */ jsx7(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs6(
3949
+ return /* @__PURE__ */ jsx10(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs6(
3263
3950
  "div",
3264
3951
  {
3265
3952
  className: "flex items-center rounded-md border border-[var(--border-1)] bg-[var(--transparency-block)] p-0.5",
3266
3953
  role: "group",
3267
3954
  children: [
3268
- /* @__PURE__ */ jsx7(
3955
+ /* @__PURE__ */ jsx10(
3269
3956
  LayoutModeButton,
3270
3957
  {
3271
3958
  active: layoutMode === "icon",
@@ -3274,10 +3961,10 @@ function LayoutModeToggle({
3274
3961
  onClick: () => {
3275
3962
  onLayoutModeChange("icon");
3276
3963
  },
3277
- children: /* @__PURE__ */ jsx7(ViewGridLinedIcon, { className: "size-4" })
3964
+ children: /* @__PURE__ */ jsx10(ViewGridLinedIcon, { className: "size-4" })
3278
3965
  }
3279
3966
  ),
3280
- /* @__PURE__ */ jsx7(
3967
+ /* @__PURE__ */ jsx10(
3281
3968
  LayoutModeButton,
3282
3969
  {
3283
3970
  active: layoutMode === "list",
@@ -3286,30 +3973,30 @@ function LayoutModeToggle({
3286
3973
  onClick: () => {
3287
3974
  onLayoutModeChange("list");
3288
3975
  },
3289
- children: /* @__PURE__ */ jsx7(ViewListLinedIcon, { className: "size-4" })
3976
+ children: /* @__PURE__ */ jsx10(ViewListLinedIcon, { className: "size-4" })
3290
3977
  }
3291
3978
  ),
3292
- /* @__PURE__ */ jsx7("span", { className: "mx-0.5 h-4 w-px bg-[var(--border-1)]", "aria-hidden": true }),
3979
+ /* @__PURE__ */ jsx10("span", { className: "mx-0.5 h-4 w-px bg-[var(--border-1)]", "aria-hidden": true }),
3293
3980
  /* @__PURE__ */ jsxs6(DropdownMenu, { children: [
3294
- /* @__PURE__ */ jsx7(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
3295
- Button2,
3981
+ /* @__PURE__ */ jsx10(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
3982
+ Button3,
3296
3983
  {
3297
3984
  "aria-label": copy.t("arrangeMenuLabel"),
3298
3985
  className: "size-6 min-w-6 rounded-[4px] p-0 text-[var(--text-secondary)] data-[state=open]:bg-[var(--background-fronted)] data-[state=open]:text-[var(--text-primary)] data-[state=open]:shadow-sm",
3299
3986
  size: "icon-sm",
3300
3987
  type: "button",
3301
3988
  variant: "ghost",
3302
- children: /* @__PURE__ */ jsx7(ChevronDownIcon, { className: "size-3.5" })
3989
+ children: /* @__PURE__ */ jsx10(ChevronDownIcon, { className: "size-3.5" })
3303
3990
  }
3304
3991
  ) }),
3305
- /* @__PURE__ */ jsx7(
3992
+ /* @__PURE__ */ jsx10(
3306
3993
  DropdownMenuContent,
3307
3994
  {
3308
3995
  align: "end",
3309
3996
  className: "min-w-[236px] px-1 py-1",
3310
3997
  sideOffset: 7,
3311
3998
  style: { zIndex: "var(--z-panel-popover)" },
3312
- children: /* @__PURE__ */ jsx7(
3999
+ children: /* @__PURE__ */ jsx10(
3313
4000
  DropdownMenuRadioGroup,
3314
4001
  {
3315
4002
  value: arrangeMode,
@@ -3319,8 +4006,8 @@ function LayoutModeToggle({
3319
4006
  );
3320
4007
  },
3321
4008
  children: arrangeOptions.map((option, index) => /* @__PURE__ */ jsxs6("div", { children: [
3322
- index === 1 ? /* @__PURE__ */ jsx7(DropdownMenuSeparator, {}) : null,
3323
- /* @__PURE__ */ jsx7(
4009
+ index === 1 ? /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}) : null,
4010
+ /* @__PURE__ */ jsx10(
3324
4011
  DropdownMenuRadioItem,
3325
4012
  {
3326
4013
  className: "h-8 text-sm font-normal",
@@ -3346,12 +4033,12 @@ function LayoutModeButton({
3346
4033
  tooltipLabel
3347
4034
  }) {
3348
4035
  return /* @__PURE__ */ jsxs6(Tooltip, { children: [
3349
- /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
3350
- Button2,
4036
+ /* @__PURE__ */ jsx10(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
4037
+ Button3,
3351
4038
  {
3352
4039
  "aria-label": ariaLabel,
3353
4040
  "aria-pressed": active,
3354
- className: cn5(
4041
+ className: cn6(
3355
4042
  "size-6 min-w-6 rounded-[4px] p-0 text-text-secondary",
3356
4043
  active && "!bg-background-fronted text-foreground hover:!bg-background-fronted"
3357
4044
  ),
@@ -3362,7 +4049,7 @@ function LayoutModeButton({
3362
4049
  children
3363
4050
  }
3364
4051
  ) }),
3365
- /* @__PURE__ */ jsx7(TooltipContent, { side: "bottom", children: tooltipLabel })
4052
+ /* @__PURE__ */ jsx10(TooltipContent, { side: "bottom", children: tooltipLabel })
3366
4053
  ] });
3367
4054
  }
3368
4055
  function ToolbarIconButton({
@@ -3372,8 +4059,8 @@ function ToolbarIconButton({
3372
4059
  onClick,
3373
4060
  title
3374
4061
  }) {
3375
- return /* @__PURE__ */ jsx7(
3376
- Button2,
4062
+ return /* @__PURE__ */ jsx10(
4063
+ Button3,
3377
4064
  {
3378
4065
  "aria-label": ariaLabel,
3379
4066
  className: "size-7 min-w-7 rounded-sm p-0 text-[var(--text-primary)]",
@@ -3392,8 +4079,8 @@ function ToolbarActionButton({
3392
4079
  disabled,
3393
4080
  onClick
3394
4081
  }) {
3395
- return /* @__PURE__ */ jsx7(
3396
- Button2,
4082
+ return /* @__PURE__ */ jsx10(
4083
+ Button3,
3397
4084
  {
3398
4085
  className: "@max-[600px]/workspace-file-manager:size-7 @max-[600px]/workspace-file-manager:min-w-7 @max-[600px]/workspace-file-manager:px-0 cursor-pointer",
3399
4086
  disabled,
@@ -3412,11 +4099,11 @@ function BreadcrumbButton({
3412
4099
  showSeparator
3413
4100
  }) {
3414
4101
  return /* @__PURE__ */ jsxs6("li", { className: "flex min-w-0 items-center gap-2", children: [
3415
- showSeparator ? /* @__PURE__ */ jsx7("span", { className: "flex-none text-[var(--text-tertiary)]", children: "/" }) : null,
3416
- /* @__PURE__ */ jsx7(
4102
+ showSeparator ? /* @__PURE__ */ jsx10("span", { className: "flex-none text-[var(--text-tertiary)]", children: "/" }) : null,
4103
+ /* @__PURE__ */ jsx10(
3417
4104
  "button",
3418
4105
  {
3419
- className: cn5(
4106
+ className: cn6(
3420
4107
  "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap border-0 bg-transparent text-sm font-normal transition-colors",
3421
4108
  active ? "font-semibold text-[var(--text-primary)]" : "text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
3422
4109
  ),
@@ -3437,9 +4124,9 @@ import {
3437
4124
  TooltipContent as TooltipContent2,
3438
4125
  TooltipProvider as TooltipProvider2,
3439
4126
  TooltipTrigger as TooltipTrigger2,
3440
- cn as cn6
4127
+ cn as cn7
3441
4128
  } from "@tutti-os/ui-system";
3442
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
4129
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
3443
4130
  function WorkspaceFileManagerSidebar({
3444
4131
  disabled,
3445
4132
  locationSections,
@@ -3453,14 +4140,14 @@ function WorkspaceFileManagerSidebar({
3453
4140
  if (visibleSections.length === 0) {
3454
4141
  return null;
3455
4142
  }
3456
- return /* @__PURE__ */ jsx8(
4143
+ return /* @__PURE__ */ jsx11(
3457
4144
  "aside",
3458
4145
  {
3459
4146
  className: "@max-[600px]/workspace-file-manager:hidden flex shrink-0 flex-col border-r border-[var(--border-1)] bg-[var(--background-panel)]",
3460
4147
  style: { minWidth: width, width },
3461
- children: /* @__PURE__ */ jsx8(ScrollArea2, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx8(TooltipProvider2, { delayDuration: 350, children: /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-3 px-2 py-3", children: visibleSections.map((section) => /* @__PURE__ */ jsxs7("section", { className: "min-w-0", children: [
3462
- /* @__PURE__ */ jsx8("h2", { className: "mb-1 px-2 text-[11px] font-medium uppercase tracking-normal text-[var(--text-tertiary)]", children: section.label }),
3463
- /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-0.5", children: section.locations.map((location) => /* @__PURE__ */ jsx8(
4148
+ children: /* @__PURE__ */ jsx11(ScrollArea2, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx11(TooltipProvider2, { delayDuration: 350, children: /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-3 px-2 py-3", children: visibleSections.map((section) => /* @__PURE__ */ jsxs7("section", { className: "min-w-0", children: [
4149
+ /* @__PURE__ */ jsx11("h2", { className: "mb-1 px-2 text-[11px] font-medium uppercase tracking-normal text-[var(--text-tertiary)]", children: section.label }),
4150
+ /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: section.locations.map((location) => /* @__PURE__ */ jsx11(
3464
4151
  WorkspaceFileManagerSidebarLocation,
3465
4152
  {
3466
4153
  active: location.id === selectedLocationId,
@@ -3485,7 +4172,7 @@ function WorkspaceFileManagerSidebarLocation({
3485
4172
  "button",
3486
4173
  {
3487
4174
  "aria-current": active ? "page" : void 0,
3488
- className: cn6(
4175
+ className: cn7(
3489
4176
  "flex h-8 min-w-0 items-center gap-2 rounded-md border border-transparent px-2 text-left text-sm text-[var(--text-secondary)] transition-colors",
3490
4177
  active ? "border-[var(--line-2)] bg-[var(--background-fronted)] text-[var(--text-primary)] shadow-none" : "hover:bg-[var(--transparency-block)] hover:text-[var(--text-primary)]",
3491
4178
  disabled && "pointer-events-none opacity-60"
@@ -3496,8 +4183,8 @@ function WorkspaceFileManagerSidebarLocation({
3496
4183
  onSelectLocation(location);
3497
4184
  },
3498
4185
  children: [
3499
- location.kind === "external" && location.iconUrl ? /* @__PURE__ */ jsx8(ExternalLocationIcon, { location }) : /* @__PURE__ */ jsx8(Icon, { className: "size-4 flex-none" }),
3500
- /* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: location.label })
4186
+ location.kind === "external" && location.iconUrl ? /* @__PURE__ */ jsx11(ExternalLocationIcon, { location }) : /* @__PURE__ */ jsx11(Icon, { className: "size-4 flex-none" }),
4187
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: location.label })
3501
4188
  ]
3502
4189
  }
3503
4190
  );
@@ -3505,14 +4192,14 @@ function WorkspaceFileManagerSidebarLocation({
3505
4192
  return content;
3506
4193
  }
3507
4194
  return /* @__PURE__ */ jsxs7(Tooltip2, { children: [
3508
- /* @__PURE__ */ jsx8(TooltipTrigger2, { asChild: true, children: content }),
3509
- /* @__PURE__ */ jsx8(TooltipContent2, { side: "right", children: location.contextLabel })
4195
+ /* @__PURE__ */ jsx11(TooltipTrigger2, { asChild: true, children: content }),
4196
+ /* @__PURE__ */ jsx11(TooltipContent2, { side: "right", children: location.contextLabel })
3510
4197
  ] });
3511
4198
  }
3512
4199
  function ExternalLocationIcon({
3513
4200
  location
3514
4201
  }) {
3515
- return /* @__PURE__ */ jsx8(
4202
+ return /* @__PURE__ */ jsx11(
3516
4203
  "img",
3517
4204
  {
3518
4205
  alt: "",
@@ -3523,283 +4210,58 @@ function ExternalLocationIcon({
3523
4210
  }
3524
4211
 
3525
4212
  // src/ui/useWorkspaceFileManagerArrangeMode.ts
3526
- import { useCallback as useCallback3, useState as useState5 } from "react";
4213
+ import { useCallback as useCallback5, useState as useState6 } from "react";
3527
4214
  function useWorkspaceFileManagerArrangeMode() {
3528
- const [arrangeMode, setArrangeModeState] = useState5(
4215
+ const [arrangeMode, setArrangeModeState] = useState6(
3529
4216
  readWorkspaceFileManagerArrangeMode
3530
4217
  );
3531
- const setArrangeMode = useCallback3(
3532
- (nextArrangeMode) => {
3533
- setArrangeModeState(nextArrangeMode);
3534
- writeWorkspaceFileManagerArrangeMode(nextArrangeMode);
3535
- },
3536
- []
3537
- );
3538
- return { arrangeMode, setArrangeMode };
3539
- }
3540
-
3541
- // src/ui/useWorkspaceFileManagerLayoutMode.ts
3542
- import { useCallback as useCallback4, useState as useState6 } from "react";
3543
-
3544
- // src/ui/workspaceFileManagerLayoutMode.ts
3545
- var workspaceFileManagerLayoutModeStorageKey = "tutti.workspace-file-manager.layout-mode";
3546
- function readWorkspaceFileManagerLayoutMode() {
3547
- if (typeof window === "undefined") {
3548
- return "list";
3549
- }
3550
- const stored = window.localStorage.getItem(
3551
- workspaceFileManagerLayoutModeStorageKey
3552
- );
3553
- return stored === "icon" ? "icon" : "list";
3554
- }
3555
- function writeWorkspaceFileManagerLayoutMode(layoutMode) {
3556
- if (typeof window === "undefined") {
3557
- return;
3558
- }
3559
- window.localStorage.setItem(
3560
- workspaceFileManagerLayoutModeStorageKey,
3561
- layoutMode
3562
- );
3563
- }
3564
-
3565
- // src/ui/useWorkspaceFileManagerLayoutMode.ts
3566
- function useWorkspaceFileManagerLayoutMode() {
3567
- const [layoutMode, setLayoutModeState] = useState6(
3568
- readWorkspaceFileManagerLayoutMode
3569
- );
3570
- const setLayoutMode = useCallback4(
3571
- (nextLayoutMode) => {
3572
- setLayoutModeState(nextLayoutMode);
3573
- writeWorkspaceFileManagerLayoutMode(nextLayoutMode);
3574
- },
3575
- []
3576
- );
3577
- return { layoutMode, setLayoutMode };
3578
- }
3579
-
3580
- // src/ui/useWorkspaceFileEntryIconUrls.ts
3581
- import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo2, useState as useState7 } from "react";
3582
-
3583
- // src/ui/workspaceFileEntryIconUrlQueue.ts
3584
- var defaultMaxConcurrentIconRequests = 3;
3585
- var imageThumbnailCacheKeyPrefix = "image-thumbnail:";
3586
- function shouldKeepResolvedIconAfterViewportLeave(cacheKey) {
3587
- return cacheKey.startsWith(imageThumbnailCacheKeyPrefix);
3588
- }
3589
- function createWorkspaceFileEntryIconUrlQueue(options) {
3590
- const maxConcurrent = Math.max(
3591
- 1,
3592
- Math.floor(options.maxConcurrent ?? defaultMaxConcurrentIconRequests)
3593
- );
3594
- const iconUrlByCacheKey = /* @__PURE__ */ new Map();
3595
- const inFlightCacheKeys = /* @__PURE__ */ new Set();
3596
- const listeners = /* @__PURE__ */ new Set();
3597
- const queuedEntries = /* @__PURE__ */ new Map();
3598
- const visibleReferenceCountByCacheKey = /* @__PURE__ */ new Map();
3599
- let activeCount = 0;
3600
- let disposed = false;
3601
- let retainedCacheKeys = null;
3602
- const policyOptions = {
3603
- includeImageThumbnails: options.includeImageThumbnails
3604
- };
3605
- function publish() {
3606
- for (const listener of listeners) {
3607
- listener();
3608
- }
3609
- }
3610
- function isRetained(cacheKey) {
3611
- return retainedCacheKeys === null || retainedCacheKeys.has(cacheKey);
3612
- }
3613
- function isVisible(cacheKey) {
3614
- return (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) > 0;
3615
- }
3616
- function shouldResolveCacheKey(cacheKey) {
3617
- return isVisible(cacheKey) || shouldKeepResolvedIconAfterViewportLeave(cacheKey);
3618
- }
3619
- function retainVisibleReference(cacheKey) {
3620
- visibleReferenceCountByCacheKey.set(
3621
- cacheKey,
3622
- (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) + 1
3623
- );
3624
- }
3625
- function releaseVisibleReference(cacheKey) {
3626
- const nextCount = (visibleReferenceCountByCacheKey.get(cacheKey) ?? 0) - 1;
3627
- if (nextCount > 0) {
3628
- visibleReferenceCountByCacheKey.set(cacheKey, nextCount);
3629
- return;
3630
- }
3631
- visibleReferenceCountByCacheKey.delete(cacheKey);
3632
- if (shouldKeepResolvedIconAfterViewportLeave(cacheKey)) {
3633
- return;
3634
- }
3635
- queuedEntries.delete(cacheKey);
3636
- const hadCachedIcon = iconUrlByCacheKey.delete(cacheKey);
3637
- if (hadCachedIcon) {
3638
- publish();
3639
- }
3640
- }
3641
- function drain() {
3642
- if (disposed || !options.resolveEntryIconUrl) {
3643
- return;
3644
- }
3645
- while (activeCount < maxConcurrent && queuedEntries.size > 0) {
3646
- const next = queuedEntries.entries().next().value;
3647
- if (!next) {
3648
- return;
3649
- }
3650
- const [cacheKey, entry] = next;
3651
- queuedEntries.delete(cacheKey);
3652
- if (!shouldResolveCacheKey(cacheKey) || iconUrlByCacheKey.has(cacheKey) || inFlightCacheKeys.has(cacheKey)) {
3653
- continue;
3654
- }
3655
- activeCount += 1;
3656
- inFlightCacheKeys.add(cacheKey);
3657
- void options.resolveEntryIconUrl(entry).then((iconUrl) => {
3658
- const shouldStore = !disposed && isRetained(cacheKey) && shouldResolveCacheKey(cacheKey);
3659
- if (shouldStore) {
3660
- iconUrlByCacheKey.set(cacheKey, iconUrl?.trim() || null);
3661
- publish();
3662
- }
3663
- }).catch(() => {
3664
- const shouldStore = !disposed && isRetained(cacheKey) && shouldResolveCacheKey(cacheKey);
3665
- if (shouldStore) {
3666
- iconUrlByCacheKey.set(cacheKey, null);
3667
- publish();
3668
- }
3669
- }).finally(() => {
3670
- activeCount -= 1;
3671
- inFlightCacheKeys.delete(cacheKey);
3672
- drain();
3673
- });
3674
- }
3675
- }
3676
- return {
3677
- activate() {
3678
- disposed = false;
3679
- },
3680
- dispose() {
3681
- disposed = true;
3682
- queuedEntries.clear();
3683
- visibleReferenceCountByCacheKey.clear();
3684
- listeners.clear();
3685
- },
3686
- leaveViewport(entry) {
3687
- if (disposed || !shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)) {
3688
- return;
3689
- }
3690
- const cacheKey = resolveWorkspaceFileEntryIconCacheKey(entry);
3691
- releaseVisibleReference(cacheKey);
3692
- },
3693
- enterViewport(entry) {
3694
- if (disposed || !options.resolveEntryIconUrl || !shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)) {
3695
- return;
3696
- }
3697
- const cacheKey = resolveWorkspaceFileEntryIconCacheKey(entry);
3698
- retainVisibleReference(cacheKey);
3699
- if (iconUrlByCacheKey.has(cacheKey) || inFlightCacheKeys.has(cacheKey) || queuedEntries.has(cacheKey)) {
3700
- return;
3701
- }
3702
- queuedEntries.set(cacheKey, entry);
3703
- drain();
3704
- },
3705
- retainEntries(entries) {
3706
- retainedCacheKeys = new Set(
3707
- entries.filter(
3708
- (entry) => shouldResolveWorkspaceFileEntryIcon(entry, policyOptions)
3709
- ).map((entry) => resolveWorkspaceFileEntryIconCacheKey(entry))
3710
- );
3711
- let changed = false;
3712
- for (const cacheKey of iconUrlByCacheKey.keys()) {
3713
- if (!isRetained(cacheKey)) {
3714
- iconUrlByCacheKey.delete(cacheKey);
3715
- changed = true;
3716
- continue;
3717
- }
3718
- if (iconUrlByCacheKey.get(cacheKey) === null) {
3719
- iconUrlByCacheKey.delete(cacheKey);
3720
- changed = true;
3721
- }
3722
- }
3723
- for (const cacheKey of queuedEntries.keys()) {
3724
- if (!isRetained(cacheKey)) {
3725
- queuedEntries.delete(cacheKey);
3726
- }
3727
- }
3728
- for (const cacheKey of visibleReferenceCountByCacheKey.keys()) {
3729
- if (!isRetained(cacheKey)) {
3730
- visibleReferenceCountByCacheKey.delete(cacheKey);
3731
- }
3732
- }
3733
- if (changed) {
3734
- publish();
3735
- }
3736
- },
3737
- snapshot() {
3738
- return new Map(iconUrlByCacheKey);
3739
- },
3740
- subscribe(listener) {
3741
- if (disposed) {
3742
- return () => {
3743
- };
3744
- }
3745
- listeners.add(listener);
3746
- return () => {
3747
- listeners.delete(listener);
3748
- };
3749
- }
3750
- };
3751
- }
3752
-
3753
- // src/ui/useWorkspaceFileEntryIconUrls.ts
3754
- function buildIconTargetSignature(entries, options) {
3755
- return entries.filter((entry) => shouldResolveWorkspaceFileEntryIcon(entry, options)).map((entry) => resolveWorkspaceFileEntryIconCacheKey(entry)).join("\0");
3756
- }
3757
- function useWorkspaceFileEntryIconUrls(input) {
3758
- const { entries, includeImageThumbnails, resolveEntryIconUrl } = input;
3759
- const queue = useMemo2(
3760
- () => createWorkspaceFileEntryIconUrlQueue({
3761
- includeImageThumbnails,
3762
- resolveEntryIconUrl
3763
- }),
3764
- [includeImageThumbnails, resolveEntryIconUrl]
3765
- );
3766
- const [iconUrlByCacheKey, setIconUrlByCacheKey] = useState7(() => queue.snapshot());
3767
- const iconTargetSignature = useMemo2(
3768
- () => buildIconTargetSignature(entries, { includeImageThumbnails }),
3769
- [entries, includeImageThumbnails]
3770
- );
3771
- useEffect6(() => {
3772
- queue.activate();
3773
- return () => {
3774
- queue.dispose();
3775
- };
3776
- }, [queue]);
3777
- useEffect6(() => {
3778
- queue.retainEntries(entries);
3779
- }, [entries, iconTargetSignature, queue]);
3780
- useEffect6(() => {
3781
- setIconUrlByCacheKey(queue.snapshot());
3782
- return queue.subscribe(() => {
3783
- setIconUrlByCacheKey(queue.snapshot());
3784
- });
3785
- }, [queue]);
3786
- const reportEntryIconViewportEnter = useCallback5(
3787
- (entry) => {
3788
- queue.enterViewport(entry);
4218
+ const setArrangeMode = useCallback5(
4219
+ (nextArrangeMode) => {
4220
+ setArrangeModeState(nextArrangeMode);
4221
+ writeWorkspaceFileManagerArrangeMode(nextArrangeMode);
3789
4222
  },
3790
- [queue]
4223
+ []
3791
4224
  );
3792
- const reportEntryIconViewportLeave = useCallback5(
3793
- (entry) => {
3794
- queue.leaveViewport(entry);
4225
+ return { arrangeMode, setArrangeMode };
4226
+ }
4227
+
4228
+ // src/ui/useWorkspaceFileManagerLayoutMode.ts
4229
+ import { useCallback as useCallback6, useState as useState7 } from "react";
4230
+
4231
+ // src/ui/workspaceFileManagerLayoutMode.ts
4232
+ var workspaceFileManagerLayoutModeStorageKey = "tutti.workspace-file-manager.layout-mode";
4233
+ function readWorkspaceFileManagerLayoutMode() {
4234
+ if (typeof window === "undefined") {
4235
+ return "list";
4236
+ }
4237
+ const stored = window.localStorage.getItem(
4238
+ workspaceFileManagerLayoutModeStorageKey
4239
+ );
4240
+ return stored === "icon" ? "icon" : "list";
4241
+ }
4242
+ function writeWorkspaceFileManagerLayoutMode(layoutMode) {
4243
+ if (typeof window === "undefined") {
4244
+ return;
4245
+ }
4246
+ window.localStorage.setItem(
4247
+ workspaceFileManagerLayoutModeStorageKey,
4248
+ layoutMode
4249
+ );
4250
+ }
4251
+
4252
+ // src/ui/useWorkspaceFileManagerLayoutMode.ts
4253
+ function useWorkspaceFileManagerLayoutMode() {
4254
+ const [layoutMode, setLayoutModeState] = useState7(
4255
+ readWorkspaceFileManagerLayoutMode
4256
+ );
4257
+ const setLayoutMode = useCallback6(
4258
+ (nextLayoutMode) => {
4259
+ setLayoutModeState(nextLayoutMode);
4260
+ writeWorkspaceFileManagerLayoutMode(nextLayoutMode);
3795
4261
  },
3796
- [queue]
4262
+ []
3797
4263
  );
3798
- return {
3799
- iconUrlByCacheKey,
3800
- reportEntryIconViewportEnter,
3801
- reportEntryIconViewportLeave
3802
- };
4264
+ return { layoutMode, setLayoutMode };
3803
4265
  }
3804
4266
 
3805
4267
  // src/ui/workspaceFileManagerAnalytics.ts
@@ -3810,81 +4272,8 @@ function normalizeDirectoryPath(path) {
3810
4272
  return path.trim().replaceAll("\\", "/").replace(/\/+$/u, "");
3811
4273
  }
3812
4274
 
3813
- // src/ui/workspaceFileManagerVisibleTree.ts
3814
- function buildWorkspaceFileManagerVisibleTreeRows(input) {
3815
- return appendWorkspaceFileManagerVisibleTreeRows({
3816
- ...input,
3817
- depth: 0
3818
- });
3819
- }
3820
- function collectWorkspaceFileManagerVisibleTreeEntries(rows) {
3821
- return rows.flatMap((row) => row.kind === "entry" ? [row.entry] : []);
3822
- }
3823
- function appendWorkspaceFileManagerVisibleTreeRows(input) {
3824
- const sortedEntries = sortWorkspaceFileEntriesForArrangeMode(
3825
- input.entries,
3826
- input.arrangeMode
3827
- );
3828
- const rows = [];
3829
- for (const entry of sortedEntries) {
3830
- const expandable = entry.kind === "directory" && entry.hasChildren;
3831
- const expanded = expandable ? input.expandedDirectoryPaths[entry.path] === true : false;
3832
- const childState = input.directoryExpansionByPath[entry.path];
3833
- rows.push({
3834
- depth: input.depth,
3835
- entry,
3836
- expanded,
3837
- expandable,
3838
- kind: "entry",
3839
- loadingChildren: childState?.isLoading ?? false
3840
- });
3841
- if (!expanded) {
3842
- continue;
3843
- }
3844
- if (!childState || childState.isLoading) {
3845
- rows.push({
3846
- depth: input.depth + 1,
3847
- key: `${entry.path}:loading`,
3848
- kind: "feedback",
3849
- parentPath: entry.path,
3850
- status: "loading"
3851
- });
3852
- continue;
3853
- }
3854
- if (childState.error) {
3855
- rows.push({
3856
- depth: input.depth + 1,
3857
- key: `${entry.path}:error`,
3858
- kind: "feedback",
3859
- message: childState.error,
3860
- parentPath: entry.path,
3861
- status: "error"
3862
- });
3863
- continue;
3864
- }
3865
- if (childState.loaded && childState.entries.length === 0) {
3866
- rows.push({
3867
- depth: input.depth + 1,
3868
- key: `${entry.path}:empty`,
3869
- kind: "feedback",
3870
- parentPath: entry.path,
3871
- status: "empty"
3872
- });
3873
- continue;
3874
- }
3875
- rows.push(
3876
- ...appendWorkspaceFileManagerVisibleTreeRows({
3877
- ...input,
3878
- depth: input.depth + 1,
3879
- entries: childState.entries
3880
- })
3881
- );
3882
- }
3883
- return rows;
3884
- }
3885
-
3886
4275
  // src/ui/WorkspaceFileManager.tsx
3887
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
4276
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
3888
4277
  var workspaceFileManagerSearchDebounceMs = 180;
3889
4278
  function WorkspaceFileManager({
3890
4279
  className,
@@ -3894,6 +4283,7 @@ function WorkspaceFileManager({
3894
4283
  onCopyEntry,
3895
4284
  onDirectoryExpanded,
3896
4285
  onEntryDragStart,
4286
+ previewActions,
3897
4287
  resolveContextMenu,
3898
4288
  resolveEntryIconUrl,
3899
4289
  renderExternalLocationContent,
@@ -3913,7 +4303,7 @@ function WorkspaceFileManager({
3913
4303
  const { layoutMode, setLayoutMode } = useWorkspaceFileManagerLayoutMode();
3914
4304
  const rootView = useWorkspaceFileManagerRootView(session);
3915
4305
  const { state: panelsState, view: panelsView } = useWorkspaceFileManagerPanelsView(session);
3916
- const selectedExternalLocation = useMemo3(() => {
4306
+ const selectedExternalLocation = useMemo5(() => {
3917
4307
  const location = findWorkspaceFileLocationById(
3918
4308
  rootView.locationSections,
3919
4309
  rootView.selectedLocationId
@@ -3926,7 +4316,7 @@ function WorkspaceFileManager({
3926
4316
  containerWidth,
3927
4317
  sidebarContentMinWidth
3928
4318
  ) : workspaceFileManagerSidebarDefaultWidth;
3929
- const updateSidebarWidth = useCallback6(
4319
+ const updateSidebarWidth = useCallback7(
3930
4320
  (width) => {
3931
4321
  const nextWidth = clampWorkspaceFileManagerSidebarWidth({
3932
4322
  containerWidth: rootRef.current?.getBoundingClientRect().width ?? 0,
@@ -3969,7 +4359,7 @@ function WorkspaceFileManager({
3969
4359
  observer.disconnect();
3970
4360
  };
3971
4361
  }, [hasLocationSidebar, sidebarContentMinWidth]);
3972
- const handleSidebarResizePointerDown = useCallback6(
4362
+ const handleSidebarResizePointerDown = useCallback7(
3973
4363
  (event) => {
3974
4364
  if (event.button !== 0) {
3975
4365
  return;
@@ -3998,7 +4388,7 @@ function WorkspaceFileManager({
3998
4388
  },
3999
4389
  [sidebarWidth]
4000
4390
  );
4001
- const handleSidebarResizePointerMove = useCallback6(
4391
+ const handleSidebarResizePointerMove = useCallback7(
4002
4392
  (event) => {
4003
4393
  const resize = sidebarResizeRef.current;
4004
4394
  if (!resize || resize.pointerId !== event.pointerId) {
@@ -4010,7 +4400,7 @@ function WorkspaceFileManager({
4010
4400
  },
4011
4401
  [updateSidebarWidth]
4012
4402
  );
4013
- const handleSidebarResizePointerEnd = useCallback6(
4403
+ const handleSidebarResizePointerEnd = useCallback7(
4014
4404
  (event) => {
4015
4405
  const resize = sidebarResizeRef.current;
4016
4406
  if (!resize || resize.pointerId !== event.pointerId) {
@@ -4022,7 +4412,7 @@ function WorkspaceFileManager({
4022
4412
  },
4023
4413
  []
4024
4414
  );
4025
- const handleSidebarResizeKeyDown = useCallback6(
4415
+ const handleSidebarResizeKeyDown = useCallback7(
4026
4416
  (event) => {
4027
4417
  let nextWidth = null;
4028
4418
  if (event.key === "Home") {
@@ -4066,8 +4456,9 @@ function WorkspaceFileManager({
4066
4456
  }
4067
4457
  event.preventDefault();
4068
4458
  void (async () => {
4069
- await session.copyToClipboard(entry);
4070
- await onCopyEntry?.();
4459
+ if (await session.copyToClipboard(entry)) {
4460
+ await onCopyEntry?.();
4461
+ }
4071
4462
  })();
4072
4463
  }
4073
4464
  window.addEventListener("keydown", handleCopyShortcut);
@@ -4115,31 +4506,34 @@ function WorkspaceFileManager({
4115
4506
  selectedExternalLocation,
4116
4507
  session
4117
4508
  ]);
4118
- function openContextMenu(event, entry) {
4119
- event.preventDefault();
4120
- event.stopPropagation();
4121
- const menuWidth = 220;
4122
- const menuHeight = 280;
4123
- const x = clampContextMenuCoordinate(
4124
- event.clientX,
4125
- window.innerWidth,
4126
- menuWidth
4127
- );
4128
- const y = clampContextMenuCoordinate(
4129
- event.clientY,
4130
- window.innerHeight,
4131
- menuHeight
4132
- );
4133
- session.openContextMenu({
4134
- entryPath: entry?.path ?? null,
4135
- x,
4136
- y
4137
- });
4138
- }
4509
+ const openContextMenu = useCallback7(
4510
+ (event, entry) => {
4511
+ event.preventDefault();
4512
+ event.stopPropagation();
4513
+ const menuWidth = 220;
4514
+ const menuHeight = 280;
4515
+ const x = clampContextMenuCoordinate(
4516
+ event.clientX,
4517
+ window.innerWidth,
4518
+ menuWidth
4519
+ );
4520
+ const y = clampContextMenuCoordinate(
4521
+ event.clientY,
4522
+ window.innerHeight,
4523
+ menuHeight
4524
+ );
4525
+ session.openContextMenu({
4526
+ entryPath: entry?.path ?? null,
4527
+ x,
4528
+ y
4529
+ });
4530
+ },
4531
+ [session]
4532
+ );
4139
4533
  return /* @__PURE__ */ jsxs8(
4140
4534
  "section",
4141
4535
  {
4142
- className: cn7(
4536
+ className: cn8(
4143
4537
  "@container/workspace-file-manager relative flex h-full min-h-0 w-full overflow-hidden text-[14px] text-[var(--text-primary)]",
4144
4538
  surface === "card" ? "rounded-lg border border-[var(--border-1)] bg-[var(--background-panel)]" : "rounded-none border-0 bg-transparent",
4145
4539
  className
@@ -4154,7 +4548,7 @@ function WorkspaceFileManager({
4154
4548
  "--workspace-file-manager-dialog-overlay-z-index": "20"
4155
4549
  },
4156
4550
  children: [
4157
- hasLocationSidebar ? /* @__PURE__ */ jsx9(
4551
+ hasLocationSidebar ? /* @__PURE__ */ jsx12(
4158
4552
  WorkspaceFileManagerSidebar,
4159
4553
  {
4160
4554
  disabled: rootView.isBusy || panelsState.isLoading,
@@ -4169,7 +4563,7 @@ function WorkspaceFileManager({
4169
4563
  }
4170
4564
  }
4171
4565
  ) : null,
4172
- hasLocationSidebar ? /* @__PURE__ */ jsx9(
4566
+ hasLocationSidebar ? /* @__PURE__ */ jsx12(
4173
4567
  "div",
4174
4568
  {
4175
4569
  "aria-label": i18n.t("resizeLocationsSidebar"),
@@ -4187,8 +4581,8 @@ function WorkspaceFileManager({
4187
4581
  onPointerUp: handleSidebarResizePointerEnd
4188
4582
  }
4189
4583
  ) : null,
4190
- /* @__PURE__ */ jsx9("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children: selectedExternalLocation ? renderExternalLocationContent?.(selectedExternalLocation) ?? null : /* @__PURE__ */ jsxs8(Fragment3, { children: [
4191
- /* @__PURE__ */ jsx9(
4584
+ /* @__PURE__ */ jsx12("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children: selectedExternalLocation ? renderExternalLocationContent?.(selectedExternalLocation) ?? null : /* @__PURE__ */ jsxs8(Fragment3, { children: [
4585
+ /* @__PURE__ */ jsx12(
4192
4586
  WorkspaceFileManagerToolbarContainer,
4193
4587
  {
4194
4588
  i18n,
@@ -4201,7 +4595,7 @@ function WorkspaceFileManager({
4201
4595
  session
4202
4596
  }
4203
4597
  ),
4204
- /* @__PURE__ */ jsx9("div", { className: "@max-[600px]/workspace-file-manager:flex-col @max-[600px]/workspace-file-manager:gap-3 flex min-h-0 min-w-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx9(
4598
+ /* @__PURE__ */ jsx12("div", { className: "@max-[600px]/workspace-file-manager:flex-col @max-[600px]/workspace-file-manager:gap-3 flex min-h-0 min-w-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx12(
4205
4599
  WorkspaceFileManagerPanelsContainer,
4206
4600
  {
4207
4601
  dateLocale,
@@ -4209,9 +4603,11 @@ function WorkspaceFileManager({
4209
4603
  arrangeMode,
4210
4604
  i18n,
4211
4605
  layoutMode,
4606
+ onCopyEntry,
4212
4607
  onDirectoryExpanded,
4213
4608
  onEntryDragStart,
4214
4609
  onOpenContextMenu: openContextMenu,
4610
+ previewActions,
4215
4611
  resolveEntryIconUrl,
4216
4612
  session,
4217
4613
  showPreviewPanel
@@ -4219,8 +4615,8 @@ function WorkspaceFileManager({
4219
4615
  ) })
4220
4616
  ] }) }),
4221
4617
  !selectedExternalLocation ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
4222
- /* @__PURE__ */ jsx9(WorkspaceFileManagerDialogsContainer, { i18n, session }),
4223
- /* @__PURE__ */ jsx9(
4618
+ /* @__PURE__ */ jsx12(WorkspaceFileManagerDialogsContainer, { i18n, session }),
4619
+ /* @__PURE__ */ jsx12(
4224
4620
  WorkspaceFileManagerContextMenuContainer,
4225
4621
  {
4226
4622
  resolveContextMenu,
@@ -4245,7 +4641,7 @@ function WorkspaceFileManagerToolbarContainer({
4245
4641
  const { view } = useWorkspaceFileManagerToolbarView(session, i18n);
4246
4642
  const [searchQuery, setSearchQuery] = useState8(view.searchQuery);
4247
4643
  const submittedSearchQueryRef = useRef7(view.searchQuery);
4248
- const submitSearchQuery = useCallback6(
4644
+ const submitSearchQuery = useCallback7(
4249
4645
  (query) => {
4250
4646
  submittedSearchQueryRef.current = query;
4251
4647
  void session.search(query);
@@ -4270,11 +4666,11 @@ function WorkspaceFileManagerToolbarContainer({
4270
4666
  clearTimeout(timer);
4271
4667
  };
4272
4668
  }, [searchQuery, submitSearchQuery, view.canSearch]);
4273
- const handleSearchClear = useCallback6(() => {
4669
+ const handleSearchClear = useCallback7(() => {
4274
4670
  setSearchQuery("");
4275
4671
  submitSearchQuery("");
4276
4672
  }, [submitSearchQuery]);
4277
- return /* @__PURE__ */ jsx9(
4673
+ return /* @__PURE__ */ jsx12(
4278
4674
  WorkspaceFileManagerToolbar,
4279
4675
  {
4280
4676
  breadcrumbs: view.breadcrumbs,
@@ -4316,146 +4712,13 @@ function WorkspaceFileManagerToolbarContainer({
4316
4712
  }
4317
4713
  );
4318
4714
  }
4319
- function WorkspaceFileManagerPanelsContainer({
4320
- arrangeMode,
4321
- dateLocale,
4322
- entryDragMode,
4323
- i18n,
4324
- layoutMode,
4325
- onDirectoryExpanded,
4326
- onEntryDragStart,
4327
- onOpenContextMenu,
4328
- resolveEntryIconUrl,
4329
- session,
4330
- showPreviewPanel
4331
- }) {
4332
- const { state, view } = useWorkspaceFileManagerPanelsView(session);
4333
- const arrangedEntries = useMemo3(
4334
- () => sortWorkspaceFileEntriesForArrangeMode(state.entries, arrangeMode),
4335
- [arrangeMode, state.entries]
4336
- );
4337
- const searchEntries = useMemo3(
4338
- () => view.searchEntries.map(workspaceFileSearchEntryToEntry),
4339
- [view.searchEntries]
4340
- );
4341
- const searchEntryContextByPath = useMemo3(() => {
4342
- const contextByPath = /* @__PURE__ */ new Map();
4343
- for (const entry of view.searchEntries) {
4344
- contextByPath.set(entry.path, entry.directoryPath);
4345
- }
4346
- return contextByPath;
4347
- }, [view.searchEntries]);
4348
- const treeRows = useMemo3(
4349
- () => buildWorkspaceFileManagerVisibleTreeRows({
4350
- arrangeMode,
4351
- directoryExpansionByPath: state.directoryExpansionByPath,
4352
- entries: arrangedEntries,
4353
- expandedDirectoryPaths: state.expandedDirectoryPaths
4354
- }),
4355
- [
4356
- arrangeMode,
4357
- arrangedEntries,
4358
- state.directoryExpansionByPath,
4359
- state.expandedDirectoryPaths
4360
- ]
4361
- );
4362
- const searchTreeRows = useMemo3(
4363
- () => searchEntries.map((entry) => ({
4364
- depth: 0,
4365
- entry,
4366
- expanded: false,
4367
- expandable: false,
4368
- kind: "entry",
4369
- loadingChildren: false
4370
- })),
4371
- [searchEntries]
4372
- );
4373
- const displayedEntries = view.isSearchMode ? searchEntries : arrangedEntries;
4374
- const displayedTreeRows = view.isSearchMode ? searchTreeRows : treeRows;
4375
- const visibleTreeEntries = useMemo3(
4376
- () => collectWorkspaceFileManagerVisibleTreeEntries(displayedTreeRows),
4377
- [displayedTreeRows]
4378
- );
4379
- const {
4380
- iconUrlByCacheKey,
4381
- reportEntryIconViewportEnter,
4382
- reportEntryIconViewportLeave
4383
- } = useWorkspaceFileEntryIconUrls({
4384
- entries: layoutMode === "list" ? visibleTreeEntries : displayedEntries,
4385
- includeImageThumbnails: true,
4386
- resolveEntryIconUrl
4387
- });
4388
- return /* @__PURE__ */ jsx9(
4389
- WorkspaceFileManagerPanels,
4390
- {
4391
- arrangeMode,
4392
- canMove: view.isSearchMode ? false : view.canMove,
4393
- contextMenuEntryPath: view.contextMenuEntryPath,
4394
- copy: i18n,
4395
- dateLocale,
4396
- entryDragMode,
4397
- iconUrlByCacheKey,
4398
- inlineRenameEntryPath: view.inlineRenameEntryPath,
4399
- inlineRenameValidation: view.inlineRenameValidation,
4400
- isRenaming: view.isRenaming,
4401
- layoutMode,
4402
- pendingDirectoryPath: view.pendingDirectoryPath,
4403
- previewState: view.previewState,
4404
- entryContextByPath: view.isSearchMode ? searchEntryContextByPath : null,
4405
- treeRows: displayedTreeRows,
4406
- onEntryIconViewportEnter: reportEntryIconViewportEnter,
4407
- onEntryIconViewportLeave: reportEntryIconViewportLeave,
4408
- selectedEntry: view.selectedEntry,
4409
- selectedPath: view.selectedPath,
4410
- showPreviewPanel,
4411
- state: {
4412
- entries: displayedEntries,
4413
- error: view.isSearchMode ? view.searchError : state.error,
4414
- isLoading: view.isSearchMode ? view.isSearching : state.isLoading,
4415
- isSearchMode: view.isSearchMode
4416
- },
4417
- onBlankContextMenu: (event) => {
4418
- onOpenContextMenu(event, null);
4419
- },
4420
- onCancelInlineRename: () => {
4421
- session.cancelInlineRename();
4422
- },
4423
- onClearInlineRenameValidation: () => {
4424
- session.clearInlineRenameValidation();
4425
- },
4426
- onConfirmInlineRename: (newName) => {
4427
- return session.confirmInlineRename(newName);
4428
- },
4429
- onEntryContextMenu: onOpenContextMenu,
4430
- onEntryDragStart,
4431
- onMoveEntry: (entry, targetDirectoryPath) => {
4432
- void session.moveEntry(entry, targetDirectoryPath);
4433
- },
4434
- onOpenEntry: (entry) => {
4435
- if (entry.kind === "directory") {
4436
- onDirectoryExpanded?.(entry.path);
4437
- }
4438
- void session.openEntry(entry);
4439
- },
4440
- onSelect: (path) => {
4441
- session.select(path);
4442
- },
4443
- onToggleDirectoryExpanded: (entry, expanded) => {
4444
- if (!expanded) {
4445
- onDirectoryExpanded?.(entry.path);
4446
- }
4447
- void session.toggleDirectoryExpanded(entry);
4448
- }
4449
- }
4450
- );
4451
- }
4452
4715
  function WorkspaceFileManagerDialogsContainer({
4453
4716
  i18n,
4454
4717
  session
4455
4718
  }) {
4456
4719
  const { state, view } = useWorkspaceFileManagerDialogsView(session);
4457
4720
  return /* @__PURE__ */ jsxs8(Fragment3, { children: [
4458
- /* @__PURE__ */ jsx9(
4721
+ /* @__PURE__ */ jsx12(
4459
4722
  WorkspaceFileManagerCreateDialog,
4460
4723
  {
4461
4724
  busy: view.isBusy && state.busyAction === "create",
@@ -4472,7 +4735,7 @@ function WorkspaceFileManagerDialogsContainer({
4472
4735
  }
4473
4736
  }
4474
4737
  ),
4475
- /* @__PURE__ */ jsx9(
4738
+ /* @__PURE__ */ jsx12(
4476
4739
  WorkspaceFileManagerDeleteDialog,
4477
4740
  {
4478
4741
  busy: view.isDeleting,
@@ -4486,7 +4749,7 @@ function WorkspaceFileManagerDialogsContainer({
4486
4749
  }
4487
4750
  }
4488
4751
  ),
4489
- /* @__PURE__ */ jsx9(
4752
+ /* @__PURE__ */ jsx12(
4490
4753
  WorkspaceFileManagerUnsupportedDialog,
4491
4754
  {
4492
4755
  copy: i18n,