@arbidocs/blocks 0.4.156 → 0.4.157

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 (72) hide show
  1. package/dist/ConnectedNativeDocxComparison-2PLOOXSH.js +1 -1
  2. package/dist/ConnectedNativeDocxComparison-S25BPUH5.cjs +1 -1
  3. package/dist/ConnectedNativeDocxEditor-JJAGMHZC.js +1 -1
  4. package/dist/ConnectedNativeDocxEditor-YA4HWLY4.cjs +1 -1
  5. package/dist/ConnectedStreamedWordArtifact-EXYZAU4Y.cjs +1 -1
  6. package/dist/ConnectedStreamedWordArtifact-YAAXY5KY.js +1 -1
  7. package/dist/agents/index.cjs +238 -238
  8. package/dist/agents/index.js +4 -4
  9. package/dist/chat/index.cjs +1 -1
  10. package/dist/chat/index.js +1 -1
  11. package/dist/{chunk-6HMCF4AJ.js → chunk-5ZIYPZYE.js} +1033 -5
  12. package/dist/chunk-5ZIYPZYE.js.map +1 -0
  13. package/dist/{chunk-O3FB62T5.cjs → chunk-6QAAIDQQ.cjs} +47 -47
  14. package/dist/{chunk-O3FB62T5.cjs.map → chunk-6QAAIDQQ.cjs.map} +1 -1
  15. package/dist/{chunk-UKT3WH5Z.cjs → chunk-B3FMUFAW.cjs} +3 -3
  16. package/dist/{chunk-UKT3WH5Z.cjs.map → chunk-B3FMUFAW.cjs.map} +1 -1
  17. package/dist/{chunk-W7CWGURO.js → chunk-FTZ45WHY.js} +6 -685
  18. package/dist/chunk-FTZ45WHY.js.map +1 -0
  19. package/dist/{chunk-HDJRSCRE.cjs → chunk-GC2FSPSQ.cjs} +6 -6
  20. package/dist/{chunk-HDJRSCRE.cjs.map → chunk-GC2FSPSQ.cjs.map} +1 -1
  21. package/dist/{chunk-UKCCV63X.js → chunk-MNG7N6M2.js} +4 -4
  22. package/dist/{chunk-UKCCV63X.js.map → chunk-MNG7N6M2.js.map} +1 -1
  23. package/dist/{chunk-MUDEL7UL.cjs → chunk-NPLGB4LI.cjs} +4 -703
  24. package/dist/chunk-NPLGB4LI.cjs.map +1 -0
  25. package/dist/{chunk-UPMADXDU.js → chunk-P6RIBIVN.js} +3 -3
  26. package/dist/{chunk-UPMADXDU.js.map → chunk-P6RIBIVN.js.map} +1 -1
  27. package/dist/{chunk-WV4TD2HZ.cjs → chunk-QJFJ5JBF.cjs} +1055 -2
  28. package/dist/chunk-QJFJ5JBF.cjs.map +1 -0
  29. package/dist/{chunk-6HG56GCT.js → chunk-QKR5LWDO.js} +4 -4
  30. package/dist/{chunk-6HG56GCT.js.map → chunk-QKR5LWDO.js.map} +1 -1
  31. package/dist/{chunk-YMQDDUKW.js → chunk-WU34CHU5.js} +3 -3
  32. package/dist/{chunk-YMQDDUKW.js.map → chunk-WU34CHU5.js.map} +1 -1
  33. package/dist/{chunk-FEGXFABZ.cjs → chunk-YF3QUHKN.cjs} +3 -3
  34. package/dist/{chunk-FEGXFABZ.cjs.map → chunk-YF3QUHKN.cjs.map} +1 -1
  35. package/dist/connect/docx.cjs +1 -1
  36. package/dist/connect/docx.js +1 -1
  37. package/dist/connect/index.cjs +730 -170
  38. package/dist/connect/index.cjs.map +1 -1
  39. package/dist/connect/index.js +571 -16
  40. package/dist/connect/index.js.map +1 -1
  41. package/dist/connect/viewer.cjs +45 -45
  42. package/dist/connect/viewer.js +7 -7
  43. package/dist/connection/files/index.d.ts +5 -0
  44. package/dist/connection/files/support.d.ts +2 -0
  45. package/dist/connection/files/upload/AddFromAccountsButton.d.ts +16 -0
  46. package/dist/connection/files/upload/ConnectedUpload.d.ts +36 -0
  47. package/dist/connection/files/upload/ConnectedUploadDropzone.d.ts +36 -0
  48. package/dist/connection/files/upload/EmptyWorkspaceUpload.d.ts +13 -0
  49. package/dist/connection/files/upload/useAddFromUrls.d.ts +13 -0
  50. package/dist/document-tools/index.cjs +1 -1
  51. package/dist/document-tools/index.js +1 -1
  52. package/dist/facts/index.cjs +54 -54
  53. package/dist/facts/index.js +4 -4
  54. package/dist/files/PrivacyExplainerModal.d.ts +8 -0
  55. package/dist/files/ShareToggle.d.ts +8 -0
  56. package/dist/files/UrlInputDialog.d.ts +24 -0
  57. package/dist/files/index.cjs +298 -278
  58. package/dist/files/index.d.ts +4 -0
  59. package/dist/files/index.js +2 -2
  60. package/dist/files/media-preview.d.ts +24 -0
  61. package/dist/index.cjs +347 -347
  62. package/dist/index.js +8 -8
  63. package/dist/studio/index.cjs +1 -1
  64. package/dist/studio/index.js +1 -1
  65. package/dist/workflow-canvas/index.cjs +16 -16
  66. package/dist/workflow-canvas/index.js +4 -4
  67. package/node_modules/@arbidocs/coedit/package.json +1 -1
  68. package/package.json +5 -5
  69. package/dist/chunk-6HMCF4AJ.js.map +0 -1
  70. package/dist/chunk-MUDEL7UL.cjs.map +0 -1
  71. package/dist/chunk-W7CWGURO.js.map +0 -1
  72. package/dist/chunk-WV4TD2HZ.cjs.map +0 -1
@@ -3,9 +3,10 @@ import { LabelPickerList, DEFAULT_LABEL_MULTI_SELECT_LABELS } from './chunk-SDOE
3
3
  import { ActionLink } from './chunk-VF5EUAIV.js';
4
4
  import { TONE_SURFACE } from './chunk-AUS3KBHJ.js';
5
5
  import { testIdOr } from './chunk-7F6CBP3Z.js';
6
+ import { ModalHeader } from './chunk-TX2ZICS7.js';
6
7
  import { formatCount } from './chunk-OXF2IFXG.js';
