@arbidocs/blocks 0.4.154 → 0.4.155

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/chunk-6HMCF4AJ.js +1720 -0
  2. package/dist/chunk-6HMCF4AJ.js.map +1 -0
  3. package/dist/{chunk-BDA3RHYD.cjs → chunk-MUDEL7UL.cjs} +25 -884
  4. package/dist/chunk-MUDEL7UL.cjs.map +1 -0
  5. package/dist/{chunk-RE7NULEY.js → chunk-W7CWGURO.js} +28 -873
  6. package/dist/chunk-W7CWGURO.js.map +1 -0
  7. package/dist/chunk-WV4TD2HZ.cjs +1785 -0
  8. package/dist/chunk-WV4TD2HZ.cjs.map +1 -0
  9. package/dist/connect/index.cjs +1178 -16
  10. package/dist/connect/index.cjs.map +1 -1
  11. package/dist/connect/index.d.ts +1 -0
  12. package/dist/connect/index.js +1160 -12
  13. package/dist/connect/index.js.map +1 -1
  14. package/dist/connect/viewer.cjs +40 -44
  15. package/dist/connect/viewer.cjs.map +1 -1
  16. package/dist/connect/viewer.js +2 -6
  17. package/dist/connect/viewer.js.map +1 -1
  18. package/dist/connection/files/DocumentSyncGlyph.d.ts +28 -0
  19. package/dist/connection/files/explorer/ConnectedFilesExplorer.d.ts +56 -0
  20. package/dist/connection/files/explorer/FileTiles.d.ts +87 -0
  21. package/dist/connection/files/index.d.ts +5 -0
  22. package/dist/connection/files/store.d.ts +27 -0
  23. package/dist/connection/files/support.d.ts +38 -0
  24. package/dist/files/DocStatusIcon.d.ts +5 -4
  25. package/dist/files/FailedStageIcon.d.ts +26 -0
  26. package/dist/files/folders/FolderProviderIcon.d.ts +29 -0
  27. package/dist/files/folders/folderPaths.d.ts +86 -0
  28. package/dist/files/folders/index.d.ts +2 -0
  29. package/dist/files/index.cjs +326 -238
  30. package/dist/files/index.d.ts +5 -0
  31. package/dist/files/index.js +2 -2
  32. package/dist/files/permissions.d.ts +28 -0
  33. package/dist/files/recently-deleted.d.ts +7 -0
  34. package/dist/files/visibility.d.ts +31 -0
  35. package/dist/index.cjs +38 -38
  36. package/dist/index.js +2 -2
  37. package/node_modules/@arbidocs/coedit/package.json +1 -1
  38. package/package.json +6 -5
  39. package/dist/chunk-BDA3RHYD.cjs.map +0 -1
  40. package/dist/chunk-JGOST4IL.js +0 -648
  41. package/dist/chunk-JGOST4IL.js.map +0 -1
  42. package/dist/chunk-RE7NULEY.js.map +0 -1
  43. package/dist/chunk-YAEXSCUW.cjs +0 -677
  44. package/dist/chunk-YAEXSCUW.cjs.map +0 -1
