@tutti-os/workspace-file-manager 0.0.38 → 0.0.40

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.
@@ -2478,6 +2478,8 @@ export {
2478
2478
  resolveWorkspaceFileLocationDefaultId,
2479
2479
  isWorkspaceFileRecentLocation,
2480
2480
  findWorkspaceFileEntry,
2481
+ resolveWorkspaceFileOpenWithCacheKey,
2482
+ WorkspaceFileOpenWithApplicationsCache,
2481
2483
  createWorkspaceFileManagerService,
2482
2484
  shouldResolveWorkspaceFileEntryIcon,
2483
2485
  shouldUseWorkspaceFileExtensionDocumentIcon,
@@ -2490,4 +2492,4 @@ export {
2490
2492
  sortWorkspaceFileEntriesForArrangeMode,
2491
2493
  resolveWorkspaceFileEntryArrangeDateMs
2492
2494
  };
2493
- //# sourceMappingURL=chunk-KBMBX2K2.js.map
2495
+ //# sourceMappingURL=chunk-M7G5F5PL.js.map
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { E as WorkspaceFileOpenWithApplication, c as WorkspaceFileEntry, B as WorkspaceFileManagerSession } from './workspaceFileManagerLocations-VT6VDqTL.js';
2
2
  export { C as CreateWorkspaceFileManagerSessionInput, W as WorkspaceFileActivationTarget, a as WorkspaceFileDirectoryListing, b as WorkspaceFileDirectoryLocation, d as WorkspaceFileEntryKind, e as WorkspaceFileImportConflict, f as WorkspaceFileImportSummary, g as WorkspaceFileImportSummaryReason, h as WorkspaceFileImportSummaryReasonCount, i as WorkspaceFileLocation, j as WorkspaceFileLocationKind, k as WorkspaceFileLocationSection, l as WorkspaceFileManagerCapabilities, m as WorkspaceFileManagerFileActivationRequest, o as WorkspaceFileManagerHost, p as WorkspaceFileManagerHostActionMessage, q as WorkspaceFileManagerHostActionMessageStatus, r as WorkspaceFileManagerHostActionResult, s as WorkspaceFileManagerHostExportResult, t as WorkspaceFileManagerHostFallbackAction, u as WorkspaceFileManagerHostFallbackActionKind, v as WorkspaceFileManagerHostFileActivationResult, w as WorkspaceFileManagerHostImportConflict, x as WorkspaceFileManagerHostImportResult, y as WorkspaceFileManagerMutationErrorMessage, z as WorkspaceFileManagerPersistedState, A as WorkspaceFileManagerService, D as WorkspaceFileManagerState, F as WorkspaceFilePreviewKind, G as WorkspaceFilePreviewState, H as WorkspaceFileRecentLocation, I as WorkspaceFileSearchEntry, J as WorkspaceFileSearchResult, K as classifyWorkspaceFilePreviewKind, L as createWorkspaceFileManagerService, M as findWorkspaceFileLocationById, N as flattenWorkspaceFileLocations, O as isWorkspaceFileRecentLocation, P as resolveWorkspaceFileActivationTarget, Q as resolveWorkspaceFileLocationDefaultId } from './workspaceFileManagerLocations-VT6VDqTL.js';
3
3
  import { a as WorkspaceFileManagerI18nRuntime } from './index-D23bxzS8.js';
4
- export { W as WorkspaceFileManagerI18nKey, c as createWorkspaceFileManagerI18nRuntime, w as workspaceFileManagerI18nNamespace, b as workspaceFileManagerI18nResources } from './index-D23bxzS8.js';
5
- import { ReactElement } from 'react';
4
+ export { W as WorkspaceFileManagerI18nKey, c as createWorkspaceFileManagerI18nRuntime, r as resolveRevealInFolderLabel, w as workspaceFileManagerI18nNamespace, b as workspaceFileManagerI18nResources } from './index-D23bxzS8.js';
5
+ import { ReactElement, RefObject } from 'react';
6
6
  import { TuttiDateLocale } from '@tutti-os/ui-system/date-format';
7
7
  export { decodeWorkspaceTextFile, isWorkspaceTextFileTooLarge, looksLikeBinaryText, resolveWorkspaceFileExtension, resolveWorkspaceImageMimeType, resolveWorkspaceVideoMimeType, workspaceFilePreviewMaxBytes, workspaceFileTextMaxBytes } from '@tutti-os/workspace-file-preview';
8
8
  import '@tutti-os/ui-i18n-runtime';
@@ -27,4 +27,51 @@ interface WorkspaceFileManagerProps {
27
27
  }
28
28
  declare function WorkspaceFileManager({ className, dateLocale, entryDragMode, i18n, onCopyEntry, onCopyPath, onDirectoryExpanded, onEntryDragStart, openInAppBrowserIcon, resolveOpenWithApplicationIcon, resolveEntryIconUrl, hostOs, session, surface }: WorkspaceFileManagerProps): ReactElement;
29
29
 
30
- export { WorkspaceFileEntry, WorkspaceFileManager, type WorkspaceFileManagerEntryDragMode, WorkspaceFileManagerI18nRuntime, type WorkspaceFileManagerProps, WorkspaceFileManagerSession };
30
+ interface WorkspaceFileManagerContextMenuProps {
31
+ busy: boolean;
32
+ copy: WorkspaceFileManagerI18nRuntime;
33
+ contextMenu: {
34
+ entry: WorkspaceFileEntry | null;
35
+ x: number;
36
+ y: number;
37
+ } | null;
38
+ contextMenuRef: RefObject<HTMLDivElement | null>;
39
+ openInAppBrowserIcon?: ReactElement;
40
+ positionMode?: "local" | "viewport";
41
+ resolveOpenWithApplicationIcon?: (application: WorkspaceFileOpenWithApplication) => ReactElement | null;
42
+ showCopyAction: boolean;
43
+ showCopyPathAction: boolean;
44
+ showCreateAction: boolean;
45
+ showDeleteAction: boolean;
46
+ showImportAction: boolean;
47
+ showExportAction: boolean;
48
+ showOpenInAppBrowserAction: boolean;
49
+ showOpenInDefaultBrowserAction: boolean;
50
+ showOpenInFileViewerAction: boolean;
51
+ showOpenWithAction: boolean;
52
+ showOpenWithOtherAction: boolean;
53
+ showRevealInFolderAction: boolean;
54
+ showRenameAction: boolean;
55
+ revealInFolderLabel: string;
56
+ openWithApplications: readonly WorkspaceFileOpenWithApplication[];
57
+ openWithLoading: boolean;
58
+ onClose: () => void;
59
+ onCreateDirectory: () => void;
60
+ onCreateFile: () => void;
61
+ onCopy: () => Promise<void>;
62
+ onCopyPath: () => Promise<void>;
63
+ onDelete: () => void;
64
+ onExport: () => Promise<void>;
65
+ onOpen: () => Promise<void>;
66
+ onOpenInAppBrowser: () => Promise<void>;
67
+ onOpenInDefaultBrowser: () => Promise<void>;
68
+ onOpenInFileViewer: () => Promise<void>;
69
+ onOpenWithApplication: (applicationPath: string) => Promise<void>;
70
+ onOpenWithOtherApplication: () => Promise<void>;
71
+ onImport: () => Promise<void>;
72
+ onRevealInFolder: () => Promise<void>;
73
+ onRename: () => void;
74
+ }
75
+ declare function WorkspaceFileManagerContextMenu({ busy, copy, contextMenu, contextMenuRef, openInAppBrowserIcon, positionMode, showCopyAction, showCopyPathAction, showCreateAction, showDeleteAction, showImportAction, showExportAction, showOpenInAppBrowserAction, showOpenInDefaultBrowserAction, showOpenInFileViewerAction, showOpenWithAction, showOpenWithOtherAction, showRevealInFolderAction, showRenameAction, revealInFolderLabel, openWithApplications, openWithLoading, resolveOpenWithApplicationIcon, onClose, onCreateDirectory, onCreateFile, onCopy, onCopyPath, onDelete, onExport, onOpen, onOpenInAppBrowser, onOpenInDefaultBrowser, onOpenInFileViewer, onOpenWithApplication, onOpenWithOtherApplication, onImport, onRevealInFolder, onRename }: WorkspaceFileManagerContextMenuProps): ReactElement | null;
76
+
77
+ export { WorkspaceFileEntry, WorkspaceFileManager, WorkspaceFileManagerContextMenu, type WorkspaceFileManagerContextMenuProps, type WorkspaceFileManagerEntryDragMode, WorkspaceFileManagerI18nRuntime, type WorkspaceFileManagerProps, WorkspaceFileManagerSession, WorkspaceFileOpenWithApplication };
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  workspaceFileSearchEntryToEntry,
33
33
  workspaceFileTextMaxBytes,
34
34
  writeWorkspaceFileManagerArrangeMode
35
- } from "./chunk-KBMBX2K2.js";
35
+ } from "./chunk-M7G5F5PL.js";
36
36
  import {
37
37
  createWorkspaceFileManagerI18nRuntime,
38
38
  resolveRevealInFolderLabel,
@@ -124,7 +124,9 @@ function WorkspaceFileManagerContextMenu({
124
124
  contextMenu,
125
125
  contextMenuRef,
126
126
  openInAppBrowserIcon,
127
+ positionMode = "local",
127
128
  showCopyAction,
129
+ showCopyPathAction,
128
130
  showCreateAction,
129
131
  showDeleteAction,
130
132
  showImportAction,
@@ -172,16 +174,21 @@ function WorkspaceFileManagerContextMenu({
172
174
  return;
173
175
  }
174
176
  const menu = contextMenuRef.current;
175
- const boundary = menu?.closest("[data-workspace-file-manager]");
176
- if (!menu || !boundary) {
177
+ if (!menu) {
177
178
  return;
178
179
  }
179
- const boundaryRect = boundary.getBoundingClientRect();
180
180
  const menuRect = menu.getBoundingClientRect();
181
+ const boundary = positionMode === "local" ? menu.closest(
182
+ "[data-workspace-file-menu-boundary], [data-workspace-file-manager]"
183
+ ) : null;
184
+ if (positionMode === "local" && !boundary) {
185
+ return;
186
+ }
187
+ const boundaryRect = boundary?.getBoundingClientRect();
181
188
  setPosition(
182
189
  clampContextMenuPosition({
183
- boundaryHeight: boundaryRect.height,
184
- boundaryWidth: boundaryRect.width,
190
+ boundaryHeight: boundaryRect?.height ?? window.innerHeight,
191
+ boundaryWidth: boundaryRect?.width ?? window.innerWidth,
185
192
  menuHeight: menuRect.height,
186
193
  menuWidth: menuRect.width,
187
194
  x: contextMenu.x,
@@ -193,7 +200,9 @@ function WorkspaceFileManagerContextMenu({
193
200
  contextMenuRef,
194
201
  openWithApplications.length,
195
202
  openWithLoading,
203
+ positionMode,
196
204
  showCopyAction,
205
+ showCopyPathAction,
197
206
  showExportAction,
198
207
  showImportAction,
199
208
  showOpenInAppBrowserAction,
@@ -232,13 +241,15 @@ function WorkspaceFileManagerContextMenu({
232
241
  label: copy.t("copyLabel")
233
242
  });
234
243
  }
235
- editItems.push({
236
- action: onCopyPath,
237
- disabled: busy,
238
- icon: /* @__PURE__ */ jsx(CopyIcon, { className: "size-4" }),
239
- key: "copy-path",
240
- label: copy.t("copyPathLabel")
241
- });
244
+ if (showCopyPathAction) {
245
+ editItems.push({
246
+ action: onCopyPath,
247
+ disabled: busy,
248
+ icon: /* @__PURE__ */ jsx(CopyIcon, { className: "size-4" }),
249
+ key: "copy-path",
250
+ label: copy.t("copyPathLabel")
251
+ });
252
+ }
242
253
  if (showRevealInFolderAction) {
243
254
  editItems.push({
244
255
  action: onRevealInFolder,
@@ -318,7 +329,10 @@ function WorkspaceFileManagerContextMenu({
318
329
  MenuSurface,
319
330
  {
320
331
  ref: contextMenuRef,
321
- className: "absolute w-[220px] overflow-visible p-1",
332
+ className: cn(
333
+ "w-[220px] overflow-visible p-1",
334
+ positionMode === "viewport" ? "fixed" : "absolute"
335
+ ),
322
336
  role: "menu",
323
337
  style: {
324
338
  left: `${position.x}px`,
@@ -399,12 +413,22 @@ function ContextMenuActionGroup({
399
413
  ] });
400
414
  }
401
415
  function ContextMenuActionButton({
416
+ activateOnPointerDown = false,
402
417
  danger = false,
403
418
  disabled = false,
404
419
  icon,
405
420
  label,
406
421
  onClick
407
422
  }) {
423
+ const pointerActivatedRef = useRef(false);
424
+ const pointerActivationResetTimerRef = useRef(null);
425
+ useEffect(() => {
426
+ return () => {
427
+ if (pointerActivationResetTimerRef.current !== null) {
428
+ window.clearTimeout(pointerActivationResetTimerRef.current);
429
+ }
430
+ };
431
+ }, []);
408
432
  return /* @__PURE__ */ jsxs(
409
433
  "button",
410
434
  {
@@ -415,7 +439,33 @@ function ContextMenuActionButton({
415
439
  disabled,
416
440
  role: "menuitem",
417
441
  type: "button",
418
- onClick,
442
+ onClick: () => {
443
+ if (pointerActivatedRef.current) {
444
+ pointerActivatedRef.current = false;
445
+ if (pointerActivationResetTimerRef.current !== null) {
446
+ window.clearTimeout(pointerActivationResetTimerRef.current);
447
+ pointerActivationResetTimerRef.current = null;
448
+ }
449
+ return;
450
+ }
451
+ onClick();
452
+ },
453
+ onPointerDown: (event) => {
454
+ if (!activateOnPointerDown || disabled || event.button !== 0) {
455
+ return;
456
+ }
457
+ event.preventDefault();
458
+ event.stopPropagation();
459
+ pointerActivatedRef.current = true;
460
+ if (pointerActivationResetTimerRef.current !== null) {
461
+ window.clearTimeout(pointerActivationResetTimerRef.current);
462
+ }
463
+ pointerActivationResetTimerRef.current = window.setTimeout(() => {
464
+ pointerActivatedRef.current = false;
465
+ pointerActivationResetTimerRef.current = null;
466
+ }, 750);
467
+ onClick();
468
+ },
419
469
  children: [
420
470
  /* @__PURE__ */ jsx(
421
471
  "span",
@@ -455,7 +505,7 @@ function OpenWithMenuItem({
455
505
  }) {
456
506
  const triggerRef = useRef(null);
457
507
  const [open, setOpen] = useState(false);
458
- const [placementPoint, setPlacementPoint] = useState({ x: 0, y: 0 });
508
+ const [submenuPosition, setSubmenuPosition] = useState({ left: 0, top: 0 });
459
509
  const closeTimerRef = useRef(null);
460
510
  const showExternalSection = showOpenInDefaultBrowser || showOpenWithOther || isLoading || applications.length > 0;
461
511
  const estimatedSubmenuHeight = estimateOpenWithSubmenuHeight({
@@ -498,9 +548,14 @@ function OpenWithMenuItem({
498
548
  return;
499
549
  }
500
550
  const rect = trigger.getBoundingClientRect();
501
- setPlacementPoint({
502
- x: rect.right + CONTEXT_MENU_SUBMENU_GAP_PX,
503
- y: rect.top
551
+ const maxHeight = Math.min(
552
+ estimatedSubmenuHeight,
553
+ 480,
554
+ Math.max(0, window.innerHeight - 24)
555
+ );
556
+ setSubmenuPosition({
557
+ left: rect.right + CONTEXT_MENU_SUBMENU_GAP_PX,
558
+ top: Math.max(12, Math.min(rect.top, window.innerHeight - maxHeight - 12))
504
559
  });
505
560
  }, [open, estimatedSubmenuHeight]);
506
561
  return /* @__PURE__ */ jsxs(
@@ -556,40 +611,42 @@ function OpenWithMenuItem({
556
611
  dismissOnPointerDownOutside: false,
557
612
  dismissOnScroll: false,
558
613
  placement: {
559
- type: "point",
560
- point: placementPoint,
561
- alignX: "start",
562
- alignY: "auto",
563
- estimatedSize: {
564
- width: 220,
565
- height: estimatedSubmenuHeight
566
- }
614
+ type: "absolute",
615
+ left: submenuPosition.left,
616
+ top: submenuPosition.top,
617
+ boundaryPoint: { x: -1, y: -1 },
618
+ constrainToBoundary: false
567
619
  },
568
620
  role: "menu",
621
+ style: { zIndex: "calc(var(--z-panel-popover) + 200)" },
569
622
  onPointerEnter: openSubmenu,
570
623
  onPointerLeave: scheduleClose,
571
624
  children: [
572
625
  showOpenInFileViewer ? /* @__PURE__ */ jsx(
573
626
  ContextMenuActionButton,
574
627
  {
628
+ activateOnPointerDown: true,
575
629
  disabled: busy,
576
630
  icon: /* @__PURE__ */ jsx(EyeIcon, { className: "size-4" }),
577
631
  label: copy.t("openInFileViewerLabel"),
578
632
  onClick: () => {
633
+ const openPromise = onOpenInFileViewer();
579
634
  onClose();
580
- void onOpenInFileViewer();
635
+ void openPromise;
581
636
  }
582
637
  }
583
638
  ) : null,
584
639
  showOpenInAppBrowser ? /* @__PURE__ */ jsx(
585
640
  ContextMenuActionButton,
586
641
  {
642
+ activateOnPointerDown: true,
587
643
  disabled: busy,
588
644
  icon: openInAppBrowserIcon ?? /* @__PURE__ */ jsx(WebIcon, { className: "size-4" }),
589
645
  label: copy.t("openInAppBrowserLabel"),
590
646
  onClick: () => {
647
+ const openPromise = onOpenInAppBrowser();
591
648
  onClose();
592
- void onOpenInAppBrowser();
649
+ void openPromise;
593
650
  }
594
651
  }
595
652
  ) : null,
@@ -600,6 +657,7 @@ function OpenWithMenuItem({
600
657
  return /* @__PURE__ */ jsx(
601
658
  ContextMenuActionButton,
602
659
  {
660
+ activateOnPointerDown: true,
603
661
  disabled: busy,
604
662
  icon: resolvedIcon ?? (application.iconDataUrl ? /* @__PURE__ */ jsx(
605
663
  "img",
@@ -611,8 +669,11 @@ function OpenWithMenuItem({
611
669
  ) : /* @__PURE__ */ jsx(EyeIcon, { className: "size-4" })),
612
670
  label: application.name,
613
671
  onClick: () => {
672
+ const openPromise = onOpenWithApplication(
673
+ application.applicationPath
674
+ );
614
675
  onClose();
615
- void onOpenWithApplication(application.applicationPath);
676
+ void openPromise;
616
677
  }
617
678
  },
618
679
  application.applicationPath
@@ -621,24 +682,28 @@ function OpenWithMenuItem({
621
682
  showOpenInDefaultBrowser ? /* @__PURE__ */ jsx(
622
683
  ContextMenuActionButton,
623
684
  {
685
+ activateOnPointerDown: true,
624
686
  disabled: busy,
625
687
  icon: /* @__PURE__ */ jsx(WebIcon, { className: "size-4" }),
626
688
  label: copy.t("openInDefaultBrowserLabel"),
627
689
  onClick: () => {
690
+ const openPromise = onOpenInDefaultBrowser();
628
691
  onClose();
629
- void onOpenInDefaultBrowser();
692
+ void openPromise;
630
693
  }
631
694
  }
632
695
  ) : null,
633
696
  showOpenWithOther ? /* @__PURE__ */ jsx(
634
697
  ContextMenuActionButton,
635
698
  {
699
+ activateOnPointerDown: true,
636
700
  disabled: busy,
637
701
  icon: /* @__PURE__ */ jsx(LaunchIcon, { className: "size-4" }),
638
702
  label: copy.t("openWithOtherLabel"),
639
703
  onClick: () => {
704
+ const openPromise = onOpenWithOtherApplication();
640
705
  onClose();
641
- void onOpenWithOtherApplication();
706
+ void openPromise;
642
707
  }
643
708
  }
644
709
  ) : null
@@ -902,6 +967,7 @@ function WorkspaceFileManagerContextMenuContainer({
902
967
  contextMenu: view.contextMenu,
903
968
  contextMenuRef,
904
969
  showCopyAction: view.showCopyAction,
970
+ showCopyPathAction: true,
905
971
  showCreateAction: view.showCreateAction,
906
972
  showDeleteAction: view.showDeleteAction,
907
973
  showImportAction: view.showImportAction,
@@ -4519,6 +4585,7 @@ function isPointInsideElement(element, clientX, clientY) {
4519
4585
  }
4520
4586
  export {
4521
4587
  WorkspaceFileManager,
4588
+ WorkspaceFileManagerContextMenu,
4522
4589
  classifyWorkspaceFilePreviewKind,
4523
4590
  createWorkspaceFileManagerI18nRuntime,
4524
4591
  createWorkspaceFileManagerService,
@@ -4528,6 +4595,7 @@ export {
4528
4595
  isWorkspaceFileRecentLocation,
4529
4596
  isWorkspaceTextFileTooLarge,
4530
4597
  looksLikeBinaryText,
4598
+ resolveRevealInFolderLabel,
4531
4599
  resolveWorkspaceFileActivationTarget,
4532
4600
  resolveWorkspaceFileExtension,
4533
4601
  resolveWorkspaceFileLocationDefaultId,