7
- import { AlertCircle, CircleSlash, BrainCircuit, LockKeyholeOpen, DatabaseZap, FileX, BookMarked, ListChevronsUpDown, FolderOpen, ChevronsUpDown, Check, Minus, Plus, X, ChevronDown, FolderPlus, UploadCloud, Mail, Folder, LoaderCircle, Sparkles, MoreHorizontal } from 'lucide-react';
8
- import { cn, Button, Tooltip, TooltipTrigger, Slider, TooltipContent, Popover, PopoverTrigger, PopoverContent, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, getDocumentTitle, getFileExtension, extensionFromMime, getEntityLabel, isAgent, getUserDisplayName, Checkbox, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuItem } from '@arbidocs/react/ui';
8
+ import { AlertCircle, CircleSlash, BrainCircuit, LockKeyholeOpen, DatabaseZap, FileX, BookMarked, ListChevronsUpDown, FolderOpen, ChevronsUpDown, Check, Minus, Plus, X, ChevronDown, FolderPlus, UploadCloud, Mail, Folder, LoaderCircle, Sparkles, CopyX, Loader2, Upload, FileText, Globe, Link, Info, ChevronRight, ShieldCheck, KeyRound, Eye, MoreHorizontal } from 'lucide-react';
9
+ import { cn, Button, Tooltip, TooltipTrigger, Slider, TooltipContent, Popover, PopoverTrigger, PopoverContent, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, getDocumentTitle, getFileExtension, extensionFromMime, getEntityLabel, isAgent, getUserDisplayName, Progress, IconToggle, Textarea, Dialog, DialogTrigger, DialogContent, DialogTitle, DialogDescription, Label, Input, DialogHeader, VisibilityToggle, Checkbox, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuItem } from '@arbidocs/react/ui';
9
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
11
  import { forwardRef, memo, useMemo, useState, useRef, useEffect, useCallback } from 'react';
11
12
  import { cva } from 'class-variance-authority';
@@ -1714,7 +1715,1034 @@ function visibilityToggleTooltip(shared) {
1714
1715
  const workspace = getEntityLabel("WORKSPACE").toLowerCase();
1715
1716
  return shared ? `${visibilityAudience(true)}: everyone in it can see this file. Click to make it private to you.` : `${visibilityAudience(false)}: only you can see this file. Click to share it with the ${workspace}.`;
1716
1717
  }