@@ -11,11 +11,11 @@ export { ConnectedAssistantDock, redlineStats, textToArtifact, toRedlineMarkdown
11
11
  import { useConnectedWorkspaceRole, ConnectedNativeDocxEditor } from '../chunk-GO3BN76C.js';
12
12
  export { ConnectedWorkflowDocumentsField, ConnectedWorkflowLauncher, ConnectedWorkflowsTable, DEFAULT_CONNECTED_WORKFLOWS_TABLE_LABELS, NativeDocxSupportProvider, toWorkflowFieldDocument, useConnectedAgentOptions, useConnectedWorkflowScopeSource, useConnectedWorkspaceRole, useNativeDocxSupport } from '../chunk-GO3BN76C.js';
13
13
  export { DocumentViewerSupportProvider, useDocumentViewerSupport } from '../chunk-EYJMMGXF.js';
14
- import { FilterButton, FilesViewCenter, FilesFilterBar, ROW_MIN, rowHeightForLines, linesForRowHeight, RowDensityControl, FolderFilterButton, FolderPickerRow, FieldLabelsSelect } from '../chunk-JGOST4IL.js';
14
+ import { FilterButton, ExplorerFileTile, formatFolderSegment, hasFolderProviderIcon, FolderProviderIcon, ExplorerFolderTile, searchExplorerDocuments, buildExplorerEntries, collectFolderDocs, joinFolderPath, getExplorerDisplayName, RECENTLY_DELETED_HINT, RECENTLY_DELETED_HINT_PLURAL, canDeleteDocument, ExplorerFrame, ExplorerStage, ExplorerScroller, ExplorerLoadingTiles, ExplorerEmpty, ExplorerMarquee, ExplorerDropOverlay, ExplorerDragPreview, ExplorerFileTooltip, visibilityAudience, StatusPill, FailedStageIcon, FilesViewCenter, FilesFilterBar, ROW_MIN, rowHeightForLines, linesForRowHeight, RowDensityControl, FolderFilterButton, FolderPickerRow, FieldLabelsSelect } from '../chunk-6HMCF4AJ.js';
15
15
  import '../chunk-RC6VTY2H.js';
16
16
  import { useConnectedTags, useConnectedDocuments, useConnectedConversations } from '../chunk-KDLX5AA7.js';
17
17
  export { ConnectedWorkspaceDataProvider, useConnectedConversations, useConnectedDocuments, useConnectedTags } from '../chunk-KDLX5AA7.js';
18
- import { ConnectionProvider, useConnection, useConnectedWorkspace } from '../chunk-TF7HN5EU.js';
18
+ import { useConnectedWorkspace, ConnectionProvider, useConnection } from '../chunk-TF7HN5EU.js';
19
19
  export { ConnectionProvider, SESSION_EXPIRED_MESSAGE, isAuthenticated, useConnectedWorkspace, useConnection } from '../chunk-TF7HN5EU.js';
20
20
  import { SignDocumentDialog } from '../chunk-2YOVK524.js';
21
21
  import '../chunk-MTSKK3BN.js';
@@ -25,10 +25,10 @@ import { ViewToggle } from '../chunk-JJA6GQKH.js';
25
25
  import { FactSourcesPanel, FactPassageItem } from '../chunk-HFZ45FD3.js';
26
26
  import { WorkspaceActivityList, WorkspaceNotificationMenu, ThreadFollowMenuItem, ThreadFollowButton, TagTable, TagPicker, tagsPageNouns, entitiesPhrase, ExtractEntitiesDialog, GridRowNavigator, LoadingIndicator, EntityIdentityBar, IDENTITY_NAME_MAX_LENGTH, tagTableDocx, documentCount, tagTableLabels, TagDetail, tagDetailLabels, tagNoteLabel, tagKindDescription, glossaryEntries, glossaryDocx, glossaryMarkdown, glossaryFileName } from '../chunk-MNG7N6M2.js';
27
27
  import { MemberAvatarStack } from '../chunk-XNOASBGW.js';
28
+ import { renderSearchSnippet, SearchGroupHeader, isMeaningMatch, extractHighlightTerms, passesRelevanceCutoff, groupSearchResults, SearchPillToggle, SearchPagination, SearchPageFrame, SearchPageHeader, ApplyLabelDialog, SearchPageBody, SearchToolbarRow, SearchBar, SearchControlsBar, RelevanceThreshold, SearchTotalsHeadline, ApplyLabelButton, getSearchMatchHint, SearchResultsMain, SearchResultsInner, SearchErrorBanner, SearchResultList, SearchGroupCard, SearchNoResults, SearchEmptyState } from '../chunk-G4IYES7G.js';
28
29
  import { UsageMeter } from '../chunk-66TA5IBN.js';
29
30
  import '../chunk-UJE32SST.js';
30
31
  import '../chunk-NHZK64TQ.js';
31
- import { renderSearchSnippet, SearchGroupHeader, isMeaningMatch, extractHighlightTerms, passesRelevanceCutoff, groupSearchResults, SearchPillToggle, SearchPagination, SearchPageFrame, SearchPageHeader, ApplyLabelDialog, SearchPageBody, SearchToolbarRow, SearchBar, SearchControlsBar, RelevanceThreshold, SearchTotalsHeadline, ApplyLabelButton, getSearchMatchHint, SearchResultsMain, SearchResultsInner, SearchErrorBanner, SearchResultList, SearchGroupCard, SearchNoResults, SearchEmptyState } from '../chunk-G4IYES7G.js';
32
32
  import { PlainMailBodyEditor, MemberNameCell, MemberMutedText, MemberRoleCell, MemberListLoading, MemberBulkActions, buildMailFolderTree, MailFolderTree, draftMailCompose, replyMailCompose, emptyMailCompose, MailGrid, MailMessageView, MailDocumentPicker, parseMailRecipients } from '../chunk-SZSJGZSO.js';
33
33
  import '../chunk-NBV56SGU.js';
34
34
  import '../chunk-SDOEHEJP.js';
@@ -73,13 +73,13 @@ import { useBlocksEnvironment, useRequestedSettingsSection, BlocksProvider } fro
73
73
  import { ModalScaffold, ModalSection } from '../chunk-TX2ZICS7.js';
74
74
  import { fmtDate, initials } from '../chunk-OXF2IFXG.js';
75
75
  import '../chunk-VUNV25KB.js';
76
- import { lazy, memo, useState, useEffect, useCallback, useMemo, useRef, Suspense, useSyncExternalStore, useLayoutEffect, useId } from 'react';
76
+ import { lazy, memo, useState, useEffect, useCallback, createContext, useMemo, forwardRef, useRef, useLayoutEffect, useImperativeHandle, useContext, Suspense, useSyncExternalStore, useId } from 'react';
77
77
  import { QueryClient, QueryClientProvider, useQueryClient, useQuery } from '@tanstack/react-query';
78
- import { HtmlIcon, useArbiSessionState, ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, isWorkspaceNotificationType, useArbi, useNotifications, useDeleteNotifications, useMarkNotificationsRead, useWorkspaces, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, useSkills, useSetSkillVisibility, useDeleteSkill, useSkillSource, useAgentModels, useMcpTools, useAgentConfig, useAgentWorkspaces, useCreateAgent, useSaveAgentConfig, useGrantAgentAccess, useRevokeAgentAccess, useGenerateAgentAvatar, useWorkflowRuns, useConversationActivity, useAgents, ArtifactCanvas, useArbiChat, useDocuments, useTagKindSchema, useTags, useCreateTag, useSetTagValues, useUpdateTagKindSchema, useFacts, useDeleteTag, useExtractEntities, useShellWorkspaces, ArbiBotHeadIcon, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useSignOut, useDeleteAccount, useDeleteAccountFlow, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailPolicy, useMailAccounts, useUserSettings, useUpdateUserSettings, useAvailableModels, useConnectorVendorVisibility, useConnectorCatalog, useMyConnectors, useDisconnectConnector, useSlackConnection, useDisconnectSlack, isMailboxSyncVendor, isSharePointVendor, useUpdateWorkspace, useAddWorkspaceMembers, useRemoveWorkspaceMembers, useSetWorkspaceMemberRoles, toShellWorkspace, useEnableMailboxSync, useMailboxFolders, useEnableDriveSync, useDriveFolderChildren, connectorErrorStatus, useProjectInvoices, useProjects, useUpdateProjectFields, useUserUsageToday, formatGbp, useChargeWatch, useAddonQuote, STILL_WORKING, useProjectBillingPortal, useIsSlow, useProducts, useCreateProject, useDeleteProject, useRefreshProject, useCanCreateProjects, useDeleteWorkspaces, PROJECT_CREATION_DENIED_REASON, MAX_RETRIEVE_DOC_IDS, useSemanticSearch, useAssignDocTags, useArbiWebSocket, useWorkNotifications, describeServerNotification, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailMessage, useMailCache, MailOfflineUnavailableError, accountPath, useCalendarEvents, useCalendars, useFetchCalendarEvent, useCalendarSettings, useCalendarOverlays, useCalendarOutbox, useCalendarAccounts, useSyncAccountsOnOpen, useResumeCalendarAccountConnect, useCreateEvent, useUpdateEvent, useDeleteEvent, useMoveEvent, useRespondToEvent, useCreateCalendar, useUpdateCalendar, useDeleteCalendar, useImportCalendar, useSaveCalendarSettings, useRefreshCalendar, useCalendarEvent, useMailAttachmentText, useCalendarEventByUid, getPublicBookingPage, bookPublicSlot, PublicBookingError, useAgentSession, useStartAgentSession, useCloseAgentSession, isQueuedCalendarWrite, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, connectorReturnTo, ArbiWebSocketProvider, isDeploymentUserNotification, getWorkspaceActionText, getWorkspaceSentPhrase, BOX_SEAT_REFUSED, BOX_SEAT_REFUSED_TITLE, documentActivityPhrase, isSignatureNotificationType, inputRequestTitle, WORK_NOTIFICATION_LABELS, documentDisplayName, getWorkspaceKind, formatRelativeTime, useCreateWork, useActOnStep, useConversationThreads, useUpdateTag, usePassageQuotes, useUpdateFact, useDeleteFact, useCreateFact, ProviderIcon, formatGbpPerMillionTokens, useDriveFolderDocCount, useChangeProjectPlan, useCancelProjectSubscription, usePreviewProjectPlanChange, useAddProjectAddonBlock, useProjectUsage, usePlanQuote, useCreateProjectCheckout, useInvalidateProjects, fileAttachment, useMailProjects, useSaveMailDraft, useSendMail, useMailDraftReplaced, useCalendarInvitations, useDecideCounter, useSearchContacts, usePublishCalendar, useCalendarShares, useShareCalendar, useUnshareCalendar, useSubscribeCalendar, useHolidayCountries, useAddHolidayCalendar, cancelPublicBooking, AiAnswer, signatureNotificationPhrase, signatureRequestMeta, getNotificationTypeMeta, useMailDraftEditor, useFreeBusy, useRooms, useFindTime, useCalendarSupport, useStartCalendarAccountOAuth, useConnectOAuthAccount, useBookingPages, useDeleteBookingPage, SHAREPOINT_VENDORS, useUpdateProjectNextPeriodAddons, useUpdateProjectAddonSettings, useSyncCalendarAccount, useUpdateCalendarAccount, useDisconnectCalendarAccount, ACCOUNT_VENDOR, useCalDavProfiles, useConnectCalDavAccount, useSaveBookingPage, useCreateRoom } from '@arbidocs/react';
78
+ import { HtmlIcon, useMailPolicy, useProcessingTasks, useUpdateDocuments, useDeleteDocuments, useRenameFolder, useDownloadDocuments, useTags, useDocumentThumbnails, useArbiSessionState, useProcessingTask, getDocumentSubject, useWorkspaces, ArbiProvider, useConversations, useRunConversation, useWorkspaceActivity, useWorkspaceUsers, isWorkspaceNotificationType, useArbi, useNotifications, useDeleteNotifications, useMarkNotificationsRead, useWorkspaceNotificationLevel, useFollowThread, useUploadSkillFiles, useSkills, useSetSkillVisibility, useDeleteSkill, useSkillSource, useAgentModels, useMcpTools, useAgentConfig, useAgentWorkspaces, useCreateAgent, useSaveAgentConfig, useGrantAgentAccess, useRevokeAgentAccess, useGenerateAgentAvatar, useWorkflowRuns, useConversationActivity, useAgents, ArtifactCanvas, useArbiChat, useDocuments, useTagKindSchema, useCreateTag, useSetTagValues, useUpdateTagKindSchema, useFacts, useDeleteTag, useExtractEntities, useShellWorkspaces, ArbiBotHeadIcon, useProfile, useUpdateProfile, useRequestEmailChange, useConfirmEmailChange, useNameEditor, useAvatarCrop, useEmailChange, ImageCropModal, useIdentityVerification, usePasswordChange, usePasswordChangeForm, useNotificationPreferences, useSessions, useCurrentSessionId, useTerminateSession, useSignOut, useDeleteAccount, useDeleteAccountFlow, useMailClient, defaultMailAlias, useClaimNativeAlias, useMailOnline, useConnectMailAccount, useDisconnectMailAccount, useMailAccounts, useUserSettings, useUpdateUserSettings, useAvailableModels, useConnectorVendorVisibility, useConnectorCatalog, useMyConnectors, useDisconnectConnector, useSlackConnection, useDisconnectSlack, isMailboxSyncVendor, isSharePointVendor, useUpdateWorkspace, useAddWorkspaceMembers, useRemoveWorkspaceMembers, useSetWorkspaceMemberRoles, toShellWorkspace, useEnableMailboxSync, useMailboxFolders, useEnableDriveSync, useDriveFolderChildren, connectorErrorStatus, useProjectInvoices, useProjects, useUpdateProjectFields, useUserUsageToday, formatGbp, useChargeWatch, useAddonQuote, STILL_WORKING, useProjectBillingPortal, useIsSlow, useProducts, useCreateProject, useDeleteProject, useRefreshProject, useCanCreateProjects, useDeleteWorkspaces, PROJECT_CREATION_DENIED_REASON, MAX_RETRIEVE_DOC_IDS, useSemanticSearch, useAssignDocTags, useArbiWebSocket, useWorkNotifications, describeServerNotification, useMailUnlock, useMailFolders, useMailMessages, useMailDraftCompose, useMailMessage, useMailCache, MailOfflineUnavailableError, accountPath, useCalendarEvents, useCalendars, useFetchCalendarEvent, useCalendarSettings, useCalendarOverlays, useCalendarOutbox, useCalendarAccounts, useSyncAccountsOnOpen, useResumeCalendarAccountConnect, useCreateEvent, useUpdateEvent, useDeleteEvent, useMoveEvent, useRespondToEvent, useCreateCalendar, useUpdateCalendar, useDeleteCalendar, useImportCalendar, useSaveCalendarSettings, useRefreshCalendar, useCalendarEvent, useMailAttachmentText, useCalendarEventByUid, getPublicBookingPage, bookPublicSlot, PublicBookingError, useAgentSession, useStartAgentSession, useCloseAgentSession, isQueuedCalendarWrite, isAliasTakenError, nextAliasSuggestion, aliasTakenMessage, mailJson, connectorReturnTo, ArbiWebSocketProvider, isDeploymentUserNotification, getWorkspaceActionText, getWorkspaceSentPhrase, BOX_SEAT_REFUSED, BOX_SEAT_REFUSED_TITLE, documentActivityPhrase, isSignatureNotificationType, inputRequestTitle, WORK_NOTIFICATION_LABELS, documentDisplayName, getWorkspaceKind, formatRelativeTime, useCreateWork, useActOnStep, useConversationThreads, useUpdateTag, usePassageQuotes, useUpdateFact, useDeleteFact, useCreateFact, ProviderIcon, formatGbpPerMillionTokens, useDriveFolderDocCount, useChangeProjectPlan, useCancelProjectSubscription, usePreviewProjectPlanChange, useAddProjectAddonBlock, useProjectUsage, usePlanQuote, useCreateProjectCheckout, useInvalidateProjects, fileAttachment, useMailProjects, useSaveMailDraft, useSendMail, useMailDraftReplaced, useCalendarInvitations, useDecideCounter, useSearchContacts, usePublishCalendar, useCalendarShares, useShareCalendar, useUnshareCalendar, useSubscribeCalendar, useHolidayCountries, useAddHolidayCalendar, cancelPublicBooking, AiAnswer, signatureNotificationPhrase, signatureRequestMeta, getNotificationTypeMeta, useMailDraftEditor, useFreeBusy, useRooms, useFindTime, useCalendarSupport, useStartCalendarAccountOAuth, useConnectOAuthAccount, useBookingPages, useDeleteBookingPage, SHAREPOINT_VENDORS, useUpdateProjectNextPeriodAddons, useUpdateProjectAddonSettings, useSyncCalendarAccount, useUpdateCalendarAccount, useDisconnectCalendarAccount, ACCOUNT_VENDOR, useCalDavProfiles, useConnectCalDavAccount, useSaveBookingPage, useCreateRoom } from '@arbidocs/react';
79
79
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
80
- import { Badge, Button, Label, Input, cn, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, tagKindNouns, tagKindTitle, isImageDocument, getDocumentTitle, getEntityColor, IconToggle, VisibilityToggle, Switch, getEntityLabel, formatFriendlyDate, ProjectEntity, DialogFooter, EntityIcon, getDocIcon, Tooltip, TooltipTrigger, TooltipContent, DocumentEntity, EntityPill, ShieldIcon, getTagTypeLabel, Popover, PopoverTrigger, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, UserEntity, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, VerifiedToggle, Progress } from '@arbidocs/react/ui';
80
+ import { getFileTypeIcon, DocumentEntity, cn, getDocumentTitle, Badge, VisibilityIcon, formatFriendlyDate, getEntityLabel, Avatar, AvatarImage, getUserDisplayName, AvatarFallback, getUserInitials, getUserAvatarColor, Button, Label, Input, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, tagKindNouns, tagKindTitle, isImageDocument, getEntityColor, IconToggle, VisibilityToggle, Switch, ProjectEntity, DialogFooter, EntityIcon, getDocIcon, Tooltip, TooltipTrigger, TooltipContent, EntityPill, ShieldIcon, getTagTypeLabel, Popover, PopoverTrigger, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, UserEntity, PopoverContent, Tabs, TabsList, TabsTrigger, TabsContent, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, VerifiedToggle, Progress } from '@arbidocs/react/ui';
81
81
  import { useNotify, notify, sharedSearchSession, takePendingSearch } from '@arbidocs/react/shell';
82
- import { Bell, Activity, Info, UserCheck, Trash2, Loader2, Upload, Link2, Search, Plus, ArrowLeft, ExternalLink, Tags, CheckCircle2, Milestone, Monitor, UserCircle, KeyRound, Radio, LogOut, Wifi, RotateCcw, Mail, User, HatGlasses, MessageSquare, PlugZap, FolderInput, SquareStack, Table, FileText, MessagesSquare, Tag, Settings, Video, Printer, CalendarDays, Clock, MapPin, Globe, Sparkles, ArrowUpCircle, Building2, Contact, Scale, NotebookPen, Check, X, Pencil, BookA, ChevronDown, Users, Paperclip, Quote, Download, Send, Inbox, CalendarX, CalendarCheck, Minimize2, Maximize2, Minus, CalendarSearch, DoorOpen, RefreshCw, AlertTriangle } from 'lucide-react';
82
+ import { SquarePen, BookOpen, MessagesSquare, Pencil, Download, Mail, FolderInput, Trash2, Sparkles, Play, ArrowDownToLine, Bell, Activity, Info, UserCheck, Loader2, Upload, Link2, Search, Plus, ArrowLeft, ExternalLink, Tags, CheckCircle2, Milestone, Monitor, UserCircle, KeyRound, Radio, LogOut, Wifi, RotateCcw, User, HatGlasses, MessageSquare, PlugZap, SquareStack, Table, FileText, Tag, Settings, Video, Printer, CalendarDays, Clock, MapPin, Globe, AlertTriangle, FileX, SkipForward, CheckCircle, ArrowUpCircle, Building2, Contact, Scale, NotebookPen, Check, X, BookA, ChevronDown, Users, Paperclip, Quote, Send, Inbox, CalendarX, CalendarCheck, Minimize2, Maximize2, Minus, CalendarSearch, DoorOpen, RefreshCw } from 'lucide-react';
83
83
  import { downloadBlob, useIsOnline, useOptionalDocumentTools, fetchDocumentPdf, DocumentToolsProvider } from '@arbidocs/react/document-tools';
84
84
  import { useStreamedArtifact } from '@arbidocs/react/artifacts';
85
85
  import { FLAT_TAG_KINDS, TAG_KINDS, refusalReason, getChildTags, tagUseCount, flattenTagTree, isFlatKind, isEntityTag, tagsForScope, filterDocumentTags, isLabelTag, getTagDescendantIds, getEligibleParentTags, canTagHaveParent } from '@arbidocs/sdk/browser';
@@ -87,6 +87,8 @@ import { bytesToBase64 } from '@arbidocs/client';
87
87
  import { createPortal } from 'react-dom';
88
88
  import { create } from 'zustand';
89
89
  import { persist, createJSONStorage } from 'zustand/middleware';
90
+ import { useDraggable, useDroppable, useSensors, useSensor, MouseSensor, TouchSensor, pointerWithin, rectIntersection, DndContext, DragOverlay } from '@dnd-kit/core';
91
+ import { useVirtualizer } from '@tanstack/react-virtual';
90
92
 
91
93
  function ConnectionBoundWebSocket({ children }) {
92
94
  const { status } = useConnection();
@@ -14387,7 +14389,7 @@ function BookingPagesPanel({ defaultTimeZone, className }) {
14387
14389
  var selectClass4 = "h-9 w-full rounded-md border border-input bg-background px-2 text-sm text-foreground shadow-sm";
14388
14390
  function CreateRoomForm({ zone, onDone }) {
14389
14391
  const { notify: notify14 } = useBlocksEnvironment();
14390
- const create2 = useCreateRoom();
14392
+ const create3 = useCreateRoom();
14391
14393
  const [name, setName] = useState("");
14392
14394
  const [type, setType] = useState("room");
14393
14395
  const [capacity, setCapacity] = useState("");
@@ -14407,7 +14409,7 @@ function CreateRoomForm({ zone, onDone }) {
14407
14409
  const cap = capacity.trim() ? Number(capacity) : null;
14408
14410
  if (cap !== null && (!Number.isInteger(cap) || cap < 1))
14409
14411
  return setError("Capacity must be a whole number");
14410
- create2.mutate(
14412
+ create3.mutate(
14411
14413
  {
14412
14414
  name: name.trim(),
14413
14415
  type,
@@ -14498,8 +14500,8 @@ function CreateRoomForm({ zone, onDone }) {
14498
14500
  error && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }),
14499
14501
  /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
14500
14502
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onDone, children: "Cancel" }),
14501
- /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: create2.isPending, children: [
14502
- create2.isPending && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 size-4 animate-spin" }),
14503
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: create3.isPending, children: [
14504
+ create3.isPending && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 size-4 animate-spin" }),
14503
14505
  "Create"
14504
14506
  ] })
14505
14507
  ] })
@@ -16815,6 +16817,1152 @@ function ConnectedPublicBooking({ slug, cancelToken }) {
16815
16817
  ] })
