@tutti-os/workspace-file-manager 0.0.231 → 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
34
  useCallback as useCallback7,
35
35
  useEffect as useEffect7,
36
36
  useLayoutEffect as useLayoutEffect5,
37
- useMemo as useMemo4,
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";
@@ -1130,7 +1130,7 @@ function WorkspaceFileManagerUnsupportedDialog({
1130
1130
  // src/ui/WorkspaceFileManagerPanelsContainer.tsx
1131
1131
  import {
1132
1132
  useCallback as useCallback4,
1133
- useMemo as useMemo3
1133
+ useMemo as useMemo4
1134
1134
  } from "react";
1135
1135
 
1136
1136
  // src/ui/WorkspaceFileManagerPanels.tsx
@@ -1141,10 +1141,49 @@ import {
1141
1141
  LoadingIcon as LoadingIcon2,
1142
1142
  ScrollArea,
1143
1143
  VideoFileIcon as VideoFileIcon2,
1144
- cn as cn4
1144
+ cn as cn5
1145
1145
  } from "@tutti-os/ui-system";
1146
1146
  import { resolveWorkspaceFileVisualKind as resolveWorkspaceFileVisualKind2 } from "@tutti-os/workspace-file-preview";
1147
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
1148
1187
  import {
1149
1188
  memo,
1150
1189
  useCallback as useCallback2,
@@ -1156,7 +1195,7 @@ import {
1156
1195
  } from "react";
1157
1196
 
1158
1197
  // src/ui/WorkspaceFileManagerIconGrid.tsx
1159
- import { cn as cn3 } from "@tutti-os/ui-system";
1198
+ import { cn as cn4 } from "@tutti-os/ui-system";
1160
1199
  import { useLayoutEffect as useLayoutEffect3, useRef as useRef4 } from "react";
1161
1200
 
1162
1201
  // src/ui/WorkspaceFileEntryIcon.tsx
@@ -1168,14 +1207,14 @@ import {
1168
1207
  ImageFileIcon,
1169
1208
  LoadingIcon,
1170
1209
  VideoFileIcon,
1171
- cn as cn2
1210
+ cn as cn3
1172
1211
  } from "@tutti-os/ui-system";
1173
1212
  import { useEffect as useEffect3, useRef as useRef3 } from "react";
1174
1213
  import {
1175
1214
  resolveWorkspaceFileExtension,
1176
1215
  resolveWorkspaceFileVisualKind
1177
1216
  } from "@tutti-os/workspace-file-preview";
1178
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1217
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1179
1218
  function WorkspaceFileEntryIcon({
1180
1219
  entry,
1181
1220
  frameClassName,
@@ -1252,25 +1291,25 @@ function WorkspaceFileEntryIcon({
1252
1291
  reportViewportLeave();
1253
1292
  };
1254
1293
  }, [cacheKey, entry, isEnteringDirectory, onViewportLeave, onViewportEnter]);
1255
- return /* @__PURE__ */ jsx4(
1294
+ return /* @__PURE__ */ jsx5(
1256
1295
  "span",
1257
1296
  {
1258
1297
  ref: frameRef,
1259
- className: cn2(
1298
+ className: cn3(
1260
1299
  "grid flex-none place-items-center overflow-hidden",
1261
1300
  frameClassName,
1262
1301
  isEnteringDirectory ? "text-[var(--text-tertiary)]" : entryIconColorClassName(visualKind, isAppBundle)
1263
1302
  ),
1264
- children: isEnteringDirectory ? /* @__PURE__ */ jsx4(
1303
+ children: isEnteringDirectory ? /* @__PURE__ */ jsx5(
1265
1304
  LoadingIcon,
1266
1305
  {
1267
- className: cn2(loadingIconClassName ?? iconClassName, "animate-spin")
1306
+ className: cn3(loadingIconClassName ?? iconClassName, "animate-spin")
1268
1307
  }
1269
- ) : iconUrl ? /* @__PURE__ */ jsx4(
1308
+ ) : iconUrl ? /* @__PURE__ */ jsx5(
1270
1309
  "img",
1271
1310
  {
1272
1311
  alt: "",
1273
- className: cn2(
1312
+ className: cn3(
1274
1313
  iconClassName,
1275
1314
  visualKind === "image" ? "rounded-[6px] border border-[var(--border-1)] bg-[var(--transparency-block)] object-contain" : "rounded-[4px] object-contain"
1276
1315
  ),
@@ -1279,7 +1318,7 @@ function WorkspaceFileEntryIcon({
1279
1318
  loading: "lazy",
1280
1319
  src: iconUrl
1281
1320
  }
1282
- ) : /* @__PURE__ */ jsx4(
1321
+ ) : /* @__PURE__ */ jsx5(
1283
1322
  DefaultEntryIcon,
1284
1323
  {
1285
1324
  entry,
@@ -1310,13 +1349,13 @@ function DefaultEntryIcon({
1310
1349
  }) {
1311
1350
  const vectorIconClassName = vectorFallbackIconClassName(iconClassName);
1312
1351
  if (isWorkspaceApplicationBundle(entry)) {
1313
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1352
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1314
1353
  }
1315
1354
  if (shouldUseWorkspaceFileArchiveIcon(entry)) {
1316
- return /* @__PURE__ */ jsx4(WorkspaceArchiveFallbackIcon, { className: iconClassName });
1355
+ return /* @__PURE__ */ jsx5(WorkspaceArchiveFallbackIcon, { className: iconClassName });
1317
1356
  }
1318
1357
  if (shouldUseWorkspaceFileExtensionDocumentIcon(entry)) {
1319
- return /* @__PURE__ */ jsx4(
1358
+ return /* @__PURE__ */ jsx5(
1320
1359
  ExtensionDocumentIcon,
1321
1360
  {
1322
1361
  entry,
@@ -1326,20 +1365,20 @@ function DefaultEntryIcon({
1326
1365
  }
1327
1366
  switch (visualKind) {
1328
1367
  case "directory":
1329
- return /* @__PURE__ */ jsx4(WorkspaceFolderFallbackIcon, { className: iconClassName });
1368
+ return /* @__PURE__ */ jsx5(WorkspaceFolderFallbackIcon, { className: iconClassName });
1330
1369
  case "image":
1331
- return /* @__PURE__ */ jsx4(WorkspaceImageFallbackIcon, { className: iconClassName });
1370
+ return /* @__PURE__ */ jsx5(WorkspaceImageFallbackIcon, { className: iconClassName });
1332
1371
  case "video":
1333
- return /* @__PURE__ */ jsx4(VideoFileIcon, { className: vectorIconClassName });
1372
+ return /* @__PURE__ */ jsx5(VideoFileIcon, { className: vectorIconClassName });
1334
1373
  case "markdown":
1335
1374
  case "document":
1336
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1375
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1337
1376
  case "code":
1338
- return /* @__PURE__ */ jsx4(FileCodeIcon, { className: vectorIconClassName });
1377
+ return /* @__PURE__ */ jsx5(FileCodeIcon, { className: vectorIconClassName });
1339
1378
  case "binary":
1340
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1379
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1341
1380
  default:
1342
- return /* @__PURE__ */ jsx4(FileTextIcon, { className: vectorIconClassName });
1381
+ return /* @__PURE__ */ jsx5(FileTextIcon, { className: vectorIconClassName });
1343
1382
  }
1344
1383
  }
1345
1384
  function vectorFallbackIconClassName(iconClassName) {
@@ -1348,17 +1387,17 @@ function vectorFallbackIconClassName(iconClassName) {
1348
1387
  function WorkspaceFolderFallbackIcon({
1349
1388
  className
1350
1389
  }) {
1351
- return /* @__PURE__ */ jsx4(FolderFilledIcon, { className: vectorFallbackIconClassName(className) });
1390
+ return /* @__PURE__ */ jsx5(FolderFilledIcon, { className: vectorFallbackIconClassName(className) });
1352
1391
  }
1353
1392
  function WorkspaceArchiveFallbackIcon({
1354
1393
  className
1355
1394
  }) {
1356
- return /* @__PURE__ */ jsx4(FileArchiveIcon, { className: vectorFallbackIconClassName(className) });
1395
+ return /* @__PURE__ */ jsx5(FileArchiveIcon, { className: vectorFallbackIconClassName(className) });
1357
1396
  }
1358
1397
  function WorkspaceImageFallbackIcon({
1359
1398
  className
1360
1399
  }) {
1361
- return /* @__PURE__ */ jsx4(ImageFileIcon, { className: vectorFallbackIconClassName(className) });
1400
+ return /* @__PURE__ */ jsx5(ImageFileIcon, { className: vectorFallbackIconClassName(className) });
1362
1401
  }
1363
1402
  function ExtensionDocumentIcon({
1364
1403
  entry,
@@ -1370,11 +1409,11 @@ function ExtensionDocumentIcon({
1370
1409
  "span",
1371
1410
  {
1372
1411
  "aria-hidden": "true",
1373
- className: cn2("relative inline-block overflow-visible", iconClassName),
1412
+ className: cn3("relative inline-block overflow-visible", iconClassName),
1374
1413
  children: [
1375
- /* @__PURE__ */ jsx4("span", { className: "absolute inset-[5%] rounded-[6px] border border-black/10 bg-linear-to-br from-white via-[#f8f8f8] to-[#ececec]" }),
1376
- /* @__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]" }) }),
1377
- 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
1378
1417
  ]
1379
1418
  }
1380
1419
  );
@@ -1403,7 +1442,7 @@ function workspaceFileManagerIconGridFrameClassName() {
1403
1442
  }
1404
1443
 
1405
1444
  // src/ui/WorkspaceFileManagerIconGrid.tsx
1406
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1445
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1407
1446
  function WorkspaceFileManagerIconGrid({
1408
1447
  contextMenuEntryPath,
1409
1448
  copy,
@@ -1428,14 +1467,14 @@ function WorkspaceFileManagerIconGrid({
1428
1467
  onEntryClick,
1429
1468
  onEntryPointerDown
1430
1469
  }) {
1431
- return /* @__PURE__ */ jsx5(
1470
+ return /* @__PURE__ */ jsx6(
1432
1471
  "div",
1433
1472
  {
1434
1473
  className: "grid auto-rows-min content-start items-start gap-x-2 gap-y-6 px-4 py-4",
1435
1474
  style: {
1436
1475
  gridTemplateColumns: `repeat(auto-fill, minmax(${workspaceFileManagerIconGridLayout.tileMinWidthPx}px, 1fr))`
1437
1476
  },
1438
- children: entries.map((entry) => /* @__PURE__ */ jsx5(
1477
+ children: entries.map((entry) => /* @__PURE__ */ jsx6(
1439
1478
  WorkspaceFileManagerIconTile,
1440
1479
  {
1441
1480
  contextMenuActive: contextMenuEntryPath === entry.path,
@@ -1501,7 +1540,7 @@ function WorkspaceFileManagerIconTile({
1501
1540
  inline: "nearest"
1502
1541
  });
1503
1542
  }, [selected]);
1504
- const tileClassName = cn3(
1543
+ const tileClassName = cn4(
1505
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",
1506
1545
  isInlineRenaming ? "cursor-default" : "cursor-pointer hover:bg-transparency-hover active:bg-[var(--transparency-active)]",
1507
1546
  moveDragActive && "cursor-grabbing",
@@ -1509,7 +1548,7 @@ function WorkspaceFileManagerIconTile({
1509
1548
  moveDragSource && "opacity-55",
1510
1549
  moveDragTarget && "bg-[var(--accent-bg)] text-[var(--text-primary)] outline outline-1 -outline-offset-1 outline-[var(--border-focus)]"
1511
1550
  );
1512
- const iconGraphic = /* @__PURE__ */ jsx5(
1551
+ const iconGraphic = /* @__PURE__ */ jsx6(
1513
1552
  WorkspaceFileEntryIcon,
1514
1553
  {
1515
1554
  entry,
@@ -1531,7 +1570,7 @@ function WorkspaceFileManagerIconTile({
1531
1570
  "data-workspace-file-entry-path": entry.path,
1532
1571
  children: [
1533
1572
  iconGraphic,
1534
- /* @__PURE__ */ jsx5(
1573
+ /* @__PURE__ */ jsx6(
1535
1574
  IconTileRenameInput,
1536
1575
  {
1537
1576
  copy,
@@ -1575,8 +1614,8 @@ function WorkspaceFileManagerIconTile({
1575
1614
  children: [
1576
1615
  iconGraphic,
1577
1616
  /* @__PURE__ */ jsxs4("span", { className: "line-clamp-2 w-full break-all text-[13px] leading-[18px] text-[var(--text-primary)]", children: [
1578
- /* @__PURE__ */ jsx5("span", { children: nameParts.start }),
1579
- 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
1580
1619
  ] })
1581
1620
  ]
1582
1621
  }
@@ -1608,12 +1647,12 @@ function IconTileRenameInput({
1608
1647
  }, [hasFileExtension, nameParts.start.length]);
1609
1648
  const validationMessage = inlineRenameValidation === "required" ? copy.t("createNameRequired") : inlineRenameValidation === "invalid" ? copy.t("createNameInvalid") : null;
1610
1649
  return /* @__PURE__ */ jsxs4("span", { className: "flex w-full min-w-0 flex-col gap-0.5", children: [
1611
- /* @__PURE__ */ jsx5(
1650
+ /* @__PURE__ */ jsx6(
1612
1651
  "input",
1613
1652
  {
1614
1653
  "aria-invalid": inlineRenameValidation !== null,
1615
1654
  "aria-label": copy.t("renameLabel"),
1616
- className: cn3(
1655
+ className: cn4(
1617
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",
1618
1657
  inlineRenameValidation !== null && "border-[var(--state-danger)]"
1619
1658
  ),
@@ -1639,7 +1678,7 @@ function IconTileRenameInput({
1639
1678
  }
1640
1679
  }
1641
1680
  ),
1642
- 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
1643
1682
  ] });
1644
1683
  }
1645
1684
 
@@ -1732,7 +1771,7 @@ function resolveWorkspaceFileManagerPreservedNameColumnWidth(width) {
1732
1771
  }
1733
1772
 
1734
1773
  // src/ui/WorkspaceFileManagerPanels.tsx
1735
- 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";
1736
1775
  var workspaceFileManagerTableGridStyle = {
1737
1776
  gridTemplateColumns: workspaceFileManagerTableGridTemplate
1738
1777
  };
@@ -1765,6 +1804,7 @@ function WorkspaceFileManagerPanels({
1765
1804
  isRenaming,
1766
1805
  layoutMode,
1767
1806
  pendingDirectoryPath,
1807
+ previewActions,
1768
1808
  previewState,
1769
1809
  onEntryIconViewportLeave,
1770
1810
  onEntryIconViewportEnter,
@@ -2122,22 +2162,22 @@ function WorkspaceFileManagerPanels({
2122
2162
  },
2123
2163
  [clampPreviewPaneWidth, previewPaneMaxWidth, previewPaneWidth]
2124
2164
  );
2125
- const tablePanel = /* @__PURE__ */ jsx6(
2165
+ const tablePanel = /* @__PURE__ */ jsx7(
2126
2166
  "div",
2127
2167
  {
2128
- className: cn4(
2168
+ className: cn5(
2129
2169
  "flex min-h-0 min-w-0 flex-col overflow-hidden",
2130
2170
  useStackedPreview ? "h-[44%] max-h-[48%] flex-none" : "flex-1"
2131
2171
  ),
2132
2172
  onContextMenu: handleTablePanelContextMenu,
2133
- 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(
2134
2174
  FeedbackState,
2135
2175
  {
2136
2176
  message: copy.t(
2137
2177
  state.isSearchMode ? "noSearchResults" : "emptyDirectory"
2138
2178
  )
2139
2179
  }
2140
- ) : /* @__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(
2141
2181
  WorkspaceFileManagerIconGrid,
2142
2182
  {
2143
2183
  contextMenuEntryPath,
@@ -2167,16 +2207,16 @@ function WorkspaceFileManagerPanels({
2167
2207
  /* @__PURE__ */ jsxs5(
2168
2208
  "div",
2169
2209
  {
2170
- className: cn4(
2210
+ className: cn5(
2171
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)]",
2172
2212
  tableGridClassName
2173
2213
  ),
2174
2214
  style: tableGridStyle,
2175
2215
  children: [
2176
- /* @__PURE__ */ jsx6(
2216
+ /* @__PURE__ */ jsx7(
2177
2217
  "span",
2178
2218
  {
2179
- className: cn4(
2219
+ className: cn5(
2180
2220
  "min-w-0 overflow-hidden",
2181
2221
  tableCellPaddingClassName
2182
2222
  ),
@@ -2184,17 +2224,17 @@ function WorkspaceFileManagerPanels({
2184
2224
  children: copy.t("nameLabel")
2185
2225
  }
2186
2226
  ),
2187
- /* @__PURE__ */ jsx6(
2227
+ /* @__PURE__ */ jsx7(
2188
2228
  "span",
2189
2229
  {
2190
- className: cn4("min-w-0 truncate", tableCellPaddingClassName),
2230
+ className: cn5("min-w-0 truncate", tableCellPaddingClassName),
2191
2231
  children: dateColumnLabel
2192
2232
  }
2193
2233
  ),
2194
- /* @__PURE__ */ jsx6(
2234
+ /* @__PURE__ */ jsx7(
2195
2235
  "span",
2196
2236
  {
2197
- className: cn4("min-w-0 truncate", tableCellPaddingClassName),
2237
+ className: cn5("min-w-0 truncate", tableCellPaddingClassName),
2198
2238
  children: copy.t("sizeLabel")
2199
2239
  }
2200
2240
  )
@@ -2202,7 +2242,7 @@ function WorkspaceFileManagerPanels({
2202
2242
  }
2203
2243
  ),
2204
2244
  treeRows.map(
2205
- (row) => row.kind === "feedback" ? /* @__PURE__ */ jsx6(
2245
+ (row) => row.kind === "feedback" ? /* @__PURE__ */ jsx7(
2206
2246
  TreeFeedbackRow,
2207
2247
  {
2208
2248
  copy,
@@ -2214,7 +2254,7 @@ function WorkspaceFileManagerPanels({
2214
2254
  tableCellPaddingClassName
2215
2255
  },
2216
2256
  row.key
2217
- ) : /* @__PURE__ */ jsx6(
2257
+ ) : /* @__PURE__ */ jsx7(
2218
2258
  EntryRow,
2219
2259
  {
2220
2260
  contextMenuActive: contextMenuEntryPath === row.entry.path,
@@ -2258,21 +2298,22 @@ function WorkspaceFileManagerPanels({
2258
2298
  ] }) })
2259
2299
  }
2260
2300
  );
2261
- const previewPanel = showPreviewPanel ? /* @__PURE__ */ jsx6(
2301
+ const previewPanel = showPreviewPanel ? /* @__PURE__ */ jsx7(
2262
2302
  "aside",
2263
2303
  {
2264
- className: cn4(
2304
+ className: cn5(
2265
2305
  "relative flex h-full min-h-0 flex-col gap-[14px] overflow-auto p-4",
2266
2306
  useStackedPreview ? "max-h-[44%] flex-none" : "flex-none",
2267
2307
  previewPaneClassName
2268
2308
  ),
2269
2309
  style: useStackedPreview ? void 0 : { width: previewPaneWidth },
2270
- children: /* @__PURE__ */ jsx6(
2310
+ children: /* @__PURE__ */ jsx7(
2271
2311
  PreviewPane,
2272
2312
  {
2273
2313
  copy,
2274
2314
  dateLocale,
2275
2315
  entry: selectedEntry,
2316
+ previewActions,
2276
2317
  previewState
2277
2318
  }
2278
2319
  )
@@ -2287,13 +2328,13 @@ function WorkspaceFileManagerPanels({
2287
2328
  /* @__PURE__ */ jsxs5(
2288
2329
  "div",
2289
2330
  {
2290
- className: cn4(
2331
+ className: cn5(
2291
2332
  "h-full min-h-0 min-w-0 bg-transparent",
2292
2333
  useStackedPreview ? "flex flex-col gap-3" : "flex"
2293
2334
  ),
2294
2335
  children: [
2295
2336
  tablePanel,
2296
- showPreviewPanel && !useStackedPreview ? /* @__PURE__ */ jsx6(
2337
+ showPreviewPanel && !useStackedPreview ? /* @__PURE__ */ jsx7(
2297
2338
  "div",
2298
2339
  {
2299
2340
  "aria-label": copy.t("resizePreviewPanel"),
@@ -2318,7 +2359,7 @@ function WorkspaceFileManagerPanels({
2318
2359
  ]
2319
2360
  }
2320
2361
  ),
2321
- moveDragPreview ? /* @__PURE__ */ jsx6(
2362
+ moveDragPreview ? /* @__PURE__ */ jsx7(
2322
2363
  MoveDragPreview,
2323
2364
  {
2324
2365
  iconUrlByCacheKey,
@@ -2420,7 +2461,7 @@ var EntryRow = memo(function EntryRow2({
2420
2461
  inline: "nearest"
2421
2462
  });
2422
2463
  }, [selected]);
2423
- const rowClassName = cn4(
2464
+ const rowClassName = cn5(
2424
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",
2425
2466
  gridClassName,
2426
2467
  isInlineRenaming ? "cursor-default" : "cursor-pointer hover:bg-transparency-hover active:bg-[var(--transparency-active)]",
@@ -2438,15 +2479,15 @@ var EntryRow = memo(function EntryRow2({
2438
2479
  onContextMenu(event, entry);
2439
2480
  }
2440
2481
  };
2441
- const nameCell = /* @__PURE__ */ jsx6(
2482
+ const nameCell = /* @__PURE__ */ jsx7(
2442
2483
  "span",
2443
2484
  {
2444
- className: cn4(
2485
+ className: cn5(
2445
2486
  "min-w-0 overflow-hidden",
2446
2487
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2447
2488
  tableCellPaddingClassName
2448
2489
  ),
2449
- children: /* @__PURE__ */ jsx6(
2490
+ children: /* @__PURE__ */ jsx7(
2450
2491
  EntryNameCell,
2451
2492
  {
2452
2493
  copy,
@@ -2471,10 +2512,10 @@ var EntryRow = memo(function EntryRow2({
2471
2512
  )
2472
2513
  }
2473
2514
  );
2474
- const modifiedCell = /* @__PURE__ */ jsx6(
2515
+ const modifiedCell = /* @__PURE__ */ jsx7(
2475
2516
  "span",
2476
2517
  {
2477
- className: cn4(
2518
+ className: cn5(
2478
2519
  "truncate text-xs text-[var(--text-secondary)]",
2479
2520
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2480
2521
  tableCellPaddingClassName
@@ -2485,10 +2526,10 @@ var EntryRow = memo(function EntryRow2({
2485
2526
  )
2486
2527
  }
2487
2528
  );
2488
- const sizeCell = /* @__PURE__ */ jsx6(
2529
+ const sizeCell = /* @__PURE__ */ jsx7(
2489
2530
  "span",
2490
2531
  {
2491
- className: cn4(
2532
+ className: cn5(
2492
2533
  "truncate text-xs text-[var(--text-secondary)]",
2493
2534
  !isInlineRenaming && "pointer-events-none relative z-[1]",
2494
2535
  tableCellPaddingClassName
@@ -2526,7 +2567,7 @@ var EntryRow = memo(function EntryRow2({
2526
2567
  onDragStart?.(entry, event.dataTransfer);
2527
2568
  },
2528
2569
  children: [
2529
- /* @__PURE__ */ jsx6(
2570
+ /* @__PURE__ */ jsx7(
2530
2571
  "button",
2531
2572
  {
2532
2573
  "aria-label": entry.name,
@@ -2565,7 +2606,7 @@ function MoveDragPreview({
2565
2606
  transform: "translate(12px, 12px)"
2566
2607
  },
2567
2608
  children: [
2568
- /* @__PURE__ */ jsx6(
2609
+ /* @__PURE__ */ jsx7(
2569
2610
  WorkspaceFileEntryIcon,
2570
2611
  {
2571
2612
  entry: preview.entry,
@@ -2576,7 +2617,7 @@ function MoveDragPreview({
2576
2617
  onViewportEnter: onEntryIconViewportEnter
2577
2618
  }
2578
2619
  ),
2579
- /* @__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 })
2580
2621
  ]
2581
2622
  }
2582
2623
  );
@@ -2629,26 +2670,26 @@ function TreeFeedbackRow({
2629
2670
  return /* @__PURE__ */ jsxs5(
2630
2671
  "div",
2631
2672
  {
2632
- className: cn4(
2673
+ className: cn5(
2633
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)]",
2634
2675
  gridClassName
2635
2676
  ),
2636
2677
  style: gridStyle,
2637
2678
  children: [
2638
- /* @__PURE__ */ jsx6("span", { className: tableCellPaddingClassName, children: /* @__PURE__ */ jsxs5(
2679
+ /* @__PURE__ */ jsx7("span", { className: tableCellPaddingClassName, children: /* @__PURE__ */ jsxs5(
2639
2680
  "span",
2640
2681
  {
2641
2682
  className: "flex min-w-0 items-center gap-2",
2642
2683
  style: workspaceFileManagerTreeIndentStyle(depth),
2643
2684
  children: [
2644
- /* @__PURE__ */ jsx6("span", { className: "size-5 shrink-0" }),
2645
- status === "loading" ? /* @__PURE__ */ jsx6(LoadingIcon2, { className: "size-3.5 shrink-0 animate-spin" }) : null,
2646
- /* @__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 })
2647
2688
  ]
2648
2689
  }
2649
2690
  ) }),
2650
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true" }),
2651
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true" })
2691
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true" }),
2692
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true" })
2652
2693
  ]
2653
2694
  }
2654
2695
  );
@@ -2662,9 +2703,9 @@ function DirectoryDisclosureButton({
2662
2703
  onToggle
2663
2704
  }) {
2664
2705
  if (!show) {
2665
- 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" });
2666
2707
  }
2667
- return /* @__PURE__ */ jsx6(
2708
+ return /* @__PURE__ */ jsx7(
2668
2709
  "button",
2669
2710
  {
2670
2711
  "aria-label": copy.t(
@@ -2682,10 +2723,10 @@ function DirectoryDisclosureButton({
2682
2723
  onPointerDown: (event) => {
2683
2724
  event.stopPropagation();
2684
2725
  },
2685
- 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(
2686
2727
  ArrowRightIcon2,
2687
2728
  {
2688
- className: cn4(
2729
+ className: cn5(
2689
2730
  "size-3.5 transition-transform",
2690
2731
  expanded && "rotate-90"
2691
2732
  )
@@ -2760,7 +2801,7 @@ function EntryNameCell({
2760
2801
  className: "flex min-w-0 items-center gap-1.5",
2761
2802
  style: workspaceFileManagerTreeIndentStyle(treeDepth),
2762
2803
  children: [
2763
- /* @__PURE__ */ jsx6(
2804
+ /* @__PURE__ */ jsx7(
2764
2805
  DirectoryDisclosureButton,
2765
2806
  {
2766
2807
  copy,
@@ -2771,7 +2812,7 @@ function EntryNameCell({
2771
2812
  onToggle: onToggleDirectoryExpanded
2772
2813
  }
2773
2814
  ),
2774
- /* @__PURE__ */ jsx6(
2815
+ /* @__PURE__ */ jsx7(
2775
2816
  WorkspaceFileEntryIcon,
2776
2817
  {
2777
2818
  entry,
@@ -2783,12 +2824,12 @@ function EntryNameCell({
2783
2824
  }
2784
2825
  ),
2785
2826
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
2786
- /* @__PURE__ */ jsx6(
2827
+ /* @__PURE__ */ jsx7(
2787
2828
  "input",
2788
2829
  {
2789
2830
  "aria-invalid": inlineRenameValidation !== null,
2790
2831
  "aria-label": copy.t("renameLabel"),
2791
- className: cn4(
2832
+ className: cn5(
2792
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",
2793
2834
  inlineRenameValidation !== null && "border-[var(--state-danger)]"
2794
2835
  ),
@@ -2830,7 +2871,7 @@ function EntryNameCell({
2830
2871
  }
2831
2872
  }
2832
2873
  ),
2833
- 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
2834
2875
  ] })
2835
2876
  ]
2836
2877
  }
@@ -2842,7 +2883,7 @@ function EntryNameCell({
2842
2883
  className: "flex min-w-0 items-center gap-1.5",
2843
2884
  style: workspaceFileManagerTreeIndentStyle(treeDepth),
2844
2885
  children: [
2845
- /* @__PURE__ */ jsx6(
2886
+ /* @__PURE__ */ jsx7(
2846
2887
  DirectoryDisclosureButton,
2847
2888
  {
2848
2889
  copy,
@@ -2853,7 +2894,7 @@ function EntryNameCell({
2853
2894
  onToggle: onToggleDirectoryExpanded
2854
2895
  }
2855
2896
  ),
2856
- /* @__PURE__ */ jsx6(
2897
+ /* @__PURE__ */ jsx7(
2857
2898
  WorkspaceFileEntryIcon,
2858
2899
  {
2859
2900
  entry,
@@ -2867,10 +2908,10 @@ function EntryNameCell({
2867
2908
  ),
2868
2909
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
2869
2910
  /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 max-w-full overflow-hidden whitespace-nowrap text-sm", children: [
2870
- /* @__PURE__ */ jsx6("span", { className: "min-w-0 overflow-hidden text-ellipsis", children: nameParts.start }),
2871
- 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
2872
2913
  ] }),
2873
- 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
2874
2915
  ] })
2875
2916
  ]
2876
2917
  }
@@ -2882,47 +2923,55 @@ function EntryIcon({
2882
2923
  }) {
2883
2924
  switch (visualKind) {
2884
2925
  case "directory":
2885
- return /* @__PURE__ */ jsx6(WorkspaceFolderFallbackIcon, { className });
2926
+ return /* @__PURE__ */ jsx7(WorkspaceFolderFallbackIcon, { className });
2886
2927
  case "image":
2887
- return /* @__PURE__ */ jsx6(WorkspaceImageFallbackIcon, { className });
2928
+ return /* @__PURE__ */ jsx7(WorkspaceImageFallbackIcon, { className });
2888
2929
  case "video":
2889
- return /* @__PURE__ */ jsx6(VideoFileIcon2, { className });
2930
+ return /* @__PURE__ */ jsx7(VideoFileIcon2, { className });
2890
2931
  case "markdown":
2891
2932
  case "document":
2892
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2933
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2893
2934
  case "code":
2894
- return /* @__PURE__ */ jsx6(FileCodeIcon2, { className });
2935
+ return /* @__PURE__ */ jsx7(FileCodeIcon2, { className });
2895
2936
  case "binary":
2896
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2937
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2897
2938
  default:
2898
- return /* @__PURE__ */ jsx6(FileTextIcon2, { className });
2939
+ return /* @__PURE__ */ jsx7(FileTextIcon2, { className });
2899
2940
  }
2900
2941
  }
2901
2942
  function PreviewPane({
2902
2943
  copy,
2903
2944
  dateLocale,
2904
2945
  entry,
2946
+ previewActions,
2905
2947
  previewState
2906
2948
  }) {
2907
2949
  if (!entry || previewState.status === "empty") {
2908
- 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") }) });
2909
2951
  }
2910
2952
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2911
- /* @__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
+ ),
2912
2961
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-col gap-[14px]", children: [
2913
2962
  /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-col gap-[3px]", children: [
2914
- /* @__PURE__ */ jsx6("strong", { className: "min-w-0 truncate text-[15px] font-semibold text-[var(--text-primary)]", children: entry.name }),
2915
- /* @__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 })
2916
2965
  ] }),
2917
2966
  /* @__PURE__ */ jsxs5("dl", { className: "border-t border-[var(--border-1)]", children: [
2918
- /* @__PURE__ */ jsx6(
2967
+ /* @__PURE__ */ jsx7(
2919
2968
  PreviewDetail,
2920
2969
  {
2921
2970
  label: copy.t("modifiedLabel"),
2922
2971
  value: formatWorkspaceFileModifiedTime(entry.mtimeMs, dateLocale)
2923
2972
  }
2924
2973
  ),
2925
- /* @__PURE__ */ jsx6(
2974
+ /* @__PURE__ */ jsx7(
2926
2975
  PreviewDetail,
2927
2976
  {
2928
2977
  label: copy.t("sizeLabel"),
@@ -2937,15 +2986,15 @@ function PreviewSurface({
2937
2986
  copy,
2938
2987
  previewState
2939
2988
  }) {
2940
- return /* @__PURE__ */ jsx6(
2989
+ return /* @__PURE__ */ jsx7(
2941
2990
  SharedWorkspaceFilePreviewSurface,
2942
2991
  {
2943
2992
  directoryMessage: copy.t("previewDirectoryLabel"),
2944
2993
  emptyMessage: copy.t("previewEmptyLabel"),
2945
2994
  imageAlt: (entry) => entry.name,
2946
- 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" }) }),
2947
2996
  loadingMessage: copy.t("previewLoadingLabel"),
2948
- renderIcon: (entry) => /* @__PURE__ */ jsx6(
2997
+ renderIcon: (entry) => /* @__PURE__ */ jsx7(
2949
2998
  EntryIcon,
2950
2999
  {
2951
3000
  className: "mx-auto size-7",
@@ -2973,14 +3022,14 @@ function PreviewDetail({
2973
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",
2974
3023
  style: workspaceFileManagerPreviewDetailGridStyle,
2975
3024
  children: [
2976
- /* @__PURE__ */ jsx6("dt", { className: "truncate text-[var(--text-secondary)]", children: label }),
2977
- /* @__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 })
2978
3027
  ]
2979
3028
  }
2980
3029
  );
2981
3030
  }
2982
3031
  function FeedbackState({ message }) {
2983
- 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 }) });
2984
3033
  }
2985
3034
 
2986
3035
  // src/ui/useWorkspaceFileEntryIconUrls.ts
@@ -3208,6 +3257,157 @@ function useWorkspaceFileEntryIconUrls(input) {
3208
3257
  };
3209
3258
  }
3210
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
+
3211
3411
  // src/ui/workspaceFileManagerVisibleTree.ts
3212
3412
  function buildWorkspaceFileManagerVisibleTreeRows(input) {
3213
3413
  return appendWorkspaceFileManagerVisibleTreeRows({
@@ -3282,37 +3482,39 @@ function appendWorkspaceFileManagerVisibleTreeRows(input) {
3282
3482
  }
3283
3483
 
3284
3484
  // src/ui/WorkspaceFileManagerPanelsContainer.tsx
3285
- import { jsx as jsx7 } from "react/jsx-runtime";
3485
+ import { jsx as jsx9 } from "react/jsx-runtime";
3286
3486
  function WorkspaceFileManagerPanelsContainer({
3287
3487
  arrangeMode,
3288
3488
  dateLocale,
3289
3489
  entryDragMode,
3290
3490
  i18n,
3291
3491
  layoutMode,
3492
+ onCopyEntry,
3292
3493
  onDirectoryExpanded,
3293
3494
  onEntryDragStart,
3294
3495
  onOpenContextMenu,
3496
+ previewActions,
3295
3497
  resolveEntryIconUrl,
3296
3498
  session,
3297
3499
  showPreviewPanel
3298
3500
  }) {
3299
3501
  const { state, view } = useWorkspaceFileManagerPanelsView(session);
3300
- const arrangedEntries = useMemo3(
3502
+ const arrangedEntries = useMemo4(
3301
3503
  () => sortWorkspaceFileEntriesForArrangeMode(state.entries, arrangeMode),
3302
3504
  [arrangeMode, state.entries]
3303
3505
  );
3304
- const searchEntries = useMemo3(
3506
+ const searchEntries = useMemo4(
3305
3507
  () => view.searchEntries.map(workspaceFileSearchEntryToEntry),
3306
3508
  [view.searchEntries]
3307
3509
  );
3308
- const searchEntryContextByPath = useMemo3(() => {
3510
+ const searchEntryContextByPath = useMemo4(() => {
3309
3511
  const contextByPath = /* @__PURE__ */ new Map();
3310
3512
  for (const entry of view.searchEntries) {
3311
3513
  contextByPath.set(entry.path, entry.directoryPath);
3312
3514
  }
3313
3515
  return contextByPath;
3314
3516
  }, [view.searchEntries]);
3315
- const treeRows = useMemo3(
3517
+ const treeRows = useMemo4(
3316
3518
  () => buildWorkspaceFileManagerVisibleTreeRows({
3317
3519
  arrangeMode,
3318
3520
  directoryExpansionByPath: state.directoryExpansionByPath,
@@ -3326,7 +3528,7 @@ function WorkspaceFileManagerPanelsContainer({
3326
3528
  state.expandedDirectoryPaths
3327
3529
  ]
3328
3530
  );
3329
- const searchTreeRows = useMemo3(
3531
+ const searchTreeRows = useMemo4(
3330
3532
  () => searchEntries.map((entry) => ({
3331
3533
  depth: 0,
3332
3534
  entry,
@@ -3339,11 +3541,11 @@ function WorkspaceFileManagerPanelsContainer({
3339
3541
  );
3340
3542
  const displayedEntries = view.isSearchMode ? searchEntries : arrangedEntries;
3341
3543
  const displayedTreeRows = view.isSearchMode ? searchTreeRows : treeRows;
3342
- const visibleTreeEntries = useMemo3(
3544
+ const visibleTreeEntries = useMemo4(
3343
3545
  () => collectWorkspaceFileManagerVisibleTreeEntries(displayedTreeRows),
3344
3546
  [displayedTreeRows]
3345
3547
  );
3346
- const panelState = useMemo3(
3548
+ const panelState = useMemo4(
3347
3549
  () => ({
3348
3550
  entries: displayedEntries,
3349
3551
  error: view.isSearchMode ? view.searchError : state.error,
@@ -3368,6 +3570,14 @@ function WorkspaceFileManagerPanelsContainer({
3368
3570
  includeImageThumbnails: true,
3369
3571
  resolveEntryIconUrl
3370
3572
  });
3573
+ const resolvedPreviewActions = useWorkspaceFileManagerPreviewActions({
3574
+ config: previewActions,
3575
+ copy: i18n,
3576
+ entry: view.selectedEntry,
3577
+ onCopyEntry,
3578
+ session,
3579
+ state
3580
+ });
3371
3581
  const handleBlankContextMenu = useCallback4(
3372
3582
  (event) => {
3373
3583
  onOpenContextMenu(event, null);
@@ -3414,7 +3624,7 @@ function WorkspaceFileManagerPanelsContainer({
3414
3624
  },
3415
3625
  [onDirectoryExpanded, session]
3416
3626
  );
3417
- return /* @__PURE__ */ jsx7(
3627
+ return /* @__PURE__ */ jsx9(
3418
3628
  WorkspaceFileManagerPanels,
3419
3629
  {
3420
3630
  arrangeMode,
@@ -3430,6 +3640,7 @@ function WorkspaceFileManagerPanelsContainer({
3430
3640
  isRenaming: view.isRenaming,
3431
3641
  layoutMode,
3432
3642
  pendingDirectoryPath: view.pendingDirectoryPath,
3643
+ previewActions: resolvedPreviewActions,
3433
3644
  previewState: view.previewState,
3434
3645
  selectedEntry: view.selectedEntry,
3435
3646
  selectedPath: view.selectedPath,
@@ -3456,7 +3667,7 @@ function WorkspaceFileManagerPanelsContainer({
3456
3667
  import {
3457
3668
  ArrowLeftIcon as ArrowLeftIcon2,
3458
3669
  ArrowRightIcon as ArrowRightIcon3,
3459
- Button as Button2,
3670
+ Button as Button3,
3460
3671
  ChevronDownIcon,
3461
3672
  CloseIcon,
3462
3673
  DropdownMenu,
@@ -3475,10 +3686,10 @@ import {
3475
3686
  TooltipTrigger,
3476
3687
  ViewGridLinedIcon,
3477
3688
  ViewListLinedIcon,
3478
- cn as cn5
3689
+ cn as cn6
3479
3690
  } from "@tutti-os/ui-system";
3480
3691
  import { useEffect as useEffect6, useRef as useRef6, useState as useState5 } from "react";
3481
- import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3692
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
3482
3693
  function WorkspaceFileManagerToolbar({
3483
3694
  breadcrumbs,
3484
3695
  canSearch,
@@ -3509,33 +3720,33 @@ function WorkspaceFileManagerToolbar({
3509
3720
  onRefresh();
3510
3721
  };
3511
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: [
3512
- /* @__PURE__ */ jsx8(
3723
+ /* @__PURE__ */ jsx10(
3513
3724
  ToolbarIconButton,
3514
3725
  {
3515
3726
  ariaLabel: copy.t("backLabel"),
3516
3727
  disabled: !canGoBack || isLoading || isBusy,
3517
3728
  title: copy.t("backLabel"),
3518
3729
  onClick: onGoBack,
3519
- children: /* @__PURE__ */ jsx8(ArrowLeftIcon2, { className: "size-4" })
3730
+ children: /* @__PURE__ */ jsx10(ArrowLeftIcon2, { className: "size-4" })
3520
3731
  }
3521
3732
  ),
3522
- /* @__PURE__ */ jsx8(
3733
+ /* @__PURE__ */ jsx10(
3523
3734
  ToolbarIconButton,
3524
3735
  {
3525
3736
  ariaLabel: copy.t("forwardLabel"),
3526
3737
  disabled: !canGoForward || isLoading || isBusy,
3527
3738
  title: copy.t("forwardLabel"),
3528
3739
  onClick: onGoForward,
3529
- children: /* @__PURE__ */ jsx8(ArrowRightIcon3, { className: "size-4" })
3740
+ children: /* @__PURE__ */ jsx10(ArrowRightIcon3, { className: "size-4" })
3530
3741
  }
3531
3742
  ),
3532
- /* @__PURE__ */ jsx8(
3743
+ /* @__PURE__ */ jsx10(
3533
3744
  "nav",
3534
3745
  {
3535
3746
  "aria-label": currentDirectoryPath,
3536
3747
  className: "@max-[600px]/workspace-file-manager:flex-auto flex min-w-0 flex-1 overflow-hidden pr-2",
3537
3748
  "data-workspace-file-manager-path": "",
3538
- children: /* @__PURE__ */ jsx8("ol", { className: "flex min-w-0 max-w-full flex-1 items-center gap-2 overflow-hidden", children: breadcrumbs.map((crumb, index) => /* @__PURE__ */ jsx8(
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(
3539
3750
  BreadcrumbButton,
3540
3751
  {
3541
3752
  active: index === breadcrumbs.length - 1,
@@ -3550,7 +3761,7 @@ function WorkspaceFileManagerToolbar({
3550
3761
  }
3551
3762
  ),
3552
3763
  /* @__PURE__ */ jsxs6("div", { className: "@max-[600px]/workspace-file-manager:justify-end flex flex-none items-center gap-1.5", children: [
3553
- /* @__PURE__ */ jsx8(
3764
+ /* @__PURE__ */ jsx10(
3554
3765
  LayoutModeToggle,
3555
3766
  {
3556
3767
  arrangeMode,
@@ -3566,17 +3777,17 @@ function WorkspaceFileManagerToolbar({
3566
3777
  disabled: isLoading || isMutating || isBusy,
3567
3778
  onClick: handleRefresh,
3568
3779
  children: [
3569
- isLoading ? /* @__PURE__ */ jsx8(LoadingIcon3, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx8(
3780
+ isLoading ? /* @__PURE__ */ jsx10(LoadingIcon3, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx10(
3570
3781
  RefreshIcon,
3571
3782
  {
3572
- className: cn5(
3783
+ className: cn6(
3573
3784
  "size-4",
3574
3785
  refreshAnimationKey > 0 && "motion-safe:animate-[spin_520ms_cubic-bezier(0.4,0,0.2,1)_1_reverse]"
3575
3786
  )
3576
3787
  },
3577
3788
  refreshAnimationKey
3578
3789
  ),
3579
- /* @__PURE__ */ jsx8("span", { className: "@max-[600px]/workspace-file-manager:hidden", children: copy.t("refreshLabel") })
3790
+ /* @__PURE__ */ jsx10("span", { className: "@max-[600px]/workspace-file-manager:hidden", children: copy.t("refreshLabel") })
3580
3791
  ]
3581
3792
  }
3582
3793
  ),
@@ -3586,7 +3797,7 @@ function WorkspaceFileManagerToolbar({
3586
3797
  isLoading,
3587
3798
  isMutating
3588
3799
  }) ?? null,
3589
- /* @__PURE__ */ jsx8(
3800
+ /* @__PURE__ */ jsx10(
3590
3801
  WorkspaceFileManagerToolbarSearch,
3591
3802
  {
3592
3803
  canSearch,
@@ -3641,7 +3852,7 @@ function WorkspaceFileManagerToolbarSearch({
3641
3852
  setExpanded(false);
3642
3853
  };
3643
3854
  if (!active) {
3644
- return /* @__PURE__ */ jsx8(
3855
+ return /* @__PURE__ */ jsx10(
3645
3856
  ToolbarIconButton,
3646
3857
  {
3647
3858
  ariaLabel: copy.t("searchPlaceholder"),
@@ -3650,21 +3861,21 @@ function WorkspaceFileManagerToolbarSearch({
3650
3861
  onClick: () => {
3651
3862
  setExpanded(true);
3652
3863
  },
3653
- children: /* @__PURE__ */ jsx8(SearchIcon, { className: "size-4" })
3864
+ children: /* @__PURE__ */ jsx10(SearchIcon, { className: "size-4" })
3654
3865
  }
3655
3866
  );
3656
3867
  }
3657
3868
  return /* @__PURE__ */ jsxs6(
3658
3869
  "div",
3659
3870
  {
3660
- className: cn5(
3871
+ className: cn6(
3661
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",
3662
3873
  "@max-[600px]/workspace-file-manager:w-[min(170px,42vw)]",
3663
3874
  !canSearch && "opacity-60"
3664
3875
  ),
3665
3876
  children: [
3666
- /* @__PURE__ */ jsx8(SearchIcon, { className: "pointer-events-none absolute top-1/2 left-2 size-4 -translate-y-1/2 text-[var(--text-tertiary)]" }),
3667
- /* @__PURE__ */ jsx8(
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(
3668
3879
  Input2,
3669
3880
  {
3670
3881
  "aria-label": copy.t("searchPlaceholder"),
@@ -3703,14 +3914,14 @@ function WorkspaceFileManagerToolbarSearch({
3703
3914
  }
3704
3915
  }
3705
3916
  ),
3706
- /* @__PURE__ */ jsx8(
3917
+ /* @__PURE__ */ jsx10(
3707
3918
  "button",
3708
3919
  {
3709
3920
  "aria-label": copy.t("clearSearchLabel"),
3710
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)]",
3711
3922
  type: "button",
3712
3923
  onClick: handleClear,
3713
- children: isSearching ? /* @__PURE__ */ jsx8(LoadingIcon3, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx8(CloseIcon, { className: "size-3" })
3924
+ children: isSearching ? /* @__PURE__ */ jsx10(LoadingIcon3, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx10(CloseIcon, { className: "size-3" })
3714
3925
  }
3715
3926
  )
3716
3927
  ]
@@ -3735,13 +3946,13 @@ function LayoutModeToggle({
3735
3946
  { label: copy.t("arrangeCreatedLabel"), mode: "created" },
3736
3947
  { label: copy.t("sizeLabel"), mode: "size" }
3737
3948
  ];
3738
- return /* @__PURE__ */ jsx8(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs6(
3949
+ return /* @__PURE__ */ jsx10(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs6(
3739
3950
  "div",
3740
3951
  {
3741
3952
  className: "flex items-center rounded-md border border-[var(--border-1)] bg-[var(--transparency-block)] p-0.5",
3742
3953
  role: "group",
3743
3954
  children: [
3744
- /* @__PURE__ */ jsx8(
3955
+ /* @__PURE__ */ jsx10(
3745
3956
  LayoutModeButton,
3746
3957
  {
3747
3958
  active: layoutMode === "icon",
@@ -3750,10 +3961,10 @@ function LayoutModeToggle({
3750
3961
  onClick: () => {
3751
3962
  onLayoutModeChange("icon");
3752
3963
  },
3753
- children: /* @__PURE__ */ jsx8(ViewGridLinedIcon, { className: "size-4" })
3964
+ children: /* @__PURE__ */ jsx10(ViewGridLinedIcon, { className: "size-4" })
3754
3965
  }
3755
3966
  ),
3756
- /* @__PURE__ */ jsx8(
3967
+ /* @__PURE__ */ jsx10(
3757
3968
  LayoutModeButton,
3758
3969
  {
3759
3970
  active: layoutMode === "list",
@@ -3762,30 +3973,30 @@ function LayoutModeToggle({
3762
3973
  onClick: () => {
3763
3974
  onLayoutModeChange("list");
3764
3975
  },
3765
- children: /* @__PURE__ */ jsx8(ViewListLinedIcon, { className: "size-4" })
3976
+ children: /* @__PURE__ */ jsx10(ViewListLinedIcon, { className: "size-4" })
3766
3977
  }
3767
3978
  ),
3768
- /* @__PURE__ */ jsx8("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 }),
3769
3980
  /* @__PURE__ */ jsxs6(DropdownMenu, { children: [
3770
- /* @__PURE__ */ jsx8(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx8(
3771
- Button2,
3981
+ /* @__PURE__ */ jsx10(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
3982
+ Button3,
3772
3983
  {
3773
3984
  "aria-label": copy.t("arrangeMenuLabel"),
3774
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",
3775
3986
  size: "icon-sm",
3776
3987
  type: "button",
3777
3988
  variant: "ghost",
3778
- children: /* @__PURE__ */ jsx8(ChevronDownIcon, { className: "size-3.5" })
3989
+ children: /* @__PURE__ */ jsx10(ChevronDownIcon, { className: "size-3.5" })
3779
3990
  }
3780
3991
  ) }),
3781
- /* @__PURE__ */ jsx8(
3992
+ /* @__PURE__ */ jsx10(
3782
3993
  DropdownMenuContent,
3783
3994
  {
3784
3995
  align: "end",
3785
3996
  className: "min-w-[236px] px-1 py-1",
3786
3997
  sideOffset: 7,
3787
3998
  style: { zIndex: "var(--z-panel-popover)" },
3788
- children: /* @__PURE__ */ jsx8(
3999
+ children: /* @__PURE__ */ jsx10(
3789
4000
  DropdownMenuRadioGroup,
3790
4001
  {
3791
4002
  value: arrangeMode,
@@ -3795,8 +4006,8 @@ function LayoutModeToggle({
3795
4006
  );
3796
4007
  },
3797
4008
  children: arrangeOptions.map((option, index) => /* @__PURE__ */ jsxs6("div", { children: [
3798
- index === 1 ? /* @__PURE__ */ jsx8(DropdownMenuSeparator, {}) : null,
3799
- /* @__PURE__ */ jsx8(
4009
+ index === 1 ? /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}) : null,
4010
+ /* @__PURE__ */ jsx10(
3800
4011
  DropdownMenuRadioItem,
3801
4012
  {
3802
4013
  className: "h-8 text-sm font-normal",
@@ -3822,12 +4033,12 @@ function LayoutModeButton({
3822
4033
  tooltipLabel
3823
4034
  }) {
3824
4035
  return /* @__PURE__ */ jsxs6(Tooltip, { children: [
3825
- /* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx8(
3826
- Button2,
4036
+ /* @__PURE__ */ jsx10(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx10(
4037
+ Button3,
3827
4038
  {
3828
4039
  "aria-label": ariaLabel,
3829
4040
  "aria-pressed": active,
3830
- className: cn5(
4041
+ className: cn6(
3831
4042
  "size-6 min-w-6 rounded-[4px] p-0 text-text-secondary",
3832
4043
  active && "!bg-background-fronted text-foreground hover:!bg-background-fronted"
3833
4044
  ),
@@ -3838,7 +4049,7 @@ function LayoutModeButton({
3838
4049
  children
3839
4050
  }
3840
4051
  ) }),
3841
- /* @__PURE__ */ jsx8(TooltipContent, { side: "bottom", children: tooltipLabel })
4052
+ /* @__PURE__ */ jsx10(TooltipContent, { side: "bottom", children: tooltipLabel })
3842
4053
  ] });
3843
4054
  }
3844
4055
  function ToolbarIconButton({
@@ -3848,8 +4059,8 @@ function ToolbarIconButton({
3848
4059
  onClick,
3849
4060
  title
3850
4061
  }) {
3851
- return /* @__PURE__ */ jsx8(
3852
- Button2,
4062
+ return /* @__PURE__ */ jsx10(
4063
+ Button3,
3853
4064
  {
3854
4065
  "aria-label": ariaLabel,
3855
4066
  className: "size-7 min-w-7 rounded-sm p-0 text-[var(--text-primary)]",
@@ -3868,8 +4079,8 @@ function ToolbarActionButton({
3868
4079
  disabled,
3869
4080
  onClick
3870
4081
  }) {
3871
- return /* @__PURE__ */ jsx8(
3872
- Button2,
4082
+ return /* @__PURE__ */ jsx10(
4083
+ Button3,
3873
4084
  {
3874
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",
3875
4086
  disabled,
@@ -3888,11 +4099,11 @@ function BreadcrumbButton({
3888
4099
  showSeparator
3889
4100
  }) {
3890
4101
  return /* @__PURE__ */ jsxs6("li", { className: "flex min-w-0 items-center gap-2", children: [
3891
- showSeparator ? /* @__PURE__ */ jsx8("span", { className: "flex-none text-[var(--text-tertiary)]", children: "/" }) : null,
3892
- /* @__PURE__ */ jsx8(
4102
+ showSeparator ? /* @__PURE__ */ jsx10("span", { className: "flex-none text-[var(--text-tertiary)]", children: "/" }) : null,
4103
+ /* @__PURE__ */ jsx10(
3893
4104
  "button",
3894
4105
  {
3895
- className: cn5(
4106
+ className: cn6(
3896
4107
  "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap border-0 bg-transparent text-sm font-normal transition-colors",
3897
4108
  active ? "font-semibold text-[var(--text-primary)]" : "text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
3898
4109
  ),
@@ -3913,9 +4124,9 @@ import {
3913
4124
  TooltipContent as TooltipContent2,
3914
4125
  TooltipProvider as TooltipProvider2,
3915
4126
  TooltipTrigger as TooltipTrigger2,
3916
- cn as cn6
4127
+ cn as cn7
3917
4128
  } from "@tutti-os/ui-system";
3918
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
4129
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
3919
4130
  function WorkspaceFileManagerSidebar({
3920
4131
  disabled,
3921
4132
  locationSections,
@@ -3929,14 +4140,14 @@ function WorkspaceFileManagerSidebar({
3929
4140
  if (visibleSections.length === 0) {
3930
4141
  return null;
3931
4142
  }
3932
- return /* @__PURE__ */ jsx9(
4143
+ return /* @__PURE__ */ jsx11(
3933
4144
  "aside",
3934
4145
  {
3935
4146
  className: "@max-[600px]/workspace-file-manager:hidden flex shrink-0 flex-col border-r border-[var(--border-1)] bg-[var(--background-panel)]",
3936
4147
  style: { minWidth: width, width },
3937
- children: /* @__PURE__ */ jsx9(ScrollArea2, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx9(TooltipProvider2, { delayDuration: 350, children: /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-3 px-2 py-3", children: visibleSections.map((section) => /* @__PURE__ */ jsxs7("section", { className: "min-w-0", children: [
3938
- /* @__PURE__ */ jsx9("h2", { className: "mb-1 px-2 text-[11px] font-medium uppercase tracking-normal text-[var(--text-tertiary)]", children: section.label }),
3939
- /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-0.5", children: section.locations.map((location) => /* @__PURE__ */ jsx9(
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(
3940
4151
  WorkspaceFileManagerSidebarLocation,
3941
4152
  {
3942
4153
  active: location.id === selectedLocationId,
@@ -3961,7 +4172,7 @@ function WorkspaceFileManagerSidebarLocation({
3961
4172
  "button",
3962
4173
  {
3963
4174
  "aria-current": active ? "page" : void 0,
3964
- className: cn6(
4175
+ className: cn7(
3965
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",
3966
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)]",
3967
4178
  disabled && "pointer-events-none opacity-60"
@@ -3972,8 +4183,8 @@ function WorkspaceFileManagerSidebarLocation({
3972
4183
  onSelectLocation(location);
3973
4184
  },
3974
4185
  children: [
3975
- location.kind === "external" && location.iconUrl ? /* @__PURE__ */ jsx9(ExternalLocationIcon, { location }) : /* @__PURE__ */ jsx9(Icon, { className: "size-4 flex-none" }),
3976
- /* @__PURE__ */ jsx9("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 })
3977
4188
  ]
3978
4189
  }
3979
4190
  );
@@ -3981,14 +4192,14 @@ function WorkspaceFileManagerSidebarLocation({
3981
4192
  return content;
3982
4193
  }
3983
4194
  return /* @__PURE__ */ jsxs7(Tooltip2, { children: [
3984
- /* @__PURE__ */ jsx9(TooltipTrigger2, { asChild: true, children: content }),
3985
- /* @__PURE__ */ jsx9(TooltipContent2, { side: "right", children: location.contextLabel })
4195
+ /* @__PURE__ */ jsx11(TooltipTrigger2, { asChild: true, children: content }),
4196
+ /* @__PURE__ */ jsx11(TooltipContent2, { side: "right", children: location.contextLabel })
3986
4197
  ] });
3987
4198
  }
3988
4199
  function ExternalLocationIcon({
3989
4200
  location
3990
4201
  }) {
3991
- return /* @__PURE__ */ jsx9(
4202
+ return /* @__PURE__ */ jsx11(
3992
4203
  "img",
3993
4204
  {
3994
4205
  alt: "",
@@ -4062,7 +4273,7 @@ function normalizeDirectoryPath(path) {
4062
4273
  }
4063
4274
 
4064
4275
  // src/ui/WorkspaceFileManager.tsx
4065
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
4276
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
4066
4277
  var workspaceFileManagerSearchDebounceMs = 180;
4067
4278
  function WorkspaceFileManager({
4068
4279
  className,
@@ -4072,6 +4283,7 @@ function WorkspaceFileManager({
4072
4283
  onCopyEntry,
4073
4284
  onDirectoryExpanded,
4074
4285
  onEntryDragStart,
4286
+ previewActions,
4075
4287
  resolveContextMenu,
4076
4288
  resolveEntryIconUrl,
4077
4289
  renderExternalLocationContent,
@@ -4091,7 +4303,7 @@ function WorkspaceFileManager({
4091
4303
  const { layoutMode, setLayoutMode } = useWorkspaceFileManagerLayoutMode();
4092
4304
  const rootView = useWorkspaceFileManagerRootView(session);
4093
4305
  const { state: panelsState, view: panelsView } = useWorkspaceFileManagerPanelsView(session);
4094
- const selectedExternalLocation = useMemo4(() => {
4306
+ const selectedExternalLocation = useMemo5(() => {
4095
4307
  const location = findWorkspaceFileLocationById(
4096
4308
  rootView.locationSections,
4097
4309
  rootView.selectedLocationId
@@ -4244,8 +4456,9 @@ function WorkspaceFileManager({
4244
4456
  }
4245
4457
  event.preventDefault();
4246
4458
  void (async () => {
4247
- await session.copyToClipboard(entry);
4248
- await onCopyEntry?.();
4459
+ if (await session.copyToClipboard(entry)) {
4460
+ await onCopyEntry?.();
4461
+ }
4249
4462
  })();
4250
4463
  }
4251
4464
  window.addEventListener("keydown", handleCopyShortcut);
@@ -4320,7 +4533,7 @@ function WorkspaceFileManager({
4320
4533
  return /* @__PURE__ */ jsxs8(
4321
4534
  "section",
4322
4535
  {
4323
- className: cn7(
4536
+ className: cn8(
4324
4537
  "@container/workspace-file-manager relative flex h-full min-h-0 w-full overflow-hidden text-[14px] text-[var(--text-primary)]",
4325
4538
  surface === "card" ? "rounded-lg border border-[var(--border-1)] bg-[var(--background-panel)]" : "rounded-none border-0 bg-transparent",
4326
4539
  className
@@ -4335,7 +4548,7 @@ function WorkspaceFileManager({
4335
4548
  "--workspace-file-manager-dialog-overlay-z-index": "20"
4336
4549
  },
4337
4550
  children: [
4338
- hasLocationSidebar ? /* @__PURE__ */ jsx10(
4551
+ hasLocationSidebar ? /* @__PURE__ */ jsx12(
4339
4552
  WorkspaceFileManagerSidebar,
4340
4553
  {
4341
4554
  disabled: rootView.isBusy || panelsState.isLoading,
@@ -4350,7 +4563,7 @@ function WorkspaceFileManager({
4350
4563
  }
4351
4564
  }
4352
4565
  ) : null,
4353
- hasLocationSidebar ? /* @__PURE__ */ jsx10(
4566
+ hasLocationSidebar ? /* @__PURE__ */ jsx12(
4354
4567
  "div",
4355
4568
  {
4356
4569
  "aria-label": i18n.t("resizeLocationsSidebar"),
@@ -4368,8 +4581,8 @@ function WorkspaceFileManager({
4368
4581
  onPointerUp: handleSidebarResizePointerEnd
4369
4582
  }
4370
4583
  ) : null,
4371
- /* @__PURE__ */ jsx10("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children: selectedExternalLocation ? renderExternalLocationContent?.(selectedExternalLocation) ?? null : /* @__PURE__ */ jsxs8(Fragment3, { children: [
4372
- /* @__PURE__ */ jsx10(
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(
4373
4586
  WorkspaceFileManagerToolbarContainer,
4374
4587
  {
4375
4588
  i18n,
@@ -4382,7 +4595,7 @@ function WorkspaceFileManager({
4382
4595
  session
4383
4596
  }
4384
4597
  ),
4385
- /* @__PURE__ */ jsx10("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__ */ jsx10(
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(
4386
4599
  WorkspaceFileManagerPanelsContainer,
4387
4600
  {
4388
4601
  dateLocale,
@@ -4390,9 +4603,11 @@ function WorkspaceFileManager({
4390
4603
  arrangeMode,
4391
4604
  i18n,
4392
4605
  layoutMode,
4606
+ onCopyEntry,
4393
4607
  onDirectoryExpanded,
4394
4608
  onEntryDragStart,
4395
4609
  onOpenContextMenu: openContextMenu,
4610
+ previewActions,
4396
4611
  resolveEntryIconUrl,
4397
4612
  session,
4398
4613
  showPreviewPanel
@@ -4400,8 +4615,8 @@ function WorkspaceFileManager({
4400
4615
  ) })
4401
4616
  ] }) }),
4402
4617
  !selectedExternalLocation ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
4403
- /* @__PURE__ */ jsx10(WorkspaceFileManagerDialogsContainer, { i18n, session }),
4404
- /* @__PURE__ */ jsx10(
4618
+ /* @__PURE__ */ jsx12(WorkspaceFileManagerDialogsContainer, { i18n, session }),
4619
+ /* @__PURE__ */ jsx12(
4405
4620
  WorkspaceFileManagerContextMenuContainer,
4406
4621
  {
4407
4622
  resolveContextMenu,
@@ -4455,7 +4670,7 @@ function WorkspaceFileManagerToolbarContainer({
4455
4670
  setSearchQuery("");
4456
4671
  submitSearchQuery("");
4457
4672
  }, [submitSearchQuery]);
4458
- return /* @__PURE__ */ jsx10(
4673
+ return /* @__PURE__ */ jsx12(
4459
4674
  WorkspaceFileManagerToolbar,
4460
4675
  {
4461
4676
  breadcrumbs: view.breadcrumbs,
@@ -4503,7 +4718,7 @@ function WorkspaceFileManagerDialogsContainer({
4503
4718
  }) {
4504
4719
  const { state, view } = useWorkspaceFileManagerDialogsView(session);
4505
4720
  return /* @__PURE__ */ jsxs8(Fragment3, { children: [
4506
- /* @__PURE__ */ jsx10(
4721
+ /* @__PURE__ */ jsx12(
4507
4722
  WorkspaceFileManagerCreateDialog,
4508
4723
  {
4509
4724
  busy: view.isBusy && state.busyAction === "create",
@@ -4520,7 +4735,7 @@ function WorkspaceFileManagerDialogsContainer({
4520
4735
  }
4521
4736
  }
4522
4737
  ),
4523
- /* @__PURE__ */ jsx10(
4738
+ /* @__PURE__ */ jsx12(
4524
4739
  WorkspaceFileManagerDeleteDialog,
4525
4740
  {
4526
4741
  busy: view.isDeleting,
@@ -4534,7 +4749,7 @@ function WorkspaceFileManagerDialogsContainer({
4534
4749
  }
4535
4750
  }
4536
4751
  ),
4537
- /* @__PURE__ */ jsx10(
4752
+ /* @__PURE__ */ jsx12(
4538
4753
  WorkspaceFileManagerUnsupportedDialog,
4539
4754
  {
4540
4755
  copy: i18n,