1718
+ var DEFAULT_UPLOAD_PARTS_LABELS = {
1719
+ rejectedReasons: {
1720
+ too_large: "The following files exceed the 1 GB size limit:",
1721
+ unsupported: "The following files are not in a supported format (PDF, DOC(X), PPT(X), XLS(X), RTF, TXT, MD, HTML, images, audio, video, email, Outlook archive (.pst/.ost), or ZIP):",
1722
+ empty: "The following files are empty (0 bytes):",
1723
+ no_extension: "The following files have no file extension:",
1724
+ empty_zip: "The following ZIP archives contain no supported files:",
1725
+ zip_error: "The following ZIP archives could not be processed:"
1726
+ },
1727
+ rejectedFallback: "The following files cannot be uploaded:",
1728
+ clearRejected: "Clear rejected files",
1729
+ selectedFiles: (count) => `Selected Files (${count})`,
1730
+ total: (size) => `Total: ${size}`,
1731
+ clearAll: "Clear All",
1732
+ remove: (fileName) => `Remove ${fileName}`,
1733
+ uploadComplete: (entity) => `Upload complete! Processing ${entity} in the background...`,
1734
+ uploading: (count, entity) => `Uploading ${count} ${entity}...`,
1735
+ uploadedIndexing: "Files uploaded successfully. ARBI is now indexing them in the background.",
1736
+ afterUpload: "After upload completes, ARBI will index files in the background."
1737
+ };
1738
+ function mergeUploadLabels(labels) {
1739
+ return {
1740
+ ...DEFAULT_UPLOAD_PARTS_LABELS,
1741
+ ...labels,
1742
+ rejectedReasons: {
1743
+ ...DEFAULT_UPLOAD_PARTS_LABELS.rejectedReasons,
1744
+ ...labels?.rejectedReasons
1745
+ }
1746
+ };
1747
+ }
1748
+ function UploadErrorMessage({
1749
+ message,
1750
+ className,
1751
+ testId
1752
+ }) {
1753
+ return /* @__PURE__ */ jsxs(
1754
+ "div",
1755
+ {
1756
+ role: "alert",
1757
+ className: cn("mt-6 flex items-center gap-2 rounded-lg bg-red-bg p-3", className),
1758
+ "data-testid": testIdOr(testId, { testId: "error-message" }),
1759
+ children: [
1760
+ /* @__PURE__ */ jsx(AlertCircle, { className: "size-5 shrink-0 text-red-text" }),
1761
+ /* @__PURE__ */ jsx("p", { className: "text-base text-red-text", children: message })
1762
+ ]
1763
+ }
1764
+ );
1765
+ }
1766
+ function RejectedFilesList({ files, onClear, labels, testIds }) {
1767
+ if (files.length === 0) return null;
1768
+ const l = mergeUploadLabels(labels);
1769
+ const byReason = /* @__PURE__ */ new Map();
1770
+ for (const file of files) {
1771
+ const reason = typeof file === "string" ? "unsupported" : file.reason || "unsupported";
1772
+ const name = typeof file === "string" ? file : file.name;
1773
+ byReason.set(reason, [...byReason.get(reason) ?? [], name]);
1774
+ }
1775
+ return /* @__PURE__ */ jsx(
1776
+ "div",
1777
+ {
1778
+ className: "mt-4 rounded-lg bg-red-bg p-3",
1779
+ "data-testid": testIdOr(testIds?.root, { testId: "rejected-files-list" }),
1780
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
1781
+ /* @__PURE__ */ jsx(AlertCircle, { className: "mt-0.5 size-5 shrink-0 text-red-text" }),
1782
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: Array.from(byReason.entries()).map(([reason, names], groupIndex) => /* @__PURE__ */ jsxs("div", { className: groupIndex > 0 ? "mt-3" : "", children: [
1783
+ /* @__PURE__ */ jsx("p", { className: "mb-2 text-sm font-medium text-red-text", children: l.rejectedReasons[reason] ?? l.rejectedFallback }),
1784
+ /* @__PURE__ */ jsx("div", { className: "space-y-1", children: names.map((name, index) => /* @__PURE__ */ jsx(
1785
+ "div",
1786
+ {
1787
+ className: "rounded-md bg-background/60 p-2",
1788
+ "data-testid": testIdOr(testIds?.item?.(groupIndex, index), {
1789
+ testId: `rejected-file-item-${groupIndex}-${index}`
1790
+ }),
1791
+ children: /* @__PURE__ */ jsx("span", { className: "text-sm text-red-text", children: name })
1792
+ },
1793
+ index
1794
+ )) })
1795
+ ] }, reason)) }),
1796
+ /* @__PURE__ */ jsx(
1797
+ Button,
1798
+ {
1799
+ variant: "ghost",
1800
+ size: "icon",
1801
+ onClick: onClear,
1802
+ "aria-label": l.clearRejected,
1803
+ className: "size-6 text-red-text hover:text-red-text pointer-coarse:size-11",
1804
+ "data-testid": testIdOr(testIds?.clear, { testId: "clear-rejected-files-button" }),
1805
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
1806
+ }
1807
+ )
1808
+ ] })
1809
+ }
1810
+ );
1811
+ }
1812
+ function folderPathOf(file) {
1813
+ const rel = file.webkitRelativePath;
1814
+ if (!rel) return null;
1815
+ const slash = rel.lastIndexOf("/");
1816
+ return slash > 0 ? rel.substring(0, slash) : null;
1817
+ }
1818
+ function SelectedFilesList({
1819
+ files,
1820
+ onRemoveFile,
1821
+ onClearAll,
1822
+ formatFileSize,
1823
+ renderFileIcon,
1824
+ labels,
1825
+ testIds
1826
+ }) {
1827
+ if (files.length === 0) return null;
1828
+ const l = mergeUploadLabels(labels);
1829
+ const total = files.reduce((sum, f) => sum + f.size, 0);
1830
+ return /* @__PURE__ */ jsxs(
1831
+ "div",
1832
+ {
1833
+ className: "mt-2 sm:mt-6",
1834
+ "data-testid": testIdOr(testIds?.root, { testId: "selected-files-list" }),
1835
+ children: [
1836
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
1837
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col sm:flex-row sm:items-center sm:gap-4", children: [
1838
+ /* @__PURE__ */ jsx("h4", { className: "text-base font-medium", children: l.selectedFiles(files.length) }),
1839
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.total(formatFileSize(total)) })
1840
+ ] }),
1841
+ onClearAll && /* @__PURE__ */ jsxs(
1842
+ Button,
1843
+ {
1844
+ variant: "ghost",
1845
+ size: "sm",
1846
+ onClick: onClearAll,
1847
+ className: "h-8 shrink-0 whitespace-nowrap text-muted-foreground hover:text-foreground pointer-coarse:h-11",
1848
+ "data-testid": testIdOr(testIds?.clearAll, { testId: "clear-all-files-button" }),
1849
+ children: [
1850
+ l.clearAll,
1851
+ /* @__PURE__ */ jsx(X, { className: "ml-1 hidden size-4 sm:block" })
1852
+ ]
1853
+ }
1854
+ )
1855
+ ] }),
1856
+ /* @__PURE__ */ jsx("div", { className: "space-y-1 overflow-y-auto overscroll-contain pr-1 sm:max-h-[300px]", children: files.map((file, index) => {
1857
+ const folder = folderPathOf(file);
1858
+ return /* @__PURE__ */ jsxs(
1859
+ "div",
1860
+ {
1861
+ className: "flex min-w-0 items-center gap-2 rounded-lg bg-muted p-3",
1862
+ "data-testid": testIdOr(testIds?.item?.(index), {
1863
+ testId: `selected-file-item-${index}`
1864
+ }),
1865
+ children: [
1866
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", children: renderFileIcon(file.name) }),
1867
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1868
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-base", title: file.name, children: file.name }),
1869
+ folder && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-sm text-muted-foreground", children: [
1870
+ /* @__PURE__ */ jsx(FolderOpen, { className: "size-3" }),
1871
+ folder
1872
+ ] })
1873
+ ] }),
1874
+ /* @__PURE__ */ jsx(
1875
+ Button,
1876
+ {
1877
+ variant: "ghost",
1878
+ size: "icon",
1879
+ onClick: () => onRemoveFile(index),
1880
+ "aria-label": l.remove(file.name),
1881
+ className: "size-6 shrink-0 pointer-coarse:size-11",
1882
+ "data-testid": testIdOr(testIds?.remove?.(index), {
1883
+ testId: `remove-file-button-${index}`
1884
+ }),
1885
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
1886
+ }
1887
+ )
1888
+ ]
1889
+ },
1890
+ index
1891
+ );
1892
+ }) })
1893
+ ]
1894
+ }
1895
+ );
1896
+ }
1897
+ function UploadProgress({
1898
+ fileCount,
1899
+ entityLabel,
1900
+ percentage,
1901
+ batchInfo,
1902
+ labels,
1903
+ testId
1904
+ }) {
1905
+ const l = mergeUploadLabels(labels);
1906
+ const isComplete = percentage >= 100;
1907
+ return /* @__PURE__ */ jsxs("div", { className: "text-center", "data-testid": testIdOr(testId, { testId: "upload-progress" }), children: [
1908
+ /* @__PURE__ */ jsx("h3", { className: "mb-2 text-lg font-medium", children: isComplete ? l.uploadComplete(entityLabel) : l.uploading(fileCount, entityLabel) }),
1909
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto mb-4 max-w-md", children: [
1910
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1911
+ /* @__PURE__ */ jsx(Progress, { value: percentage, className: "h-3" }),
1912
+ batchInfo && batchInfo.total > 1 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0", "aria-hidden": "true", children: Array.from({ length: batchInfo.total - 1 }, (_, i) => /* @__PURE__ */ jsx(
1913
+ "span",
1914
+ {
1915
+ className: "absolute top-0 h-full w-px bg-background/50",
1916
+ style: { left: `${(i + 1) / batchInfo.total * 100}%` }
1917
+ },
1918
+ i
1919
+ )) })
1920
+ ] }),
1921
+ /* @__PURE__ */ jsx("div", { className: "mt-2 flex items-center justify-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxs("span", { children: [
1922
+ Math.round(percentage),
1923
+ "%"
1924
+ ] }) })
1925
+ ] }),
1926
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: isComplete ? l.uploadedIndexing : l.afterUpload })
1927
+ ] });
1928
+ }
1929
+ var DEFAULT_UPLOAD_FOOTER_LABELS = {
1930
+ skipDuplicates: "Skip duplicates",
1931
+ skipDuplicatesActive: "Skip identical files already in the workspace",
1932
+ skipDuplicatesInactive: "Allow uploading identical files",
1933
+ autoRename: "Auto-rename",
1934
+ autoRenameActive: "Generate a descriptive title via AI instead of using the filename",
1935
+ autoRenameInactive: "Keep the original filename",
1936
+ titleInstruction: "Title instruction",
1937
+ titleInstructionPlaceholder: "Optional: how should AI name these documents?"
1938
+ };
1939
+ function UploadContainer({
1940
+ children,
1941
+ className = "p-4 sm:p-6",
1942
+ testId
1943
+ }) {
1944
+ return /* @__PURE__ */ jsx(
1945
+ "div",
1946
+ {
1947
+ className,
1948
+ "data-slot": "upload-container",
1949
+ "data-testid": testIdOr(testId, { testId: "upload-container" }),
1950
+ children
1951
+ }
1952
+ );
1953
+ }
1954
+ function UploadShareNote({
1955
+ description,
1956
+ children,
1957
+ compact = false,
1958
+ testId
1959
+ }) {
1960
+ const id = testIdOr(testId, { testId: "share-toggle" });
1961
+ if (compact) {
1962
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
1963
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { "data-testid": id, children }) }),
1964
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "max-w-xs", children: /* @__PURE__ */ jsx("p", { className: "text-sm", children: description }) })
1965
+ ] });
1966
+ }
1967
+ return /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-muted p-4", "data-testid": id, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
1968
+ children,
1969
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
1970
+ ] }) });
1971
+ }
1972
+ var TOGGLE_COLORS = { text: "text-primary", bg: "bg-primary" };
1973
+ function UploadFooterBar({
1974
+ skipDuplicates,
1975
+ onSkipDuplicatesChange,
1976
+ autoRename,
1977
+ onAutoRenameChange,
1978
+ autoRenameInstruction,
1979
+ onAutoRenameInstructionChange,
1980
+ shareToggle,
1981
+ children,
1982
+ className,
1983
+ labels,
1984
+ testIds
1985
+ }) {
1986
+ const l = { ...DEFAULT_UPLOAD_FOOTER_LABELS, ...labels };
1987
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1988
+ /* @__PURE__ */ jsxs(
1989
+ "div",
1990
+ {
1991
+ className: cn(
1992
+ "flex shrink-0 flex-wrap items-center gap-x-3 gap-y-2 border-t border-border bg-background px-4 py-3 sm:px-6",
1993
+ className
1994
+ ),
1995
+ "data-testid": testIdOr(testIds?.bar, { testId: "upload-footer-bar" }),
1996
+ children: [
1997
+ /* @__PURE__ */ jsx(
1998
+ IconToggle,
1999
+ {
2000
+ icon: CopyX,
2001
+ isActive: skipDuplicates,
2002
+ onToggle: onSkipDuplicatesChange,
2003
+ colors: TOGGLE_COLORS,
2004
+ showLabel: true,
2005
+ label: l.skipDuplicates,
2006
+ tooltipActive: l.skipDuplicatesActive,
2007
+ tooltipInactive: l.skipDuplicatesInactive,
2008
+ "data-testid": testIdOr(testIds?.skipDuplicates, { testId: "toggle-skip-duplicates" })
2009
+ }
2010
+ ),
2011
+ /* @__PURE__ */ jsx(
2012
+ IconToggle,
2013
+ {
2014
+ icon: Sparkles,
2015
+ isActive: autoRename,
2016
+ onToggle: onAutoRenameChange,
2017
+ colors: TOGGLE_COLORS,
2018
+ showLabel: true,
2019
+ label: l.autoRename,
2020
+ tooltipActive: l.autoRenameActive,
2021
+ tooltipInactive: l.autoRenameInactive,
2022
+ "data-testid": testIdOr(testIds?.autoRename, { testId: "toggle-auto-rename" })
2023
+ }
2024
+ ),
2025
+ /* @__PURE__ */ jsx("div", { className: "hidden flex-1 sm:block" }),
2026
+ shareToggle,
2027
+ /* @__PURE__ */ jsx("div", { className: "w-full sm:w-auto max-sm:[&>button]:w-full", children })
2028
+ ]
2029
+ }
2030
+ ),
2031
+ autoRename && /* @__PURE__ */ jsxs(
2032
+ "div",
2033
+ {
2034
+ className: "shrink-0 border-t border-border bg-background px-4 py-3 sm:px-6",
2035
+ "data-testid": testIdOr(testIds?.settingsPanel, { testId: "upload-settings-panel" }),
2036
+ children: [
2037
+ /* @__PURE__ */ jsx("label", { htmlFor: "auto-rename-instruction", className: "text-sm font-medium", children: l.titleInstruction }),
2038
+ /* @__PURE__ */ jsx(
2039
+ Textarea,
2040
+ {
2041
+ id: "auto-rename-instruction",
2042
+ value: autoRenameInstruction,
2043
+ onChange: (e) => onAutoRenameInstructionChange(e.target.value),
2044
+ placeholder: l.titleInstructionPlaceholder,
2045
+ className: "mt-1 min-h-[60px] text-sm",
2046
+ "data-testid": testIdOr(testIds?.instruction, { testId: "auto-rename-instruction" })
2047
+ }
2048
+ )
2049
+ ]
2050
+ }
2051
+ )
2052
+ ] });
2053
+ }
2054
+ function ShareToggle({
2055
+ isVisible,
2056
+ onToggle,
2057
+ workspaceEntityLabel,
2058
+ compact = false
2059
+ }) {
2060
+ const doc = getEntityLabel("DOCUMENT", "PLURAL").toLowerCase();
2061
+ const workspace = workspaceEntityLabel.toLowerCase();
2062
+ const description = isVisible ? `Uploaded ${doc} will be shared with this ${workspace} \u2014 everyone in it, now and in the future.` : `Uploaded ${doc} stay private to you \u2014 not shared with this ${workspace}. You can change this later.`;
2063
+ const audience = visibilityAudience(isVisible, workspaceEntityLabel);
2064
+ const toggle = /* @__PURE__ */ jsx(
2065
+ VisibilityToggle,
2066
+ {
2067
+ items: { shared: isVisible },
2068
+ onToggle,
2069
+ showLabel: true,
2070
+ label: audience,
2071
+ showTooltip: false
2072
+ }
2073
+ );
2074
+ if (compact) {
2075
+ const summary = isVisible ? `Everyone in this ${workspace} can see uploaded ${doc}.` : `Only you can see uploaded ${doc}.`;
2076
+ return /* @__PURE__ */ jsxs("div", { "data-testid": "share-toggle", className: "flex max-w-[15rem] flex-col items-end gap-1", children: [
2077
+ toggle,
2078
+ /* @__PURE__ */ jsx("p", { className: "text-right text-xs text-muted-foreground", children: summary })
2079
+ ] });
2080
+ }
2081
+ return /* @__PURE__ */ jsx(UploadShareNote, { description, compact: false, children: toggle });
2082
+ }
2083
+ var DEFAULT_UPLOAD_SOURCES_LABELS = {
2084
+ uploadFiles: "Choose files",
2085
+ uploadFolder: "Choose a folder"
2086
+ };
2087
+ function UploadFrame({ isCompact, isDimmed, className, children }) {
2088
+ return /* @__PURE__ */ jsx(
2089
+ "div",
2090
+ {
2091
+ "data-slot": "upload-frame",
2092
+ className: cn(
2093
+ "relative flex w-full flex-col items-center",
2094
+ isCompact ? "gap-4" : "gap-6",
2095
+ isDimmed && "opacity-50",
2096
+ className
2097
+ ),
2098
+ children
2099
+ }
2100
+ );
2101
+ }
2102
+ function UploadDropArea({
2103
+ isActive,
2104
+ isDisabled,
2105
+ isProcessing,
2106
+ processingMessage,
2107
+ isCompact,
2108
+ title,
2109
+ subtitle,
2110
+ onDrag,
2111
+ onDrop,
2112
+ onClick,
2113
+ actions,
2114
+ testId = "upload-dropzone-area"
2115
+ }) {
2116
+ const active = isActive && !isDisabled;
2117
+ return /* @__PURE__ */ jsxs(
2118
+ "div",
2119
+ {
2120
+ "data-slot": "upload-drop-area",
2121
+ "data-testid": testId,
2122
+ onDragEnter: onDrag,
2123
+ onDragLeave: onDrag,
2124
+ onDragOver: onDrag,
2125
+ onDrop,
2126
+ onClick,
2127
+ className: cn(
2128
+ "relative flex w-full flex-col items-center justify-center rounded-lg border text-center transition-colors",
2129
+ active ? "border-dashed border-primary bg-accent" : "border-border bg-card shadow-xs hover:border-ring/50 hover:bg-accent/40",
2130
+ isDisabled || isProcessing ? "cursor-not-allowed" : "cursor-pointer",
2131
+ isCompact ? "min-h-[150px] p-4" : "min-h-[200px] p-5",
2132
+ // Touch screens cannot drag files in: the area stays in the DOM (assistive tech, e2e) but
2133
+ // the "Choose files / folder" buttons are the whole affordance.
2134
+ "pointer-coarse:sr-only"
2135
+ ),
2136
+ children: [
2137
+ isProcessing && /* @__PURE__ */ jsxs(
2138
+ "div",
2139
+ {
2140
+ role: "status",
2141
+ className: "absolute inset-0 z-10 flex flex-col items-center justify-center rounded-lg bg-background/80",
2142
+ children: [
2143
+ /* @__PURE__ */ jsx(Loader2, { className: "mb-3 size-8 animate-spin text-primary" }),
2144
+ /* @__PURE__ */ jsx("div", { className: "progress-bar-processing mb-3 h-1.5 w-48 rounded-sm" }),
2145
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: processingMessage })
2146
+ ]
2147
+ }
2148
+ ),
2149
+ /* @__PURE__ */ jsx(
2150
+ "div",
2151
+ {
2152
+ className: cn(
2153
+ "mb-2 flex items-center justify-center rounded-full bg-muted text-muted-foreground",
2154
+ isCompact ? "size-11" : "size-10",
2155
+ active && "bg-primary/10 text-primary"
2156
+ ),
2157
+ children: /* @__PURE__ */ jsx(Upload, { className: "size-5" })
2158
+ }
2159
+ ),
2160
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-foreground", children: title }),
2161
+ subtitle && /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: subtitle }),
2162
+ actions
2163
+ ]
2164
+ }
2165
+ );
2166
+ }
2167
+ function UploadBrowseButtons({
2168
+ onFiles,
2169
+ onFolder,
2170
+ isDisabled,
2171
+ layout = "inline",
2172
+ labels
2173
+ }) {
2174
+ const l = { ...DEFAULT_UPLOAD_SOURCES_LABELS, ...labels };
2175
+ const isInline = layout === "inline";
2176
+ const size = isInline ? "sm" : "default";
2177
+ const width = isInline ? void 0 : "w-full";
2178
+ return /* @__PURE__ */ jsxs(
2179
+ "div",
2180
+ {
2181
+ "data-slot": "upload-browse-buttons",
2182
+ className: isInline ? "mt-3 flex flex-wrap items-center justify-center gap-2" : "grid grid-cols-1 gap-2 sm:grid-cols-2",
2183
+ children: [
2184
+ /* @__PURE__ */ jsxs(
2185
+ Button,
2186
+ {
2187
+ variant: isInline ? "outline" : "default",
2188
+ size,
2189
+ className: width,
2190
+ onClick: onFiles,
2191
+ disabled: isDisabled,
2192
+ "data-testid": "upload-files-button",
2193
+ children: [
2194
+ /* @__PURE__ */ jsx(FileText, { className: "mr-2 size-4" }),
2195
+ l.uploadFiles
2196
+ ]
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ jsxs(
2200
+ Button,
2201
+ {
2202
+ variant: "outline",
2203
+ size,
2204
+ className: width,
2205
+ onClick: onFolder,
2206
+ disabled: isDisabled,
2207
+ "data-testid": "upload-folder-button",
2208
+ children: [
2209
+ /* @__PURE__ */ jsx(FolderPlus, { className: "mr-2 size-4" }),
2210
+ l.uploadFolder
2211
+ ]
2212
+ }
2213
+ )
2214
+ ]
2215
+ }
2216
+ );
2217
+ }
2218
+ function UploadSourcePicker({ options, isDisabled }) {
2219
+ return /* @__PURE__ */ jsx(
2220
+ "div",
2221
+ {
2222
+ "data-slot": "upload-source-picker",
2223
+ className: "flex w-full flex-col gap-2",
2224
+ "data-testid": "upload-source-picker",
2225
+ children: options.map((o) => {
2226
+ const button = /* @__PURE__ */ jsxs(
2227
+ Button,
2228
+ {
2229
+ variant: "outline",
2230
+ onClick: o.onClick,
2231
+ disabled: isDisabled,
2232
+ "data-testid": `upload-source-pick-${o.key}`,
2233
+ className: "h-auto w-full justify-start gap-3 px-4 py-3 text-left",
2234
+ children: [
2235
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground [&_svg]:size-5", children: o.icon }),
2236
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
2237
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: o.label }),
2238
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: o.description })
2239
+ ] })
2240
+ ]
2241
+ }
2242
+ );
2243
+ return /* @__PURE__ */ jsx("span", { className: "contents", children: o.wrap ? o.wrap(button) : button }, o.key);
2244
+ })
2245
+ }
2246
+ );
2247
+ }
2248
+ function UploadUrlButton({
2249
+ isCompact,
2250
+ isDisabled,
2251
+ label = "Add from URL",
2252
+ onClick
2253
+ }) {
2254
+ return /* @__PURE__ */ jsxs(
2255
+ Button,
2256
+ {
2257
+ variant: "outline",
2258
+ size: isCompact ? "sm" : "default",
2259
+ disabled: isDisabled,
2260
+ onClick,
2261
+ "data-testid": "upload-url-button",
2262
+ children: [
2263
+ /* @__PURE__ */ jsx(Globe, { className: "mr-2 size-4" }),
2264
+ label
2265
+ ]
2266
+ }
2267
+ );
2268
+ }
2269
+ function UploadSourceSection({
2270
+ testId,
2271
+ className,
2272
+ isIsolated,
2273
+ children
2274
+ }) {
2275
+ return /* @__PURE__ */ jsx(
2276
+ "div",
2277
+ {
2278
+ "data-slot": "upload-source-section",
2279
+ "data-testid": testId,
2280
+ className,
2281
+ onClick: isIsolated ? (e) => e.stopPropagation() : void 0,
2282
+ children
2283
+ }
2284
+ );
2285
+ }
2286
+ var DEFAULT_URL_INPUT_DIALOG_LABELS = {
2287
+ title: "Add from the internet",
2288
+ description: "Enter one or more URLs to download documents \u2014 paste several separated by commas or new lines.",
2289
+ supports: "Supports PDF, Word, PowerPoint, Excel, RTF, TXT, MD, HTML, ZIP, and audio/video links (YouTube et al.).",
2290
+ url: (position) => position === null ? "URL " : `URL ${position}`,
2291
+ placeholder: "https://example.com/document.pdf or a YouTube / audio / video link",
2292
+ removeUrl: (position) => `Remove URL ${position}`,
2293
+ addAnother: "Add Another URL",
2294
+ adding: "Adding\u2026",
2295
+ addDocuments: "Add Documents",
2296
+ mediaPreview: "Media preview"
2297
+ };
2298
+ function UrlInputDialogView({
2299
+ open,
2300
+ onOpenChange,
2301
+ trigger,
2302
+ title,
2303
+ urls,
2304
+ errors,
2305
+ onUrlChange,
2306
+ onUrlPaste,
2307
+ onAddUrl,
2308
+ onRemoveUrl,
2309
+ renderPreview,
2310
+ submitError,
2311
+ isSubmitting,
2312
+ onSubmit,
2313
+ labels,
2314
+ testIds
2315
+ }) {
2316
+ const l = { ...DEFAULT_URL_INPUT_DIALOG_LABELS, ...labels };
2317
+ return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange, children: [
2318
+ trigger !== void 0 && /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: trigger }),
2319
+ /* @__PURE__ */ jsxs(
2320
+ DialogContent,
2321
+ {
2322
+ presentation: "sheet",
2323
+ className: "flex flex-col gap-0 overflow-hidden p-0 sm:max-w-[525px]",
2324
+ "data-testid": testIdOr(testIds?.modal, { testId: "upload-modal-internet" }),
2325
+ children: [
2326
+ /* @__PURE__ */ jsx(ModalHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: title ?? l.title }) }),
2327
+ /* @__PURE__ */ jsxs("div", { className: "px-6 pb-6", children: [
2328
+ /* @__PURE__ */ jsxs(DialogDescription, { className: "pt-4 text-sm text-muted-foreground", children: [
2329
+ l.description,
2330
+ /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs", children: l.supports })
2331
+ ] }),
2332
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 py-4", children: [
2333
+ urls.map((url, index) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2334
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2335
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
2336
+ /* @__PURE__ */ jsx(Label, { htmlFor: `url-${index}`, className: "text-sm", children: l.url(urls.length > 1 ? index + 1 : null) }),
2337
+ /* @__PURE__ */ jsx(
2338
+ Input,
2339
+ {
2340
+ id: `url-${index}`,
2341
+ type: "text",
2342
+ placeholder: l.placeholder,
2343
+ value: url,
2344
+ onChange: (e) => onUrlChange(index, e.target.value),
2345
+ onPaste: (e) => {
2346
+ e.preventDefault();
2347
+ onUrlPaste(index, e.clipboardData.getData("text"));
2348
+ },
2349
+ className: cn(errors[index] && "border-red-border")
2350
+ }
2351
+ ),
2352
+ errors[index] && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-red-text", children: errors[index] })
2353
+ ] }),
2354
+ urls.length > 1 && /* @__PURE__ */ jsx(
2355
+ Button,
2356
+ {
2357
+ type: "button",
2358
+ variant: "ghost",
2359
+ size: "icon",
2360
+ onClick: () => onRemoveUrl(index),
2361
+ className: "mt-6",
2362
+ "aria-label": l.removeUrl(index + 1),
2363
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
2364
+ }
2365
+ )
2366
+ ] }),
2367
+ renderPreview?.(url, index)
2368
+ ] }, index)),
2369
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onAddUrl, className: "w-full", children: [
2370
+ /* @__PURE__ */ jsx(Plus, { className: "mr-2 h-4 w-4" }),
2371
+ l.addAnother
2372
+ ] })
2373
+ ] }),
2374
+ submitError && /* @__PURE__ */ jsx(
2375
+ "p",
2376
+ {
2377
+ className: "text-sm text-red-text",
2378
+ "data-testid": testIdOr(testIds?.submitError, { testId: "url-submit-error" }),
2379
+ children: submitError
2380
+ }
2381
+ ),
2382
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col-reverse sm:flex-row sm:justify-end gap-2", children: /* @__PURE__ */ jsx(
2383
+ Button,
2384
+ {
2385
+ onClick: onSubmit,
2386
+ disabled: isSubmitting,
2387
+ "data-testid": testIdOr(testIds?.submit, { testId: "url-submit-button" }),
2388
+ children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
2389
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2390
+ l.adding
2391
+ ] }) : l.addDocuments
2392
+ }
2393
+ ) })
2394
+ ] })
2395
+ ]
2396
+ }
2397
+ )
2398
+ ] });
2399
+ }
2400
+ function UrlDialogTitle({ icon, workspaceName, labels }) {
2401
+ const l = { ...DEFAULT_URL_INPUT_DIALOG_LABELS, ...labels };
2402
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center", icon ? "gap-3" : "gap-2"), children: [
2403
+ icon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: icon }),
2404
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", title: workspaceName, children: workspaceName }),
2405
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-muted-foreground", children: "/" }),
2406
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: l.title })
2407
+ ] });
2408
+ }
2409
+ function MediaPreviewView({ kind, src, testId, labels }) {
2410
+ const l = { ...DEFAULT_URL_INPUT_DIALOG_LABELS, ...labels };
2411
+ return /* @__PURE__ */ jsxs("div", { className: "mt-2 overflow-hidden rounded-md border", "data-testid": testId, children: [
2412
+ kind === "youtube" && /* @__PURE__ */ jsx(
2413
+ "iframe",
2414
+ {
2415
+ className: "aspect-video w-full",
2416
+ src,
2417
+ title: l.mediaPreview,
2418
+ allow: "accelerometer; encrypted-media; picture-in-picture",
2419
+ allowFullScreen: true
2420
+ }
2421
+ ),
2422
+ kind === "video" && /* @__PURE__ */ jsx("video", { className: "max-h-56 w-full bg-black", src, controls: true, preload: "metadata" }),
2423
+ kind === "audio" && /* @__PURE__ */ jsx("audio", { className: "w-full p-2", src, controls: true, preload: "metadata" })
2424
+ ] });
2425
+ }
2426
+
2427
+ // src/files/media-preview.ts
2428
+ var MEDIA_HOSTS = [
2429
+ "youtube.com",
2430
+ "youtu.be",
2431
+ "vimeo.com",
2432
+ "soundcloud.com",
2433
+ "dailymotion.com",
2434
+ "twitch.tv"
2435
+ ];
2436
+ var VIDEO_EXTENSIONS = [".mp4", ".mpeg", ".mpg", ".webm", ".mov", ".avi", ".mkv", ".ogv"];
2437
+ var AUDIO_EXTENSIONS = [".mp3", ".wav", ".m4a", ".aac", ".flac", ".ogg", ".oga"];
2438
+ function normalizeHost(hostname) {
2439
+ return hostname.toLowerCase().replace(/^(www|m)\./, "");
2440
+ }
2441
+ function getYoutubeId(url) {
2442
+ const host = normalizeHost(url.hostname);
2443
+ if (host === "youtu.be") return url.pathname.slice(1).split("/")[0] || null;
2444
+ if (host === "youtube.com" || host.endsWith(".youtube.com")) {
2445
+ if (url.pathname === "/watch") return url.searchParams.get("v");
2446
+ const segments = url.pathname.split("/").filter(Boolean);
2447
+ if (segments[0] === "embed" || segments[0] === "shorts" || segments[0] === "v") {
2448
+ return segments[1] ?? null;
2449
+ }
2450
+ }
2451
+ return null;
2452
+ }
2453
+ function getExtension(pathname) {
2454
+ const lower = pathname.toLowerCase();
2455
+ const lastDot = lower.lastIndexOf(".");
2456
+ const lastSlash = lower.lastIndexOf("/");
2457
+ return lastDot > lastSlash ? lower.slice(lastDot) : "";
2458
+ }
2459
+ function getMediaPreview(rawUrl) {
2460
+ let url;
2461
+ try {
2462
+ url = new URL(rawUrl);
2463
+ } catch {
2464
+ return null;
2465
+ }
2466
+ const youtubeId = getYoutubeId(url);
2467
+ if (youtubeId) {
2468
+ return { kind: "youtube", embedUrl: `https://www.youtube.com/embed/${youtubeId}` };
2469
+ }
2470
+ const extension = getExtension(url.pathname);
2471
+ if (VIDEO_EXTENSIONS.includes(extension)) return { kind: "video", src: rawUrl };
2472
+ if (AUDIO_EXTENSIONS.includes(extension)) return { kind: "audio", src: rawUrl };
2473
+ return null;
2474
+ }
2475
+ function isMediaUrl(rawUrl) {
2476
+ if (getMediaPreview(rawUrl)) return true;
2477
+ try {
2478
+ const host = normalizeHost(new URL(rawUrl).hostname);
2479
+ return MEDIA_HOSTS.some((mediaHost) => host === mediaHost || host.endsWith(`.${mediaHost}`));
2480
+ } catch {
2481
+ return false;
2482
+ }
2483
+ }
2484
+ function UrlInputDialog({
2485
+ onUrlsSubmit,
2486
+ trigger,
2487
+ processing = false,
2488
+ open: controlledOpen,
2489
+ onOpenChange,
2490
+ titleWorkspaceName,
2491
+ titleIcon
2492
+ }) {
2493
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
2494
+ const open = controlledOpen ?? uncontrolledOpen;
2495
+ const setOpen = (next) => {
2496
+ onOpenChange?.(next);
2497
+ if (controlledOpen === void 0) setUncontrolledOpen(next);
2498
+ };
2499
+ const [urls, setUrls] = useState([""]);
2500
+ const [errors, setErrors] = useState([]);
2501
+ const [submitting, setSubmitting] = useState(false);
2502
+ const [submitError, setSubmitError] = useState(null);
2503
+ const preprocessUrl = (url) => {
2504
+ let processedUrl = url.trim();
2505
+ if (processedUrl && !processedUrl.match(/^https?:\/\//i)) {
2506
+ processedUrl = "https://" + processedUrl;
2507
+ }
2508
+ try {
2509
+ new URL(processedUrl);
2510
+ processedUrl = processedUrl.replace(/ /g, "%20");
2511
+ return processedUrl;
2512
+ } catch {
2513
+ return processedUrl.replace(/ /g, "%20");
2514
+ }
2515
+ };
2516
+ const validateUrl = (url) => {
2517
+ try {
2518
+ const urlObj = new URL(url);
2519
+ if (isMediaUrl(url)) {
2520
+ return { valid: true };
2521
+ }
2522
+ const pathname = urlObj.pathname.toLowerCase();
2523
+ const supportedExtensions = [
2524
+ ".pdf",
2525
+ ".txt",
2526
+ ".md",
2527
+ ".html",
2528
+ ".doc",
2529
+ ".docx",
2530
+ ".rtf",
2531
+ ".ppt",
2532
+ ".pptx",
2533
+ ".xls",
2534
+ ".xlsx",
2535
+ // .zip is fetched from the URL by the browser and extracted by the
2536
+ // frontend zip worker; each entry is then uploaded as a normal file.
2537
+ ".zip"
2538
+ ];
2539
+ const hasFileExtension = pathname.includes(".") && pathname.lastIndexOf(".") > pathname.lastIndexOf("/");
2540
+ const hasValidExtension = !hasFileExtension || supportedExtensions.some((ext) => pathname.endsWith(ext));
2541
+ if (!hasValidExtension) {
2542
+ return {
2543
+ valid: false,
2544
+ error: "File type not supported. Supported formats: PDF, DOC(X), PPT(X), XLS(X), RTF, TXT, MD, HTML, ZIP."
2545
+ };
2546
+ }
2547
+ return { valid: true };
2548
+ } catch {
2549
+ return { valid: false, error: "Invalid URL format" };
2550
+ }
2551
+ };
2552
+ const addUrlField = () => {
2553
+ setUrls([...urls, ""]);
2554
+ setErrors([...errors, ""]);
2555
+ };
2556
+ const removeUrlField = (index) => {
2557
+ setUrls(urls.filter((_, i) => i !== index));
2558
+ setErrors(errors.filter((_, i) => i !== index));
2559
+ };
2560
+ const updateUrl = (index, value) => {
2561
+ if (submitError) setSubmitError(null);
2562
+ const delimiters = /[,\n]+/;
2563
+ if (delimiters.test(value)) {
2564
+ const urlList = value.split(delimiters).map((u) => u.trim()).filter((u) => u.length > 0);
2565
+ if (urlList.length > 1) {
2566
+ const newUrls2 = [...urls];
2567
+ newUrls2.splice(index, 1, ...urlList);
2568
+ if (index === urls.length - 1) {
2569
+ newUrls2.push("");
2570
+ }
2571
+ setUrls(newUrls2);
2572
+ const newErrors = [...errors];
2573
+ urlList.forEach((_, i) => {
2574
+ newErrors[index + i] = "";
2575
+ });
2576
+ setErrors(newErrors);
2577
+ return;
2578
+ }
2579
+ }
2580
+ const newUrls = [...urls];
2581
+ newUrls[index] = value;
2582
+ setUrls(newUrls);
2583
+ if (errors[index]) {
2584
+ const newErrors = [...errors];
2585
+ newErrors[index] = "";
2586
+ setErrors(newErrors);
2587
+ }
2588
+ };
2589
+ const handleSubmit = async () => {
2590
+ const newErrors = [];
2591
+ const validUrls = [];
2592
+ urls.forEach((url, index) => {
2593
+ const trimmedUrl = url.trim();
2594
+ if (!trimmedUrl) {
2595
+ newErrors[index] = "URL is required";
2596
+ } else {
2597
+ const processedUrl = preprocessUrl(trimmedUrl);
2598
+ const validation = validateUrl(processedUrl);
2599
+ if (!validation.valid) {
2600
+ newErrors[index] = validation.error || "Invalid URL";
2601
+ } else {
2602
+ newErrors[index] = "";
2603
+ validUrls.push(processedUrl);
2604
+ }
2605
+ }
2606
+ });
2607
+ setErrors(newErrors);
2608
+ if (validUrls.length > 0 && !newErrors.some((error) => error)) {
2609
+ setSubmitError(null);
2610
+ setSubmitting(true);
2611
+ let ok;
2612
+ try {
2613
+ ok = await onUrlsSubmit(validUrls);
2614
+ } catch {
2615
+ ok = false;
2616
+ }
2617
+ setSubmitting(false);
2618
+ if (ok === false) {
2619
+ setSubmitError("Failed to upload documents from URLs. Please check the URLs and try again.");
2620
+ return;
2621
+ }
2622
+ setOpen(false);
2623
+ setUrls([""]);
2624
+ setErrors([]);
2625
+ }
2626
+ };
2627
+ const showTrigger = trigger !== void 0 || controlledOpen === void 0;
2628
+ const title = titleWorkspaceName ? /* @__PURE__ */ jsx(UrlDialogTitle, { icon: titleIcon, workspaceName: titleWorkspaceName }) : void 0;
2629
+ return /* @__PURE__ */ jsx(
2630
+ UrlInputDialogView,
2631
+ {
2632
+ open,
2633
+ onOpenChange: setOpen,
2634
+ trigger: showTrigger ? trigger || /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", disabled: processing, children: [
2635
+ /* @__PURE__ */ jsx(Link, { className: "w-4 h-4 mr-2" }),
2636
+ processing ? "Processing..." : "Add from URL"
2637
+ ] }) : void 0,
2638
+ title,
2639
+ urls,
2640
+ errors,
2641
+ onUrlChange: updateUrl,
2642
+ onUrlPaste: updateUrl,
2643
+ onAddUrl: addUrlField,
2644
+ onRemoveUrl: removeUrlField,
2645
+ renderPreview: (url, index) => {
2646
+ const trimmed = url.trim();
2647
+ const preview = trimmed ? getMediaPreview(preprocessUrl(trimmed)) : null;
2648
+ if (!preview) return null;
2649
+ return /* @__PURE__ */ jsx(
2650
+ MediaPreviewView,
2651
+ {
2652
+ kind: preview.kind,
2653
+ src: preview.kind === "youtube" ? preview.embedUrl : preview.src,
2654
+ testId: `url-media-preview-${index}`
2655
+ }
2656
+ );
2657
+ },
2658
+ submitError,
2659
+ isSubmitting: submitting,
2660
+ onSubmit: handleSubmit
2661
+ }
2662
+ );
2663
+ }
2664
+ var POINTS = [
2665
+ {
2666
+ icon: KeyRound,
2667
+ heading: "Only you hold the key",
2668
+ body: "You choose who to invite to a workspace and the role they hold. Each workspace is encrypted with its own key, held only by its members \u2014 so no one outside it, including ARBI administrators, can access its confidential data."
2669
+ },
2670
+ {
2671
+ icon: Eye,
2672
+ heading: "You decide what to share",
2673
+ body: "Within a workspace, conversations are private by default and documents are shared with collaborators. Anything you keep private stays invisible to other members, even the owner."
2674
+ },
2675
+ {
2676
+ icon: ShieldCheck,
2677
+ heading: "Your data stays in the UK",
2678
+ body: "By default, the AI that processes your files runs on ARBI's own infrastructure in the United Kingdom. Third-party models (OpenAI, Anthropic or Google) are optional; we select providers with the strongest data-protection terms, but recommend reviewing each provider's data-retention policy before enabling one."
2679
+ },
2680
+ {
2681
+ icon: BrainCircuit,
2682
+ heading: "We never train AI on your data",
2683
+ body: "Your files and conversations are never used to train AI models."
2684
+ }
2685
+ ];
2686
+ function PrivacyExplainerModal() {
2687
+ const [open, setOpen] = useState(false);
2688
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2689
+ /* @__PURE__ */ jsxs(
2690
+ Button,
2691
+ {
2692
+ type: "button",
2693
+ variant: "ghost",
2694
+ size: "sm",
2695
+ onClick: () => setOpen(true),
2696
+ className: "gap-1.5 text-primary hover:text-primary",
2697
+ "data-testid": "privacy-explainer-button",
2698
+ children: [
2699
+ /* @__PURE__ */ jsx(Info, { className: "w-4 h-4" }),
2700
+ "Privacy Guarantee",
2701
+ /* @__PURE__ */ jsx(ChevronRight, { className: "w-3.5 h-3.5 opacity-70" })
2702
+ ]
2703
+ }
2704
+ ),
2705
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", "data-testid": "privacy-explainer-dialog", children: [
2706
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
2707
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
2708
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "h-5 w-5 text-primary" }),
2709
+ "Your data, your keys"
2710
+ ] }),
2711
+ /* @__PURE__ */ jsx(DialogDescription, { children: "How ARBI protects confidentiality." })
2712
+ ] }),
2713
+ /* @__PURE__ */ jsx("div", { className: "space-y-5 py-1", children: POINTS.map(({ icon: Icon, heading, body }) => /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
2714
+ /* @__PURE__ */ jsx(Icon, { className: "mt-0.5 h-5 w-5 shrink-0 text-primary" }),
2715
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
2716
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold leading-none", children: heading }),
2717
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: body })
2718
+ ] })
2719
+ ] }, heading)) }),
2720
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 pt-1", children: [
2721
+ /* @__PURE__ */ jsx(
2722
+ "a",
2723
+ {
2724
+ href: "https://arbicity.com/ARBI-Security-Whitepaper.pdf",
2725
+ target: "_blank",
2726
+ rel: "noopener noreferrer",
2727
+ className: "text-sm font-medium text-primary hover:underline",
2728
+ "data-testid": "privacy-explainer-whitepaper-link",
2729
+ children: "Full security details \u2192"
2730
+ }
2731
+ ),
2732
+ /* @__PURE__ */ jsx(
2733
+ Button,
2734
+ {
2735
+ type: "button",
2736
+ onClick: () => setOpen(false),
2737
+ "data-testid": "privacy-explainer-dismiss",
2738
+ children: "Got it"
2739
+ }
2740
+ )
2741
+ ] })
2742
+ ] }) })
2743
+ ] });
2744
+ }
1717
2745
 