16816
16818
  ] });
16817
16819
  }
16820
+ var useFilesStore = create()(
16821
+ persist(
16822
+ (set) => ({
16823
+ pendingFolders: {},
16824
+ addPendingFolder: (workspaceId, path) => set((state) => {
16825
+ const current = state.pendingFolders[workspaceId] ?? [];
16826
+ if (current.includes(path)) return state;
16827
+ return { pendingFolders: { ...state.pendingFolders, [workspaceId]: [...current, path] } };
16828
+ }),
16829
+ removePendingFolder: (workspaceId, path) => set((state) => {
16830
+ const current = state.pendingFolders[workspaceId] ?? [];
16831
+ if (!current.includes(path)) return state;
16832
+ return {
16833
+ pendingFolders: {
16834
+ ...state.pendingFolders,
16835
+ [workspaceId]: current.filter((p) => p !== path)
16836
+ }
16837
+ };
16838
+ })
16839
+ }),
16840
+ {
16841
+ name: "arbi_explorer_pending_folders",
16842
+ storage: createJSONStorage(() => sessionStorage),
16843
+ partialize: (state) => ({ pendingFolders: state.pendingFolders })
16844
+ }
16845
+ )
16846
+ );
16847
+ var NONE = [];
16848
+ function usePendingFolders(workspaceId) {
16849
+ return useFilesStore(
16850
+ (state) => (workspaceId ? state.pendingFolders[workspaceId] : void 0) ?? NONE
16851
+ );
16852
+ }
16853
+ var FilesSupportContext = createContext({});
16854
+ function FilesSupportProvider({
16855
+ value,
16856
+ children
16857
+ }) {
16858
+ return /* @__PURE__ */ jsx(FilesSupportContext.Provider, { value, children });
16859
+ }
16860
+ function useFilesSupport() {
16861
+ return useContext(FilesSupportContext);
16862
+ }
16863
+ var NO_IDS3 = /* @__PURE__ */ new Set();
16864
+ function useOfflineDocumentIds() {
16865
+ const { useOfflineDocumentIds: useHostIds } = useFilesSupport();
16866
+ return useHostIds?.() ?? NO_IDS3;
16867
+ }
16868
+ function useViewingDocumentId() {
16869
+ const { useViewingDocumentId: useHostViewing } = useFilesSupport();
16870
+ return useHostViewing?.() ?? null;
16871
+ }
16872
+ function DocumentSyncGlyph({
16873
+ docExtId,
16874
+ size = 13,
16875
+ className,
16876
+ bgClassName = "bg-muted-foreground"
16877
+ }) {
16878
+ const isAvailableOffline = useOfflineDocumentIds().has(docExtId);
16879
+ if (!isAvailableOffline) return null;
16880
+ return /* @__PURE__ */ jsx(
16881
+ "span",
16882
+ {
16883
+ title: "Available offline (saved on this device)",
16884
+ "aria-label": "Available offline",
16885
+ "data-testid": `offline-available-badge-${docExtId}`,
16886
+ className: cn(
16887
+ "inline-flex items-center justify-center rounded-full text-white ring-1 ring-white",
16888
+ bgClassName,
16889
+ className
16890
+ ),
16891
+ style: { width: size, height: size },
16892
+ children: /* @__PURE__ */ jsx(ArrowDownToLine, { size: Math.round(size * 0.7), strokeWidth: 2.75 })
16893
+ }
16894
+ );
16895
+ }
16896
+ var ACTIVE_STATUSES = /* @__PURE__ */ new Set([
16897
+ "uploading",
16898
+ "queued",
16899
+ "parsing",
16900
+ "encrypting",
16901
+ "indexing",
16902
+ "analysing"
16903
+ ]);
16904
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "skipped", "empty", "low-content"]);
16905
+ var STATUS_LABEL = {
16906
+ uploading: "Uploading",
16907
+ queued: "Queued",
16908
+ parsing: "Parsing",
16909
+ encrypting: "Encrypting",
16910
+ indexing: "Indexing",
16911
+ analysing: "Analysing"
16912
+ };
16913
+ function isActiveDocumentStatus(status) {
16914
+ return ACTIVE_STATUSES.has(status.toLowerCase());
16915
+ }
16916
+ function useDocStatus(doc) {
16917
+ const liveStatus = useProcessingTask(doc.external_id)?.status;
16918
+ const rowStatus = (doc.status ?? "").toLowerCase();
16919
+ if (liveStatus && ACTIVE_STATUSES.has(liveStatus)) return liveStatus;
16920
+ if (TERMINAL_STATUSES.has(rowStatus)) return rowStatus;
16921
+ return liveStatus ?? rowStatus;
16922
+ }
16923
+ function TileStatusBadge({ doc, status }) {
16924
+ let tone = "neutral";
16925
+ let icon2 = null;
16926
+ let title = "";
16927
+ switch (status) {
16928
+ case "completed":
16929
+ tone = "success";
16930
+ icon2 = /* @__PURE__ */ jsx(CheckCircle, { className: "size-4 text-green-text", strokeWidth: 2.75 });
16931
+ title = "Ready";
16932
+ break;
16933
+ case "failed":
16934
+ tone = "danger";
16935
+ icon2 = /* @__PURE__ */ jsx(
16936
+ FailedStageIcon,
16937
+ {
16938
+ stage: doc.failed_stage ?? null,
16939
+ errorMessage: doc.error_message,
16940
+ className: "size-4 text-red-text"
16941
+ }
16942
+ );
16943
+ title = "Failed";
16944
+ break;
16945
+ case "skipped":
16946
+ icon2 = /* @__PURE__ */ jsx(SkipForward, { className: "size-4 text-muted-foreground", strokeWidth: 2.75 });
16947
+ title = "Skipped";
16948
+ break;
16949
+ case "empty":
16950
+ tone = "warning";
16951
+ icon2 = /* @__PURE__ */ jsx(FileX, { className: "size-4 text-yellow-text", strokeWidth: 2.75 });
16952
+ title = "Empty \u2014 no extractable content";
16953
+ break;
16954
+ case "low-content":
16955
+ tone = "warning";
16956
+ icon2 = /* @__PURE__ */ jsx(AlertTriangle, { className: "size-4 text-yellow-text", strokeWidth: 2.75 });
16957
+ title = "Low content \u2014 minimal extractable content";
16958
+ break;
16959
+ default:
16960
+ return null;
16961
+ }
16962
+ return /* @__PURE__ */ jsx(
16963
+ StatusPill,
16964
+ {
16965
+ tone,
16966
+ icon: icon2,
16967
+ title,
16968
+ testId: `explorer-file-status-${doc.external_id}`
16969
+ }
16970
+ );
16971
+ }
16972
+ function TileVisibilityBadge({ doc }) {
16973
+ const shared = !!doc.shared;
16974
+ return /* @__PURE__ */ jsx(
16975
+ StatusPill,
16976
+ {
16977
+ icon: /* @__PURE__ */ jsx(VisibilityIcon, { isShared: shared, className: "size-3.5", "aria-hidden": true }),
16978
+ label: shared ? getEntityLabel("WORKSPACE") : "Only you",
16979
+ title: visibilityAudience(shared),
16980
+ testId: `explorer-file-visibility-${doc.external_id}`
16981
+ }
16982
+ );
16983
+ }
16984
+ function MiniUserAvatar({ user }) {
16985
+ return /* @__PURE__ */ jsxs(Avatar, { className: "h-4 w-4 border border-white", children: [
16986
+ user.picture && /* @__PURE__ */ jsx(AvatarImage, { src: user.picture, alt: getUserDisplayName(user) }),
16987
+ /* @__PURE__ */ jsx(
16988
+ AvatarFallback,
16989
+ {
16990
+ className: cn(
16991
+ "text-xs font-semibold leading-none text-white",
16992
+ getUserAvatarColor(user.external_id)
16993
+ ),
16994
+ children: getUserInitials(user)
16995
+ }
16996
+ )
16997
+ ] });
16998
+ }
16999
+ function FileTooltipContent({
17000
+ doc,
17001
+ modifiedBy,
17002
+ tagNameById
17003
+ }) {
17004
+ const labels = (doc.doctags ?? []).map((dt) => tagNameById.get(dt.tag_ext_id)).filter((name) => !!name);
17005
+ return /* @__PURE__ */ jsx(
17006
+ ExplorerFileTooltip,
17007
+ {
17008
+ title: getDocumentTitle(doc, doc.file_name ?? ""),
17009
+ summary: getDocumentSubject(doc),
17010
+ modifiedLabel: doc.updated_at ? formatFriendlyDate(doc.updated_at, false) : null,
17011
+ modifiedByAvatar: modifiedBy && /* @__PURE__ */ jsx(MiniUserAvatar, { user: modifiedBy }),
17012
+ shared: !!doc.shared,
17013
+ visibilityIcon: /* @__PURE__ */ jsx(VisibilityIcon, { isShared: !!doc.shared, className: "h-3 w-3" }),
17014
+ labels,
17015
+ uiLabels: { shared: visibilityAudience(true), private: visibilityAudience(false) }
17016
+ }
17017
+ );
17018
+ }
17019
+ function startMenuItem(items) {
17020
+ if (items.length === 0) return [];
17021
+ return [
17022
+ {
17023
+ id: "start",
17024
+ label: "Start\u2026",
17025
+ icon: Sparkles,
17026
+ testId: "explorer-start-menu",
17027
+ onSelect: () => {
17028
+ },
17029
+ submenu: items.map((item) => ({
17030
+ id: item.id,
17031
+ label: item.title,
17032
+ icon: Play,
17033
+ onSelect: item.run,
17034
+ testId: `explorer-start-${item.id}`
17035
+ }))
17036
+ }
17037
+ ];
17038
+ }
17039
+ var FileTile = memo(function FileTile2({
17040
+ doc,
17041
+ density,
17042
+ selected,
17043
+ dragging,
17044
+ dndActive,
17045
+ renaming,
17046
+ canDelete,
17047
+ thumbnailUrl,
17048
+ modifiedBy,
17049
+ tagNameById,
17050
+ onSelect,
17051
+ onToggle,
17052
+ onOpen,
17053
+ onReview,
17054
+ onStartRename,
17055
+ onCommitRename,
17056
+ onCancelRename,
17057
+ onDownload,
17058
+ onMove,
17059
+ onDelete
17060
+ }) {
17061
+ const { attributes, listeners: listeners2, setNodeRef } = useDraggable({
17062
+ id: `doc:${doc.external_id}`,
17063
+ data: { type: "doc", docId: doc.external_id }
17064
+ });
17065
+ const { notify: notify14 } = useBlocksEnvironment();
17066
+ const { startItemsForFile, openDocumentChat, emailDocument } = useFilesSupport();
17067
+ const status = useDocStatus(doc);
17068
+ const isProcessing = ACTIVE_STATUSES.has(status);
17069
+ const isOffline = useOfflineDocumentIds().has(doc.external_id);
17070
+ const isViewing = useViewingDocumentId() === doc.external_id;
17071
+ const showStatusMarker = !isProcessing && TERMINAL_STATUSES.has(status);
17072
+ const Icon = getFileTypeIcon(doc.file_name, { mime: doc.file_type });
17073
+ const { data: mailPolicy } = useMailPolicy();
17074
+ const shareAsEmail = async () => {
17075
+ try {
17076
+ await emailDocument?.(doc);
17077
+ } catch (error) {
17078
+ notify14(error instanceof Error ? error.message : "Could not attach document", "error");
17079
+ }
17080
+ };
17081
+ const startItems = startItemsForFile?.(doc);
17082
+ const menuItems = [
17083
+ { id: "open", label: "Open", icon: SquarePen, onSelect: () => onOpen(doc) },
17084
+ ...onReview ? [{ id: "review", label: "Open in Review", icon: BookOpen, onSelect: () => onReview(doc) }] : [],
17085
+ ...openDocumentChat ? [
17086
+ {
17087
+ id: "chat",
17088
+ label: "Document chat",
17089
+ icon: MessagesSquare,
17090
+ onSelect: () => openDocumentChat(doc.external_id)
17091
+ }
17092
+ ] : [],
17093
+ ...startMenuItem(startItems ?? []),
17094
+ {
17095
+ id: "rename",
17096
+ label: "Rename",
17097
+ icon: Pencil,
17098
+ onSelect: () => onStartRename(doc),
17099
+ separatorBefore: true
17100
+ },
17101
+ { id: "download", label: "Download", icon: Download, onSelect: () => onDownload(doc) },
17102
+ ...emailDocument && mailPolicy?.enabled ? [
17103
+ {
17104
+ id: "email",
17105
+ label: "Send as email attachment",
17106
+ icon: Mail,
17107
+ onSelect: () => {
17108
+ void shareAsEmail();
17109
+ }
17110
+ }
17111
+ ] : [],
17112
+ ...onMove ? [{ id: "move", label: "Move to\u2026", icon: FolderInput, onSelect: () => onMove(doc) }] : [],
17113
+ ...canDelete ? [
17114
+ {
17115
+ id: "delete",
17116
+ label: "Delete",
17117
+ icon: Trash2,
17118
+ onSelect: () => onDelete(doc),
17119
+ destructive: true,
17120
+ separatorBefore: true
17121
+ }
17122
+ ] : []
17123
+ ];
17124
+ const tooltip = renaming || dndActive ? void 0 : /* @__PURE__ */ jsx(FileTooltipContent, { doc, modifiedBy, tagNameById });
17125
+ return /* @__PURE__ */ jsx(
17126
+ ExplorerFileTile,
17127
+ {
17128
+ id: doc.external_id,
17129
+ title: getDocumentTitle(doc, doc.file_name ?? ""),
17130
+ density,
17131
+ selected,
17132
+ dragging,
17133
+ renaming,
17134
+ thumbnailUrl,
17135
+ renderFileIcon: (size) => /* @__PURE__ */ jsx(Icon, { size, className: "drop-shadow-sm" }),
17136
+ progress: isProcessing ? { label: STATUS_LABEL[status] ?? status, queued: status === "queued" } : void 0,
17137
+ statusMarker: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
17138
+ /* @__PURE__ */ jsx(TileVisibilityBadge, { doc }),
17139
+ showStatusMarker && /* @__PURE__ */ jsx(TileStatusBadge, { doc, status })
17140
+ ] }),
17141
+ offlineMarker: isOffline ? /* @__PURE__ */ jsx(DocumentSyncGlyph, { docExtId: doc.external_id, size: 24, bgClassName: "bg-primary" }) : void 0,
17142
+ entity: /* @__PURE__ */ jsx(
17143
+ DocumentEntity,
17144
+ {
17145
+ doc,
17146
+ labelMaxWidth: "max-w-full",
17147
+ noClamp: true,
17148
+ labelClassName: "whitespace-normal line-clamp-2 break-words",
17149
+ viewing: isViewing,
17150
+ tooltipContent: /* @__PURE__ */ jsx(FileTooltipContent, { doc, modifiedBy, tagNameById }),
17151
+ className: cn(
17152
+ "w-full min-w-0 max-w-full bg-background/90 backdrop-blur-sm",
17153
+ density === "list" ? "text-xs" : void 0
17154
+ ),
17155
+ onClick: (e) => {
17156
+ e.stopPropagation();
17157
+ onOpen(doc);
17158
+ }
17159
+ }
17160
+ ),
17161
+ tooltip,
17162
+ menuItems,
17163
+ rootRef: setNodeRef,
17164
+ rootProps: { ...listeners2, ...attributes },
17165
+ onOpen: () => onOpen(doc),
17166
+ onSelect: (e) => onSelect(doc, e),
17167
+ onToggle: (e) => onToggle(doc, e),
17168
+ onCommitRename: (name) => onCommitRename(doc, name),
17169
+ onCancelRename
17170
+ }
17171
+ );
17172
+ });
17173
+ var FolderTile = memo(function FolderTile2({
17174
+ folder,
17175
+ density,
17176
+ editing,
17177
+ selected,
17178
+ partiallySelected,
17179
+ documents,
17180
+ onOpen,
17181
+ onToggleSelect,
17182
+ onCommitName,
17183
+ onCancelName,
17184
+ onStartRename,
17185
+ onDownload,
17186
+ onMove,
17187
+ onDelete
17188
+ }) {
17189
+ const { setNodeRef, isOver } = useDroppable({
17190
+ id: `folder:${folder.path.join("/")}`,
17191
+ data: { type: "folder", path: folder.path }
17192
+ });
17193
+ const { startItemsForFolder } = useFilesSupport();
17194
+ const tasks = useProcessingTasks();
17195
+ const processingCount = useMemo(
17196
+ () => documents.filter(
17197
+ (doc) => isActiveDocumentStatus(tasks[doc.external_id]?.status ?? doc.status ?? "")
17198
+ ).length,
17199
+ [documents, tasks]
17200
+ );
17201
+ const isEmpty = folder.count === 0;
17202
+ const iconSize = density === "list" ? 34 : 48;
17203
+ const menuItems = [
17204
+ ...onStartRename ? [{ id: "rename", label: "Rename", icon: Pencil, onSelect: () => onStartRename(folder) }] : [],
17205
+ ...!isEmpty ? startMenuItem(startItemsForFolder?.(folder.path.join("/")) ?? []) : [],
17206
+ ...onDownload && !isEmpty ? [{ id: "download", label: "Download", icon: Download, onSelect: () => onDownload(folder) }] : [],
17207
+ ...onMove && !isEmpty ? [{ id: "move", label: "Move to\u2026", icon: FolderInput, onSelect: () => onMove(folder) }] : [],
17208
+ ...onDelete ? [
17209
+ {
17210
+ id: "delete",
17211
+ label: "Delete",
17212
+ icon: Trash2,
17213
+ onSelect: () => onDelete(folder),
17214
+ destructive: true,
17215
+ separatorBefore: true
17216
+ }
17217
+ ] : []
17218
+ ];
17219
+ const displayName = folder.path.length === 1 ? formatFolderSegment(folder.name) : folder.name;
17220
+ const providerIcon = folder.path.length === 1 && hasFolderProviderIcon(folder.name) ? /* @__PURE__ */ jsx(
17221
+ "span",
17222
+ {
17223
+ className: "flex items-center justify-center drop-shadow-sm",
17224
+ style: { width: iconSize, height: iconSize },
17225
+ children: /* @__PURE__ */ jsx(FolderProviderIcon, { folderPath: folder.name, className: "h-full w-full" })
17226
+ }
17227
+ ) : void 0;
17228
+ return /* @__PURE__ */ jsx(
17229
+ ExplorerFolderTile,
17230
+ {
17231
+ name: folder.name,
17232
+ displayName: displayName === "" ? null : displayName,
17233
+ path: folder.path,
17234
+ count: folder.count,
17235
+ density,
17236
+ editing,
17237
+ selected,
17238
+ partiallySelected,
17239
+ processingCount,
17240
+ isOver,
17241
+ icon: providerIcon,
17242
+ menuItems,
17243
+ rootRef: setNodeRef,
17244
+ onOpen: () => onOpen(folder),
17245
+ onToggleSelect: onToggleSelect ? (e) => onToggleSelect(folder, e) : void 0,
17246
+ onCommitName: (name) => onCommitName?.(folder, name),
17247
+ onCancelName
17248
+ }
17249
+ );
17250
+ });
17251
+ var GRID_GAP = 12;
17252
+ var SCROLL_PADDING_X = 32;
17253
+ var PAGE_ASPECT = 1.3;
17254
+ var TILE_MIN = 88;
17255
+ var TILE_MAX = 232;
17256
+ var TILE_DEFAULT = 200;
17257
+ var COMPACT_BELOW = 132;
17258
+ var NO_DOCUMENTS = [];
17259
+ function useFilesWorkspace() {
17260
+ const { workspaceId, userId, live } = useConnectedWorkspace();
17261
+ const { members, role } = useConnectedWorkspaceRole();
17262
+ const { data: workspaces } = useWorkspaces({ enabled: live });
17263
+ const workspaceName = workspaces?.find((w) => w.external_id === workspaceId)?.name;
17264
+ return {
17265
+ workspaceId: workspaceId ?? "",
17266
+ workspaceName,
17267
+ userId,
17268
+ role: role ?? null,
17269
+ members
17270
+ };
17271
+ }
17272
+ var ConnectedFilesExplorer = forwardRef(function ConnectedFilesExplorer2({
17273
+ documents,
17274
+ isLoading,
17275
+ canUpload,
17276
+ selectedIds,
17277
+ onSelectionChange,
17278
+ onOrderedFilesChange,
17279
+ onOpenDocument,
17280
+ onUploadFiles,
17281
+ folderPath,
17282
+ onFolderPathChange,
17283
+ search = "",
17284
+ sort,
17285
+ sortReversed,
17286
+ tileSize,
17287
+ onMoveSelected,
17288
+ onFolderMove,
17289
+ onOpenInReview,
17290
+ emptyWorkspace
17291
+ }, ref) {
17292
+ const { confirm, notify: notify14 } = useBlocksEnvironment();
17293
+ const currentPath = folderPath;
17294
+ const density = tileSize < COMPACT_BELOW ? "list" : "grid";
17295
+ const [containerWidth, setContainerWidth] = useState(0);
17296
+ const [activeDragId, setActiveDragId] = useState(null);
17297
+ const [isFileDragOver, setIsFileDragOver] = useState(false);
17298
+ const [renamingId, setRenamingId] = useState(null);
17299
+ const scrollRef = useRef(null);
17300
+ const anchorRef = useRef(null);
17301
+ const dragIdsRef = useRef([]);
17302
+ const fileDragDepth = useRef(0);
17303
+ const marqueeOverlayRef = useRef(null);
17304
+ const marqueeRef = useRef(
17305
+ null
17306
+ );
17307
+ const didMarqueeRef = useRef(false);
17308
+ const updateDocument = useUpdateDocuments();
17309
+ const deleteDocuments = useDeleteDocuments();
17310
+ const renameFolder = useRenameFolder();
17311
+ const { workspaceId: wsId, workspaceName, userId, role, members } = useFilesWorkspace();
17312
+ const { downloadDocuments, downloadDocument } = useDownloadDocuments({ workspaceName });
17313
+ const { data: workspaceTags } = useTags();
17314
+ const userById = useMemo(() => {
17315
+ const map = /* @__PURE__ */ new Map();
17316
+ for (const member of members) map.set(member.user.external_id, member.user);
17317
+ return map;
17318
+ }, [members]);
17319
+ const tagNameById = useMemo(() => {
17320
+ const map = /* @__PURE__ */ new Map();
17321
+ for (const tag of workspaceTags ?? []) map.set(tag.external_id, tag.name);
17322
+ return map;
17323
+ }, [workspaceTags]);
17324
+ const pendingFolders = usePendingFolders(wsId);
17325
+ const addPendingFolder = useFilesStore((state) => state.addPendingFolder);
17326
+ const removePendingFolder = useFilesStore((state) => state.removePendingFolder);
17327
+ const [editingFolderPath, setEditingFolderPath] = useState(null);
17328
+ const isSearching = search.trim().length > 0;
17329
+ const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);
17330
+ const selectedIdsRef = useRef(selectedIds);
17331
+ selectedIdsRef.current = selectedIds;
17332
+ const { folders, files } = useMemo(() => {
17333
+ const result = isSearching ? {
17334
+ folders: [],
17335
+ files: searchExplorerDocuments(documents, search, sort)
17336
+ } : buildExplorerEntries(documents, currentPath, sort, [...pendingFolders]);
17337
+ if (sortReversed) result.files = [...result.files].reverse();
17338
+ return result;
17339
+ }, [documents, currentPath, sort, isSearching, search, sortReversed, pendingFolders]);
17340
+ const entries = useMemo(
17341
+ () => [...folders, ...files.map((doc) => ({ kind: "file", doc }))],
17342
+ [folders, files]
17343
+ );
17344
+ const orderedFileIds = useMemo(() => files.map((d) => d.external_id), [files]);
17345
+ const folderDocs = useMemo(() => {
17346
+ const map = /* @__PURE__ */ new Map();
17347
+ for (const f of folders) map.set(f.path.join("/"), collectFolderDocs(documents, f.path));
17348
+ return map;
17349
+ }, [folders, documents]);
17350
+ const folderSelection = useMemo(() => {
17351
+ const map = /* @__PURE__ */ new Map();
17352
+ for (const [key, docs] of folderDocs) {
17353
+ const sel = docs.reduce((n, d) => selectedSet.has(d.external_id) ? n + 1 : n, 0);
17354
+ map.set(key, { all: docs.length > 0 && sel === docs.length, some: sel > 0 });
17355
+ }
17356
+ return map;
17357
+ }, [folderDocs, selectedSet]);
17358
+ const contentWidth = Math.max(0, containerWidth - SCROLL_PADDING_X);
17359
+ const columnCount = Math.max(
17360
+ 1,
17361
+ Math.floor((contentWidth + GRID_GAP) / (tileSize + GRID_GAP)) || 1
17362
+ );
17363
+ const colWidth = contentWidth > 0 ? (contentWidth - (columnCount - 1) * GRID_GAP) / columnCount : tileSize;
17364
+ const tileHeight = Math.max(120, Math.round(colWidth * PAGE_ASPECT));
17365
+ const rowHeight = tileHeight + GRID_GAP;
17366
+ const rowCount = Math.ceil(entries.length / columnCount);
17367
+ const rowVirtualizer = useVirtualizer({
17368
+ count: rowCount,
17369
+ getScrollElement: () => scrollRef.current,
17370
+ estimateSize: () => rowHeight,
17371
+ overscan: 3
17372
+ });
17373
+ const virtualRows = rowVirtualizer.getVirtualItems();
17374
+ const firstRow = virtualRows.length > 0 ? virtualRows[0].index : 0;
17375
+ const lastRow = virtualRows.length > 0 ? virtualRows[virtualRows.length - 1].index : -1;
17376
+ const visibleFileIds = useMemo(() => {
17377
+ const ids = [];
17378
+ for (let r = firstRow; r <= lastRow; r++) {
17379
+ const start = r * columnCount;
17380
+ for (let c = 0; c < columnCount; c++) {
17381
+ const entry = entries[start + c];
17382
+ if (entry?.kind === "file") ids.push(entry.doc.external_id);
17383
+ }
17384
+ }
17385
+ return ids;
17386
+ }, [firstRow, lastRow, columnCount, entries]);
17387
+ const thumbnailsById = useDocumentThumbnails(visibleFileIds);
17388
+ useEffect(() => {
17389
+ rowVirtualizer.measure();
17390
+ }, [rowVirtualizer, columnCount, rowHeight, entries.length]);
17391
+ const isWholeWorkspaceEmpty = !isLoading && documents.length === 0 && canUpload && emptyWorkspace !== void 0;
17392
+ useLayoutEffect(() => {
17393
+ const el = scrollRef.current;
17394
+ if (!el) return;
17395
+ const apply = () => {
17396
+ const next = Math.round(el.clientWidth);
17397
+ setContainerWidth((prev) => prev === next ? prev : next);
17398
+ };
17399
+ apply();
17400
+ const ro = new ResizeObserver(apply);
17401
+ ro.observe(el);
17402
+ return () => ro.disconnect();
17403
+ }, [isWholeWorkspaceEmpty]);
17404
+ useEffect(() => {
17405
+ anchorRef.current = null;
17406
+ if (scrollRef.current) scrollRef.current.scrollTop = 0;
17407
+ }, [currentPath, search]);
17408
+ useEffect(() => {
17409
+ const MARQUEE_THRESHOLD = 5;
17410
+ let rafId = 0;
17411
+ let cur = null;
17412
+ const paint = () => {
17413
+ rafId = 0;
17414
+ const el = marqueeOverlayRef.current;
17415
+ if (!el || !cur) return;
17416
+ el.style.display = "block";
17417
+ el.style.left = `${cur.left}px`;
17418
+ el.style.top = `${cur.top}px`;
17419
+ el.style.width = `${cur.right - cur.left}px`;
17420
+ el.style.height = `${cur.bottom - cur.top}px`;
17421
+ };
17422
+ const onMove = (e) => {
17423
+ const m = marqueeRef.current;
17424
+ if (!m) return;
17425
+ if (!didMarqueeRef.current && Math.hypot(e.clientX - m.x, e.clientY - m.y) < MARQUEE_THRESHOLD)
17426
+ return;
17427
+ didMarqueeRef.current = true;
17428
+ const b = scrollRef.current?.getBoundingClientRect();
17429
+ const px = b ? Math.max(b.left, Math.min(e.clientX, b.right)) : e.clientX;
17430
+ const py = b ? Math.max(b.top, Math.min(e.clientY, b.bottom)) : e.clientY;
17431
+ cur = {
17432
+ left: Math.min(m.x, px),
17433
+ top: Math.min(m.y, py),
17434
+ right: Math.max(m.x, px),
17435
+ bottom: Math.max(m.y, py)
17436
+ };
17437
+ if (!rafId) rafId = requestAnimationFrame(paint);
17438
+ };
17439
+ const onUp = () => {
17440
+ const m = marqueeRef.current;
17441
+ if (!m) return;
17442
+ marqueeRef.current = null;
17443
+ if (rafId) cancelAnimationFrame(rafId);
17444
+ rafId = 0;
17445
+ const el = marqueeOverlayRef.current;
17446
+ if (el) el.style.display = "none";
17447
+ if (!didMarqueeRef.current || !cur) return;
17448
+ const box = cur;
17449
+ const hit = [];
17450
+ scrollRef.current?.querySelectorAll("[data-doc-id]").forEach((node) => {
17451
+ const r = node.getBoundingClientRect();
17452
+ if (r.left < box.right && r.right > box.left && r.top < box.bottom && r.bottom > box.top) {
17453
+ const id = node.getAttribute("data-doc-id");
17454
+ if (id) hit.push(id);
17455
+ }
17456
+ });
17457
+ onSelectionChange(m.additive ? Array.from(/* @__PURE__ */ new Set([...m.base, ...hit])) : hit);
17458
+ };
17459
+ window.addEventListener("pointermove", onMove);
17460
+ window.addEventListener("pointerup", onUp);
17461
+ return () => {
17462
+ window.removeEventListener("pointermove", onMove);
17463
+ window.removeEventListener("pointerup", onUp);
17464
+ if (rafId) cancelAnimationFrame(rafId);
17465
+ };
17466
+ }, [onSelectionChange]);
17467
+ const handleScrollPointerDown = useCallback((e) => {
17468
+ if (e.button !== 0) return;
17469
+ const el = e.target;
17470
+ if (el.closest(
17471
+ '[data-testid="explorer-file"],[data-testid="explorer-folder"],button,input,select,a,[role="button"]'
17472
+ )) {
17473
+ return;
17474
+ }
17475
+ const additive = e.shiftKey || e.metaKey || e.ctrlKey;
17476
+ marqueeRef.current = {
17477
+ x: e.clientX,
17478
+ y: e.clientY,
17479
+ additive,
17480
+ base: additive ? selectedIdsRef.current : []
17481
+ };
17482
+ didMarqueeRef.current = false;
17483
+ }, []);
17484
+ const openFolder = useCallback(
17485
+ (folder) => onFolderPathChange(folder.path),
17486
+ [onFolderPathChange]
17487
+ );
17488
+ const handleNewFolder = useCallback(() => {
17489
+ if (!wsId) return;
17490
+ const siblings = new Set(folders.map((f) => f.name.toLowerCase()));
17491
+ const base = "New folder";
17492
+ let name = base;
17493
+ let i = 2;
17494
+ while (siblings.has(name.toLowerCase())) name = `${base} ${i++}`;
17495
+ const path = joinFolderPath([...currentPath, name]);
17496
+ if (!path) return;
17497
+ addPendingFolder(wsId, path);
17498
+ setEditingFolderPath(path);
17499
+ }, [wsId, folders, currentPath, addPendingFolder]);
17500
+ const lastOrderedRef = useRef([]);
17501
+ useEffect(() => {
17502
+ const prev = lastOrderedRef.current;
17503
+ if (prev.length === orderedFileIds.length && prev.every((id, i) => id === orderedFileIds[i])) {
17504
+ return;
17505
+ }
17506
+ lastOrderedRef.current = orderedFileIds;
17507
+ onOrderedFilesChange?.(orderedFileIds);
17508
+ }, [onOrderedFilesChange, orderedFileIds]);
17509
+ const scrollToDocument = useCallback(
17510
+ (docId) => {
17511
+ const index = entries.findIndex((e) => e.kind === "file" && e.doc.external_id === docId);
17512
+ if (index < 0) return;
17513
+ rowVirtualizer.scrollToIndex(Math.floor(index / columnCount), { align: "center" });
17514
+ },
17515
+ [entries, columnCount, rowVirtualizer]
17516
+ );
17517
+ useImperativeHandle(ref, () => ({ triggerNewFolder: handleNewFolder, scrollToDocument }), [
17518
+ handleNewFolder,
17519
+ scrollToDocument
17520
+ ]);
17521
+ const commitFolderName = useCallback(
17522
+ async (folder, rawName) => {
17523
+ setEditingFolderPath(null);
17524
+ const name = rawName.trim();
17525
+ if (!wsId || !name || name === folder.name) return;
17526
+ const siblings = new Set(folders.filter((f) => f !== folder).map((f) => f.name.toLowerCase()));
17527
+ if (siblings.has(name.toLowerCase())) {
17528
+ notify14(`A folder named "${name}" already exists here.`, "error");
17529
+ return;
17530
+ }
17531
+ const oldPath = joinFolderPath(folder.path);
17532
+ const newPrefix = [...folder.path.slice(0, -1), name];
17533
+ const newPath = joinFolderPath(newPrefix);
17534
+ const docs = collectFolderDocs(documents, folder.path);
17535
+ try {
17536
+ if (await renameFolder(docs, folder.path, newPrefix) > 0) {
17537
+ notify14(`Renamed folder to "${name}"`, "success");
17538
+ }
17539
+ } catch {
17540
+ notify14("Failed to rename folder. Please try again.", "error");
17541
+ return;
17542
+ }
17543
+ if (oldPath) removePendingFolder(wsId, oldPath);
17544
+ if (docs.length === 0 && newPath) addPendingFolder(wsId, newPath);
17545
+ },
17546
+ [wsId, folders, documents, renameFolder, notify14, addPendingFolder, removePendingFolder]
17547
+ );
17548
+ const cancelFolderName = useCallback(() => setEditingFolderPath(null), []);
17549
+ const clearSelection = useCallback(() => {
17550
+ if (selectedIdsRef.current.length > 0) onSelectionChange([]);
17551
+ anchorRef.current = null;
17552
+ }, [onSelectionChange]);
17553
+ const handleSelect = useCallback(
17554
+ (doc, e) => {
17555
+ const id = doc.external_id;
17556
+ if (e.shiftKey) {
17557
+ const from = anchorRef.current ?? id;
17558
+ const a = orderedFileIds.indexOf(from);
17559
+ const b = orderedFileIds.indexOf(id);
17560
+ if (a !== -1 && b !== -1) {
17561
+ anchorRef.current = from;
17562
+ const [lo, hi] = a < b ? [a, b] : [b, a];
17563
+ onSelectionChange(orderedFileIds.slice(lo, hi + 1));
17564
+ return;
17565
+ }
17566
+ }
17567
+ if (e.metaKey || e.ctrlKey) {
17568
+ onSelectionChange(
17569
+ selectedSet.has(id) ? selectedIds.filter((s) => s !== id) : [...selectedIds, id]
17570
+ );
17571
+ anchorRef.current = id;
17572
+ return;
17573
+ }
17574
+ onSelectionChange([id]);
17575
+ anchorRef.current = id;
17576
+ },
17577
+ [onSelectionChange, orderedFileIds, selectedIds, selectedSet]
17578
+ );
17579
+ const handleToggle = useCallback(
17580
+ (doc, e) => {
17581
+ e.stopPropagation();
17582
+ const id = doc.external_id;
17583
+ onSelectionChange(
17584
+ selectedSet.has(id) ? selectedIds.filter((s) => s !== id) : [...selectedIds, id]
17585
+ );
17586
+ anchorRef.current = id;
17587
+ },
17588
+ [onSelectionChange, selectedIds, selectedSet]
17589
+ );
17590
+ const handleToggleFolder = useCallback(
17591
+ (folder, e) => {
17592
+ e.stopPropagation();
17593
+ const ids = collectFolderDocs(documents, folder.path).map((d) => d.external_id);
17594
+ if (ids.length === 0) return;
17595
+ if (ids.every((id) => selectedSet.has(id))) {
17596
+ const remove = new Set(ids);
17597
+ onSelectionChange(selectedIds.filter((id) => !remove.has(id)));
17598
+ } else {
17599
+ onSelectionChange(Array.from(/* @__PURE__ */ new Set([...selectedIds, ...ids])));
17600
+ }
17601
+ anchorRef.current = null;
17602
+ },
17603
+ [documents, onSelectionChange, selectedIds, selectedSet]
17604
+ );
17605
+ const handleOpenFile = useCallback(
17606
+ (doc) => onOpenDocument(doc.external_id),
17607
+ [onOpenDocument]
17608
+ );
17609
+ const handleOpenInReview = useMemo(
17610
+ () => onOpenInReview ? (doc) => onOpenInReview(doc.external_id) : void 0,
17611
+ [onOpenInReview]
17612
+ );
17613
+ const startRename = useCallback((doc) => setRenamingId(doc.external_id), []);
17614
+ const cancelRename = useCallback(() => setRenamingId(null), []);
17615
+ const commitRename = useCallback(
17616
+ async (doc, name) => {
17617
+ setRenamingId(null);
17618
+ try {
17619
+ await updateDocument.mutateAsync({
17620
+ documents: [
17621
+ { external_id: doc.external_id, doc_metadata: { ...doc.doc_metadata, title: name } }
17622
+ ]
17623
+ });
17624
+ notify14(`Renamed to "${name}"`, "success");
17625
+ } catch {
17626
+ notify14("Failed to rename. Please try again.", "error");
17627
+ }
17628
+ },
17629
+ [updateDocument, notify14]
17630
+ );
17631
+ useEffect(() => {
17632
+ const onKey = (e) => {
17633
+ if (e.key !== "F2" || renamingId) return;
17634
+ const target = e.target;
17635
+ if (target && /^(input|textarea|select)$/i.test(target.tagName)) return;
17636
+ if (selectedIdsRef.current.length === 1) setRenamingId(selectedIdsRef.current[0]);
17637
+ };
17638
+ window.addEventListener("keydown", onKey);
17639
+ return () => window.removeEventListener("keydown", onKey);
17640
+ }, [renamingId]);
17641
+ const handleDownload = useCallback(
17642
+ (doc) => void downloadDocument(doc),
17643
+ [downloadDocument]
17644
+ );
17645
+ const handleMove = useMemo(
17646
+ () => onMoveSelected ? (doc) => {
17647
+ onSelectionChange([doc.external_id]);
17648
+ onMoveSelected();
17649
+ } : void 0,
17650
+ [onSelectionChange, onMoveSelected]
17651
+ );
17652
+ const handleDelete = useCallback(
17653
+ async (doc) => {
17654
+ const name = getExplorerDisplayName(doc);
17655
+ const confirmed = await confirm({
17656
+ message: `Delete "${name}"? ${RECENTLY_DELETED_HINT}`,
17657
+ title: "Delete file"
17658
+ });
17659
+ if (!confirmed) return;
17660
+ try {
17661
+ await deleteDocuments.mutateAsync([doc.external_id]);
17662
+ onSelectionChange(selectedIdsRef.current.filter((id) => id !== doc.external_id));
17663
+ notify14(`Deleted "${name}"`, "success");
17664
+ } catch {
17665
+ notify14("Failed to delete. Please try again.", "error");
17666
+ }
17667
+ },
17668
+ [confirm, deleteDocuments, onSelectionChange, notify14]
17669
+ );
17670
+ const startFolderRename = useCallback(
17671
+ (folder) => setEditingFolderPath(folder.path.join("/")),
17672
+ []
17673
+ );
17674
+ const handleFolderDownload = useCallback(
17675
+ (folder) => {
17676
+ const docs = collectFolderDocs(documents, folder.path);
17677
+ if (docs.length > 0) void downloadDocuments(docs);
17678
+ },
17679
+ [documents, downloadDocuments]
17680
+ );
17681
+ const handleFolderMove = useMemo(() => {
17682
+ if (onFolderMove) return onFolderMove;
17683
+ if (!onMoveSelected) return void 0;
17684
+ return (folder) => {
17685
+ const ids = collectFolderDocs(documents, folder.path).map((d) => d.external_id);
17686
+ if (ids.length === 0) return;
17687
+ onSelectionChange(ids);
17688
+ onMoveSelected();
17689
+ };
17690
+ }, [documents, onFolderMove, onMoveSelected, onSelectionChange]);
17691
+ const handleFolderDelete = useCallback(
17692
+ async (folder) => {
17693
+ const docs = collectFolderDocs(documents, folder.path);
17694
+ if (docs.length === 0) {
17695
+ const path = joinFolderPath(folder.path);
17696
+ if (wsId && path) removePendingFolder(wsId, path);
17697
+ return;
17698
+ }
17699
+ const confirmed = await confirm({
17700
+ message: `Delete folder "${folder.name}" and its ${docs.length} item${docs.length === 1 ? "" : "s"}? ${RECENTLY_DELETED_HINT_PLURAL}`,
17701
+ title: "Delete folder"
17702
+ });
17703
+ if (!confirmed) return;
17704
+ try {
17705
+ const ids = docs.map((d) => d.external_id);
17706
+ await deleteDocuments.mutateAsync(ids);
17707
+ const removed = new Set(ids);
17708
+ onSelectionChange(selectedIdsRef.current.filter((id) => !removed.has(id)));
17709
+ notify14(`Deleted folder "${folder.name}"`, "success");
17710
+ } catch {
17711
+ notify14("Failed to delete folder. Please try again.", "error");
17712
+ }
17713
+ },
17714
+ [confirm, documents, deleteDocuments, onSelectionChange, removePendingFolder, wsId, notify14]
17715
+ );
17716
+ const canDeleteFolder = useCallback(
17717
+ (folder) => canUpload && collectFolderDocs(documents, folder.path).every(
17718
+ (doc) => canDeleteDocument(doc, userId, role)
17719
+ ),
17720
+ [canUpload, documents, userId, role]
17721
+ );
17722
+ const handleBackgroundClick = useCallback(
17723
+ (e) => {
17724
+ if (didMarqueeRef.current) {
17725
+ didMarqueeRef.current = false;
17726
+ return;
17727
+ }
17728
+ if (!e.target.closest('[data-testid^="explorer-"]')) clearSelection();
17729
+ },
17730
+ [clearSelection]
17731
+ );
17732
+ const moveDocs = useCallback(
17733
+ async (ids, folder) => {
17734
+ if (ids.length === 0) return;
17735
+ try {
17736
+ await updateDocument.mutateAsync({
17737
+ documents: ids.map((external_id) => ({ external_id, folder }))
17738
+ });
17739
+ clearSelection();
17740
+ notify14(
17741
+ `Moved ${ids.length} item${ids.length === 1 ? "" : "s"} to "${folder ?? "Home"}"`,
17742
+ "success"
17743
+ );
17744
+ } catch {
17745
+ notify14("Failed to move. Please try again.", "error");
17746
+ }
17747
+ },
17748
+ [updateDocument, clearSelection, notify14]
17749
+ );
17750
+ const sensors = useSensors(
17751
+ useSensor(MouseSensor, { activationConstraint: { distance: 6 } }),
17752
+ useSensor(TouchSensor, { activationConstraint: { delay: 150, tolerance: 8 } })
17753
+ );
17754
+ const collisionDetection = useCallback((args) => {
17755
+ const hits = pointerWithin(args);
17756
+ return hits.length > 0 ? hits : rectIntersection(args);
17757
+ }, []);
17758
+ const handleDragStart = useCallback(
17759
+ (event) => {
17760
+ const docId = event.active.data.current?.docId;
17761
+ if (!docId) return;
17762
+ setActiveDragId(docId);
17763
+ if (!selectedIdsRef.current.includes(docId)) {
17764
+ onSelectionChange([docId]);
17765
+ dragIdsRef.current = [docId];
17766
+ } else {
17767
+ dragIdsRef.current = selectedIdsRef.current;
17768
+ }
17769
+ },
17770
+ [onSelectionChange]
17771
+ );
17772
+ const handleDragEnd = useCallback(
17773
+ (event) => {
17774
+ setActiveDragId(null);
17775
+ const data = event.over?.data.current;
17776
+ if (data?.type === "folder" && data.path) {
17777
+ const targetFolder = joinFolderPath(data.path);
17778
+ const moving = dragIdsRef.current.filter((id) => {
17779
+ const doc = documents.find((d) => d.external_id === id);
17780
+ return doc && (doc.folder ?? null) !== targetFolder;
17781
+ });
17782
+ void moveDocs(moving, targetFolder);
17783
+ }
17784
+ },
17785
+ [documents, moveDocs]
17786
+ );
17787
+ const handleNativeDragOver = useCallback(
17788
+ (e) => {
17789
+ if (!canUpload) return;
17790
+ if (e.dataTransfer.types.includes("Files")) {
17791
+ e.preventDefault();
17792
+ e.dataTransfer.dropEffect = "copy";
17793
+ }
17794
+ },
17795
+ [canUpload]
17796
+ );
17797
+ const handleNativeDragEnter = useCallback(
17798
+ (e) => {
17799
+ if (!canUpload || !e.dataTransfer.types.includes("Files")) return;
17800
+ fileDragDepth.current += 1;
17801
+ setIsFileDragOver(true);
17802
+ },
17803
+ [canUpload]
17804
+ );
17805
+ const handleNativeDragLeave = useCallback(() => {
17806
+ fileDragDepth.current = Math.max(0, fileDragDepth.current - 1);
17807
+ if (fileDragDepth.current === 0) setIsFileDragOver(false);
17808
+ }, []);
17809
+ const handleNativeDrop = useCallback(
17810
+ (e) => {
17811
+ fileDragDepth.current = 0;
17812
+ setIsFileDragOver(false);
17813
+ if (!canUpload) return;
17814
+ const dropped = Array.from(e.dataTransfer.files ?? []);
17815
+ if (dropped.length > 0) {
17816
+ e.preventDefault();
17817
+ onUploadFiles(dropped, joinFolderPath(currentPath));
17818
+ }
17819
+ },
17820
+ [canUpload, currentPath, onUploadFiles]
17821
+ );
17822
+ const activeDoc = activeDragId ? documents.find((d) => d.external_id === activeDragId) : null;
17823
+ const dragCount = dragIdsRef.current.length || 1;
17824
+ if (isWholeWorkspaceEmpty) return /* @__PURE__ */ jsx(Fragment, { children: emptyWorkspace });
17825
+ return /* @__PURE__ */ jsxs(
17826
+ DndContext,
17827
+ {
17828
+ sensors,
17829
+ collisionDetection,
17830
+ onDragStart: handleDragStart,
17831
+ onDragEnd: handleDragEnd,
17832
+ onDragCancel: () => setActiveDragId(null),
17833
+ children: [
17834
+ /* @__PURE__ */ jsx(ExplorerFrame, { "data-testid": "documents-explorer", children: /* @__PURE__ */ jsxs(ExplorerStage, { children: [
17835
+ /* @__PURE__ */ jsx(
17836
+ ExplorerScroller,
17837
+ {
17838
+ ref: scrollRef,
17839
+ onClick: handleBackgroundClick,
17840
+ onPointerDown: handleScrollPointerDown,
17841
+ onDragEnter: handleNativeDragEnter,
17842
+ onDragOver: handleNativeDragOver,
17843
+ onDragLeave: handleNativeDragLeave,
17844
+ onDrop: handleNativeDrop,
17845
+ "data-testid": "explorer-scroll",
17846
+ children: isLoading && documents.length === 0 ? /* @__PURE__ */ jsx(ExplorerLoadingTiles, {}) : entries.length === 0 ? /* @__PURE__ */ jsx(
17847
+ ExplorerEmpty,
17848
+ {
17849
+ title: isSearching ? `No files match "${search.trim()}"` : "This folder is empty",
17850
+ description: isSearching ? "Try a different search." : canUpload ? "Drop files here or use Upload to add documents." : "No documents here yet."
17851
+ }
17852
+ ) : containerWidth === 0 ? (
17853
+ // Not measured yet: rendering now would flash one stretched column.
17854
+ /* @__PURE__ */ jsx(ExplorerLoadingTiles, {})
17855
+ ) : /* @__PURE__ */ jsx(
17856
+ "div",
17857
+ {
17858
+ style: {
17859
+ height: rowVirtualizer.getTotalSize(),
17860
+ position: "relative",
17861
+ width: "100%"
17862
+ },
17863
+ children: virtualRows.map((virtualRow) => {
17864
+ const start = virtualRow.index * columnCount;
17865
+ const rowEntries = entries.slice(start, start + columnCount);
17866
+ return /* @__PURE__ */ jsx(
17867
+ "div",
17868
+ {
17869
+ style: {
17870
+ position: "absolute",
17871
+ top: 0,
17872
+ left: 0,
17873
+ width: "100%",
17874
+ height: tileHeight,
17875
+ transform: `translateY(${virtualRow.start}px)`,
17876
+ display: "grid",
17877
+ gridTemplateColumns: `repeat(${columnCount}, minmax(0, 1fr))`,
17878
+ columnGap: GRID_GAP
17879
+ },
17880
+ children: rowEntries.map((entry) => {
17881
+ if (entry.kind === "folder") {
17882
+ const key = entry.path.join("/");
17883
+ return /* @__PURE__ */ jsx(
17884
+ FolderTile,
17885
+ {
17886
+ folder: entry,
17887
+ density,
17888
+ editing: editingFolderPath === key,
17889
+ selected: folderSelection.get(key)?.all,
17890
+ partiallySelected: folderSelection.get(key)?.some,
17891
+ documents: folderDocs.get(key) ?? NO_DOCUMENTS,
17892
+ onOpen: openFolder,
17893
+ onToggleSelect: handleToggleFolder,
17894
+ onCommitName: commitFolderName,
17895
+ onCancelName: cancelFolderName,
17896
+ onStartRename: canUpload ? startFolderRename : void 0,
17897
+ onDownload: handleFolderDownload,
17898
+ onMove: canUpload ? handleFolderMove : void 0,
17899
+ onDelete: canDeleteFolder(entry) ? handleFolderDelete : void 0
17900
+ },
17901
+ `folder:${key}`
17902
+ );
17903
+ }
17904
+ const doc = entry.doc;
17905
+ return /* @__PURE__ */ jsx(
17906
+ FileTile,
17907
+ {
17908
+ doc,
17909
+ density,
17910
+ selected: selectedSet.has(doc.external_id),
17911
+ dragging: activeDragId === doc.external_id,
17912
+ dndActive: activeDragId !== null,
17913
+ renaming: renamingId === doc.external_id,
17914
+ canDelete: canDeleteDocument(doc, userId, role),
17915
+ thumbnailUrl: thumbnailsById.get(doc.external_id),
17916
+ modifiedBy: userById.get(
17917
+ doc.updated_by_ext_id ?? doc.created_by_ext_id
17918
+ ),
17919
+ tagNameById,
17920
+ onSelect: handleSelect,
17921
+ onToggle: handleToggle,
17922
+ onOpen: handleOpenFile,
17923
+ onReview: handleOpenInReview,
17924
+ onStartRename: startRename,
17925
+ onCommitRename: commitRename,
17926
+ onCancelRename: cancelRename,
17927
+ onDownload: handleDownload,
17928
+ onMove: canUpload ? handleMove : void 0,
17929
+ onDelete: handleDelete
17930
+ },
17931
+ doc.external_id
17932
+ );
17933
+ })
17934
+ },
17935
+ virtualRow.key
17936
+ );
17937
+ })
17938
+ }
17939
+ )
17940
+ }
17941
+ ),
17942
+ /* @__PURE__ */ jsx(ExplorerMarquee, { ref: marqueeOverlayRef, "data-testid": "explorer-marquee" }),
17943
+ isFileDragOver && /* @__PURE__ */ jsx(
17944
+ ExplorerDropOverlay,
17945
+ {
17946
+ label: `Drop to upload${currentPath.length > 0 ? ` to "${currentPath[currentPath.length - 1]}"` : ""}`
17947
+ }
17948
+ )
17949
+ ] }) }),
17950
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: activeDoc ? /* @__PURE__ */ jsx(DragPreview, { doc: activeDoc, count: dragCount }) : null })
17951
+ ]
17952
+ }
17953
+ );
17954
+ });
17955
+ function DragPreview({ doc, count }) {
17956
+ const Icon = getFileTypeIcon(doc.file_name, { mime: doc.file_type });
17957
+ return /* @__PURE__ */ jsx(
17958
+ ExplorerDragPreview,
17959
+ {
17960
+ icon: /* @__PURE__ */ jsx(Icon, { size: 20 }),
17961
+ title: getDocumentTitle(doc, doc.file_name ?? "Untitled"),
17962
+ count
17963
+ }
17964
+ );
17965
+ }
16818
17966
  function ActiveChat({ agentId, workspaceId }) {
16819
17967
  const chat = useArbiChat({
16820
17968
  agentId,
@@ -16882,6 +18030,6 @@ function ConnectedAgentChatPanel({
16882
18030
  ] });
16883
18031
  }
16884
18032
 
16885
- export { AddAddonDialog, ArbiAppProvider, BACKFILL_OPTIONS, BillingProjectEntity, ChangePlanDialog, ConnectedAccountSettings, ConnectedAgentChatPanel, ConnectedAgentCreator, ConnectedAppsSettings, ConnectedCalendar, ConnectedDangerZone, ConnectedDriveSyncDialog, ConnectedFactsPage, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailInviteCard, ConnectedMailMessage, ConnectedMailSettings, ConnectedMailSetup, ConnectedMailbox, ConnectedMailboxSyncDialog, ConnectedNotificationCenter, ConnectedNotificationPreferences, ConnectedPasswordChangeDialog, ConnectedPremiumModels, ConnectedPrivacySettings, ConnectedProfileCard, ConnectedProjectsPanel, ConnectedPublicBooking, ConnectedRequireVerified, ConnectedSearchPage, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSettingsPage, ConnectedSkillUpload, ConnectedSkillsLibrary, ConnectedTagTable, ConnectedTagsPage, ConnectedTaskAccessRequest, ConnectedTaskActivityDialog, ConnectedThreadFollow, ConnectedUpcomingEvents, ConnectedWorkflowHistory, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceDashboard, ConnectedWorkspaceList, ConnectedWorkspaceMembers, ConnectedWorkspaceNotificationMenu, ConnectedWorkspaceSettings, DEFAULT_CONNECTED_SEARCH_PAGE_LABELS, DEFAULT_CONNECTED_SKILL_UPLOAD_LABELS, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, DEFAULT_SEARCH_ROW_LABELS, DEFAULT_SETTINGS_SECTIONS, DEFAULT_TIMELINE_LABEL_WIDTH, DEFAULT_WORKSPACE_LIST_LABELS, FACTS_GRID_ID, InvoicesModal, NewProjectFlow, NotificationCenter, NotificationEvent, PROJECTS_BASE_TAB_ID, ProjectDetail, ProjectsTable, REFERRAL_OFFER, SETTINGS_SECTION_IDS, SessionStatusIcon, SharedProjects, StreamedArtifact, WORKSPACES_TAB_ID, WorkflowRunThread, accountSettingsSection, bookingCancelLink, buildNotificationCenterItems, chatSettingsSection, connectedAppsSettingsSection, countNotificationTypes, defaultUnmetPasswordRequirement, filterNotificationCenterItems, formatLastSync, mailSettingsSection, openMeetingLink, privacySettingsSection, renderSessionStatusIcon, requestCalendarComposer, requestCalendarEvent, requestProjectBillingFocus, resolveMailbox, toWorkspaceSummary, useAutoRefreshPendingProjects, useCalendarViewStore, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications, useConnectedWorkspaceList, useConnectedWorkspaceSettings, useOpenBillingPortal, useProjectBillingFocus, useReferralReward, windowLabel, workflowRunView };
18033
+ export { AddAddonDialog, ArbiAppProvider, BACKFILL_OPTIONS, BillingProjectEntity, ChangePlanDialog, ConnectedAccountSettings, ConnectedAgentChatPanel, ConnectedAgentCreator, ConnectedAppsSettings, ConnectedCalendar, ConnectedDangerZone, ConnectedDriveSyncDialog, ConnectedFactsPage, ConnectedFilesExplorer, ConnectedIdentityVerification, ConnectedLogoutDialog, ConnectedMailAccounts, ConnectedMailComposer, ConnectedMailInviteCard, ConnectedMailMessage, ConnectedMailSettings, ConnectedMailSetup, ConnectedMailbox, ConnectedMailboxSyncDialog, ConnectedNotificationCenter, ConnectedNotificationPreferences, ConnectedPasswordChangeDialog, ConnectedPremiumModels, ConnectedPrivacySettings, ConnectedProfileCard, ConnectedProjectsPanel, ConnectedPublicBooking, ConnectedRequireVerified, ConnectedSearchPage, ConnectedSecuritySection, ConnectedSessionsRow, ConnectedSettingsPage, ConnectedSkillUpload, ConnectedSkillsLibrary, ConnectedTagTable, ConnectedTagsPage, ConnectedTaskAccessRequest, ConnectedTaskActivityDialog, ConnectedThreadFollow, ConnectedUpcomingEvents, ConnectedWorkflowHistory, ConnectedWorkflowRunner, ConnectedWorkspaceActivity, ConnectedWorkspaceDashboard, ConnectedWorkspaceList, ConnectedWorkspaceMembers, ConnectedWorkspaceNotificationMenu, ConnectedWorkspaceSettings, DEFAULT_CONNECTED_SEARCH_PAGE_LABELS, DEFAULT_CONNECTED_SKILL_UPLOAD_LABELS, DEFAULT_CONNECTED_WORKFLOW_RUNNER_LABELS, DEFAULT_SEARCH_ROW_LABELS, DEFAULT_SETTINGS_SECTIONS, DEFAULT_TIMELINE_LABEL_WIDTH, DEFAULT_WORKSPACE_LIST_LABELS, DocumentSyncGlyph, FACTS_GRID_ID, FileTooltipContent, FilesSupportProvider, InvoicesModal, NewProjectFlow, NotificationCenter, NotificationEvent, PROJECTS_BASE_TAB_ID, ProjectDetail, ProjectsTable, REFERRAL_OFFER, SETTINGS_SECTION_IDS, SessionStatusIcon, SharedProjects, StreamedArtifact, TILE_DEFAULT, TILE_MAX, TILE_MIN, WORKSPACES_TAB_ID, WorkflowRunThread, accountSettingsSection, bookingCancelLink, buildNotificationCenterItems, chatSettingsSection, connectedAppsSettingsSection, countNotificationTypes, defaultUnmetPasswordRequirement, filterNotificationCenterItems, formatLastSync, isActiveDocumentStatus, mailSettingsSection, openMeetingLink, privacySettingsSection, renderSessionStatusIcon, requestCalendarComposer, requestCalendarEvent, requestProjectBillingFocus, resolveMailbox, startMenuItem, toWorkspaceSummary, useAutoRefreshPendingProjects, useCalendarViewStore, useConnectedDocxNotifications, useConnectedShellWorkspaces, useConnectedWorkNotifications, useConnectedWorkspaceList, useConnectedWorkspaceSettings, useFilesStore, useFilesSupport, useOfflineDocumentIds, useOpenBillingPortal, usePendingFolders, useProjectBillingFocus, useReferralReward, useViewingDocumentId, windowLabel, workflowRunView };
16886
18034
  //# sourceMappingURL=index.js.map
16887
18035
  //# sourceMappingURL=index.js.map