1718
- export { CHROME, DEFAULT_EXPLORER_LABELS, DEFAULT_FOLDER_FILTER_LABELS, DEFAULT_FOLDER_PICKER_LABELS, ExplorerDragPreview, ExplorerDropOverlay, ExplorerEmpty, ExplorerFileTile, ExplorerFileTooltip, ExplorerFolderTile, ExplorerFrame, ExplorerLoadingTiles, ExplorerMarquee, ExplorerScroller, ExplorerStage, FailedStageIcon, FieldAttribution, FieldFolderSelect, FieldFolderValue, FieldLabelsSelect, FieldNativeSelect, FieldValue, FieldWithAttribution, FilesFilterBar, FilesModalFooter, FilesTagsButton, FilesViewCenter, FilterButton, FolderBreadcrumb, FolderCell, FolderCommandList, FolderFilterButton, FolderPickerRow, FolderProviderIcon, FolderTree, LINE_PX, MAX_LINES, MIN_LINES, RECENTLY_DELETED_HINT, RECENTLY_DELETED_HINT_PLURAL, ROW_MIN, RowDensityControl, StatusPill, VendorLogo, buildExplorerEntries, canChangeVisibility, canDeleteDocument, canEditDocumentFields, collectFolderDocs, confirmHideFromMembers, formatFolderSegment, getExplorerDisplayName, hasFolderProviderIcon, isExplorerDocument, joinFolderPath, linesForRowHeight, parseFolderSegments, rowHeightForLines, searchExplorerDocuments, shareStateOf, shareTooltipFor, sortExplorerFiles, visibilityAudience, visibilityToggleTooltip };
1719
- //# sourceMappingURL=chunk-6HMCF4AJ.js.map
1720
- //# sourceMappingURL=chunk-6HMCF4AJ.js.map
2746
+ export { CHROME, DEFAULT_EXPLORER_LABELS, DEFAULT_FOLDER_FILTER_LABELS, DEFAULT_FOLDER_PICKER_LABELS, DEFAULT_UPLOAD_FOOTER_LABELS, DEFAULT_UPLOAD_PARTS_LABELS, DEFAULT_UPLOAD_SOURCES_LABELS, DEFAULT_URL_INPUT_DIALOG_LABELS, ExplorerDragPreview, ExplorerDropOverlay, ExplorerEmpty, ExplorerFileTile, ExplorerFileTooltip, ExplorerFolderTile, ExplorerFrame, ExplorerLoadingTiles, ExplorerMarquee, ExplorerScroller, ExplorerStage, FailedStageIcon, FieldAttribution, FieldFolderSelect, FieldFolderValue, FieldLabelsSelect, FieldNativeSelect, FieldValue, FieldWithAttribution, FilesFilterBar, FilesModalFooter, FilesTagsButton, FilesViewCenter, FilterButton, FolderBreadcrumb, FolderCell, FolderCommandList, FolderFilterButton, FolderPickerRow, FolderProviderIcon, FolderTree, LINE_PX, MAX_LINES, MIN_LINES, MediaPreviewView, PrivacyExplainerModal, RECENTLY_DELETED_HINT, RECENTLY_DELETED_HINT_PLURAL, ROW_MIN, RejectedFilesList, RowDensityControl, SelectedFilesList, ShareToggle, StatusPill, UploadBrowseButtons, UploadContainer, UploadDropArea, UploadErrorMessage, UploadFooterBar, UploadFrame, UploadProgress, UploadShareNote, UploadSourcePicker, UploadSourceSection, UploadUrlButton, UrlDialogTitle, UrlInputDialog, UrlInputDialogView, VendorLogo, buildExplorerEntries, canChangeVisibility, canDeleteDocument, canEditDocumentFields, collectFolderDocs, confirmHideFromMembers, formatFolderSegment, getExplorerDisplayName, getMediaPreview, hasFolderProviderIcon, isExplorerDocument, isMediaUrl, joinFolderPath, linesForRowHeight, parseFolderSegments, rowHeightForLines, searchExplorerDocuments, shareStateOf, shareTooltipFor, sortExplorerFiles, visibilityAudience, visibilityToggleTooltip };
2747
+ //# sourceMappingURL=chunk-5ZIYPZYE.js.map
2748
+ //# sourceMappingURL=chunk-5ZIYPZYE.js.map