@burdenoff/microfe-files 2026.525.3 → 2026.527.2

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 (74) hide show
  1. package/dist/files/components/ActionMenu.js +3 -1
  2. package/dist/files/components/ActionMenu.js.map +1 -1
  3. package/dist/files/components/ContextMenu.js +16 -112
  4. package/dist/files/components/ContextMenu.js.map +1 -1
  5. package/dist/files/components/CopyDialog.js +8 -0
  6. package/dist/files/components/CopyDialog.js.map +1 -1
  7. package/dist/files/components/EmptyState.js +2 -0
  8. package/dist/files/components/EmptyState.js.map +1 -1
  9. package/dist/files/components/FileBrowser.js +192 -171
  10. package/dist/files/components/FileBrowser.js.map +1 -1
  11. package/dist/files/components/FileCard.js +4 -0
  12. package/dist/files/components/FileCard.js.map +1 -1
  13. package/dist/files/components/FileInfoPanel.js +2 -0
  14. package/dist/files/components/FileInfoPanel.js.map +1 -1
  15. package/dist/files/components/FileListItem.js +6 -0
  16. package/dist/files/components/FileListItem.js.map +1 -1
  17. package/dist/files/components/FilePreviewModal.js +9 -3
  18. package/dist/files/components/FilePreviewModal.js.map +1 -1
  19. package/dist/files/components/FileVersionPanel.js +5 -0
  20. package/dist/files/components/FileVersionPanel.js.map +1 -1
  21. package/dist/files/components/FolderCard.js +147 -158
  22. package/dist/files/components/FolderCard.js.map +1 -1
  23. package/dist/files/components/MoveDialog.js +17 -12
  24. package/dist/files/components/MoveDialog.js.map +1 -1
  25. package/dist/files/components/SearchBar.js +4 -0
  26. package/dist/files/components/SearchBar.js.map +1 -1
  27. package/dist/files/components/ShareDialog.js +109 -104
  28. package/dist/files/components/ShareDialog.js.map +1 -1
  29. package/dist/files/components/TabBar.js +1 -0
  30. package/dist/files/components/TabBar.js.map +1 -1
  31. package/dist/files/components/Toast.js +1 -0
  32. package/dist/files/components/Toast.js.map +1 -1
  33. package/dist/files/components/UploadQueue.js +5 -0
  34. package/dist/files/components/UploadQueue.js.map +1 -1
  35. package/dist/files/components/contextMenuItems.js +103 -0
  36. package/dist/files/components/contextMenuItems.js.map +1 -0
  37. package/dist/files/components/viewers/AudioPlayer.js +4 -1
  38. package/dist/files/components/viewers/AudioPlayer.js.map +1 -1
  39. package/dist/files/components/viewers/CodeViewer.js +2 -0
  40. package/dist/files/components/viewers/CodeViewer.js.map +1 -1
  41. package/dist/files/components/viewers/ImageViewer.js +4 -0
  42. package/dist/files/components/viewers/ImageViewer.js.map +1 -1
  43. package/dist/files/components/viewers/PDFViewer.js +2 -0
  44. package/dist/files/components/viewers/PDFViewer.js.map +1 -1
  45. package/dist/files/components/viewers/TextViewer.js +4 -2
  46. package/dist/files/components/viewers/TextViewer.js.map +1 -1
  47. package/dist/files/components/viewers/VideoPlayer.js +5 -1
  48. package/dist/files/components/viewers/VideoPlayer.js.map +1 -1
  49. package/dist/files/hooks/useBatchOperations.js.map +1 -1
  50. package/dist/files/hooks/useCreateFolder.js +15 -7
  51. package/dist/files/hooks/useCreateFolder.js.map +1 -1
  52. package/dist/files/hooks/useFileVersions.js.map +1 -1
  53. package/dist/files/hooks/useMoveFile.js +1 -2
  54. package/dist/files/hooks/useMoveFile.js.map +1 -1
  55. package/dist/files/hooks/useMoveFolder.js +0 -2
  56. package/dist/files/hooks/useMoveFolder.js.map +1 -1
  57. package/dist/files/hooks/useStarredItems.js +4 -3
  58. package/dist/files/hooks/useStarredItems.js.map +1 -1
  59. package/dist/files/hooks/useToggleStar.js +10 -10
  60. package/dist/files/hooks/useToggleStar.js.map +1 -1
  61. package/dist/files/pages/BrowserPage.js +332 -302
  62. package/dist/files/pages/BrowserPage.js.map +1 -1
  63. package/dist/files/pages/ShareAccessPage.js +6 -5
  64. package/dist/files/pages/ShareAccessPage.js.map +1 -1
  65. package/dist/files/utils/index.js +1 -1
  66. package/dist/files/utils/index.js.map +1 -1
  67. package/dist/files/utils/quotaUtils.js +14 -0
  68. package/dist/files/utils/quotaUtils.js.map +1 -0
  69. package/dist/files/utils/thumbnail.js.map +1 -1
  70. package/dist/files.js +2 -1
  71. package/dist/translations/en.js +259 -0
  72. package/dist/translations/en.js.map +1 -0
  73. package/package.json +2 -5
  74. package/dist/files/components/viewers/index.js +0 -6
@@ -5,7 +5,6 @@ import { VideoPlayer as r } from "./viewers/VideoPlayer.js";
5
5
  import { AudioPlayer as i } from "./viewers/AudioPlayer.js";
6
6
  import { CodeViewer as a } from "./viewers/CodeViewer.js";
7
7
  import { TextViewer as o } from "./viewers/TextViewer.js";
8
- import "./viewers/index.js";
9
8
  import { useEffect as s } from "react";
10
9
  import { Download as c, Share2 as l, Star as u, Trash2 as d, X as f } from "lucide-react";
11
10
  import { jsx as p, jsxs as m } from "react/jsx-runtime";
@@ -71,13 +70,15 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
71
70
  }), /* @__PURE__ */ m("div", {
72
71
  className: "flex items-center gap-2",
73
72
  children: [
74
- w?.map((e, t) => /* @__PURE__ */ m("button", {
73
+ w?.map((e) => /* @__PURE__ */ m("button", {
74
+ type: "button",
75
75
  onClick: e.onClick,
76
76
  className: "flex items-center gap-1.5 px-3 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
77
77
  title: e.label,
78
78
  children: [e.icon, /* @__PURE__ */ p("span", { children: e.label })]
79
- }, t)),
79
+ }, e.label)),
80
80
  /* @__PURE__ */ p("button", {
81
+ type: "button",
81
82
  onClick: b,
82
83
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
83
84
  title: e(T, "files.common.actions.download", "Download"),
@@ -85,6 +86,7 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
85
86
  children: /* @__PURE__ */ p(c, { className: "size-5" })
86
87
  }),
87
88
  x && /* @__PURE__ */ p("button", {
89
+ type: "button",
88
90
  onClick: x,
89
91
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
90
92
  title: e(T, "files.common.actions.share", "Share"),
@@ -92,6 +94,7 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
92
94
  children: /* @__PURE__ */ p(l, { className: "size-5" })
93
95
  }),
94
96
  S && /* @__PURE__ */ p("button", {
97
+ type: "button",
95
98
  onClick: S,
96
99
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
97
100
  title: v.starred ? e(T, "files.preview.removeStar", "Remove star") : e(T, "files.preview.addStar", "Add star"),
@@ -99,6 +102,7 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
99
102
  children: /* @__PURE__ */ p(u, { className: `size-5 ${v.starred ? "fill-status-warning-text text-status-warning-text" : ""}` })
100
103
  }),
101
104
  C && /* @__PURE__ */ p("button", {
105
+ type: "button",
102
106
  onClick: C,
103
107
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
104
108
  title: e(T, "files.common.actions.delete", "Delete"),
@@ -106,6 +110,7 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
106
110
  children: /* @__PURE__ */ p(d, { className: "size-5" })
107
111
  }),
108
112
  /* @__PURE__ */ p("button", {
113
+ type: "button",
109
114
  onClick: y,
110
115
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200 ml-2",
111
116
  title: e(T, "files.common.actions.close", "Close"),
@@ -157,6 +162,7 @@ function v({ file: v, onClose: y, onDownload: b, onShare: x, onStar: S, onDelete
157
162
  children: v.mimeType
158
163
  }),
159
164
  /* @__PURE__ */ p("button", {
165
+ type: "button",
160
166
  onClick: b,
161
167
  className: "px-4 py-2 bg-action-primary-bg text-text-inverse rounded-lg hover:bg-action-primary-bg-hover transition-colors duration-200",
162
168
  children: e(T, "files.preview.downloadFile", "Download File")
@@ -1 +1 @@
1
- {"version":3,"file":"FilePreviewModal.js","names":[],"sources":["../../../src/files/components/FilePreviewModal.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { X, Download, Share2, Star, Trash2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n ImageViewer,\n PDFViewer,\n VideoPlayer,\n AudioPlayer,\n CodeViewer,\n TextViewer,\n} from \"./viewers\";\n\ninterface FilePreviewModalProps {\n file: FileItem;\n onClose: () => void;\n onDownload: () => void;\n onShare?: () => void;\n onStar?: () => void;\n onDelete?: () => void;\n customActions?: Array<{\n label: string;\n icon: React.ReactNode;\n onClick: () => void;\n }>;\n}\n\ntype PreviewType =\n | \"image\"\n | \"pdf\"\n | \"video\"\n | \"audio\"\n | \"code\"\n | \"text\"\n | \"unsupported\";\n\nfunction getFilePreviewType(mimeType: string, filename: string): PreviewType {\n // Image files\n if (mimeType.startsWith(\"image/\")) {\n return \"image\";\n }\n\n // PDF files\n if (mimeType === \"application/pdf\") {\n return \"pdf\";\n }\n\n // Video files\n if (mimeType.startsWith(\"video/\")) {\n return \"video\";\n }\n\n // Audio files\n if (mimeType.startsWith(\"audio/\")) {\n return \"audio\";\n }\n\n // Code files (by extension)\n const codeExtensions = [\n \"js\",\n \"jsx\",\n \"ts\",\n \"tsx\",\n \"py\",\n \"java\",\n \"cpp\",\n \"c\",\n \"cs\",\n \"rb\",\n \"go\",\n \"rs\",\n \"php\",\n \"html\",\n \"css\",\n \"scss\",\n \"json\",\n \"yaml\",\n \"yml\",\n \"xml\",\n \"sql\",\n \"sh\",\n \"bash\",\n \"vue\",\n \"swift\",\n \"kt\",\n \"r\",\n \"dart\",\n \"lua\",\n ];\n const ext = filename.split(\".\").pop()?.toLowerCase();\n if (ext && codeExtensions.includes(ext)) {\n return \"code\";\n }\n\n // Text files\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType === \"application/json\" ||\n mimeType === \"application/xml\"\n ) {\n return \"text\";\n }\n\n return \"unsupported\";\n}\n\nfunction detectLanguage(filename: string): string {\n const ext = filename.split(\".\").pop()?.toLowerCase();\n const languageMap: Record<string, string> = {\n js: \"javascript\",\n jsx: \"jsx\",\n ts: \"typescript\",\n tsx: \"tsx\",\n py: \"python\",\n java: \"java\",\n cpp: \"cpp\",\n c: \"c\",\n cs: \"csharp\",\n rb: \"ruby\",\n go: \"go\",\n rs: \"rust\",\n php: \"php\",\n html: \"html\",\n css: \"css\",\n json: \"json\",\n md: \"markdown\",\n yaml: \"yaml\",\n yml: \"yaml\",\n xml: \"xml\",\n sql: \"sql\",\n sh: \"bash\",\n };\n return languageMap[ext || \"\"] || \"text\";\n}\n\nexport function FilePreviewModal({\n file,\n onClose,\n onDownload,\n onShare,\n onStar,\n onDelete,\n customActions,\n}: FilePreviewModalProps) {\n const { t } = useI18n();\n const previewType = getFilePreviewType(file.mimeType, file.name);\n const url = file.downloadUrl || file.url;\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onClose();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [onClose]);\n\n const renderPreview = () => {\n switch (previewType) {\n case \"image\":\n return <ImageViewer file={file} url={url} />;\n case \"pdf\":\n return <PDFViewer file={file} url={url} />;\n case \"video\":\n return <VideoPlayer file={file} url={url} />;\n case \"audio\":\n return <AudioPlayer file={file} url={url} />;\n case \"code\":\n return (\n <CodeViewer\n file={file}\n url={url}\n language={detectLanguage(file.name)}\n />\n );\n case \"text\":\n return <TextViewer file={file} url={url} />;\n default:\n return (\n <div className=\"flex flex-col items-center justify-center h-full bg-bg-canvas p-8\">\n <div className=\"bg-bg-surface p-8 rounded-lg text-center max-w-md\">\n <p className=\"text-text-secondary mb-4\">\n {tWithFallback(\n t,\n \"files.preview.notAvailable\",\n \"Preview not available for this file type.\",\n )}\n </p>\n <p className=\"text-sm text-text-tertiary mb-6\">{file.mimeType}</p>\n <button\n onClick={onDownload}\n className=\"px-4 py-2 bg-action-primary-bg text-text-inverse rounded-lg hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.preview.downloadFile\",\n \"Download File\",\n )}\n </button>\n </div>\n </div>\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 bg-bg-overlay flex flex-col z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`Preview ${file.name}`}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 bg-bg-elevated/90 backdrop-blur-sm border-b border-border-default\">\n <div className=\"flex items-center gap-4 flex-1 min-w-0\">\n <h2 className=\"text-base font-medium text-text-primary truncate\">\n {file.name}\n </h2>\n <span className=\"text-sm text-text-secondary flex-shrink-0\">\n {(file.size / 1024 / 1024).toFixed(2)} MB\n </span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {/* Custom Actions */}\n {customActions?.map((action, index) => (\n <button\n key={index}\n onClick={action.onClick}\n className=\"flex items-center gap-1.5 px-3 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={action.label}\n >\n {action.icon}\n <span>{action.label}</span>\n </button>\n ))}\n\n {/* Standard Actions */}\n <button\n onClick={onDownload}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.common.actions.download\",\n \"Download\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.preview.downloadFile\",\n \"Download file\",\n )}\n >\n <Download className=\"size-5\" />\n </button>\n {onShare && (\n <button\n onClick={onShare}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(t, \"files.common.actions.share\", \"Share\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.shareFile\",\n \"Share file\",\n )}\n >\n <Share2 className=\"size-5\" />\n </button>\n )}\n {onStar && (\n <button\n onClick={onStar}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={\n file.starred\n ? tWithFallback(t, \"files.preview.removeStar\", \"Remove star\")\n : tWithFallback(t, \"files.preview.addStar\", \"Add star\")\n }\n aria-label={\n file.starred\n ? tWithFallback(t, \"files.preview.removeStar\", \"Remove star\")\n : tWithFallback(t, \"files.preview.addStar\", \"Add star\")\n }\n >\n <Star\n className={`size-5 ${\n file.starred\n ? \"fill-status-warning-text text-status-warning-text\"\n : \"\"\n }`}\n />\n </button>\n )}\n {onDelete && (\n <button\n onClick={onDelete}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(t, \"files.common.actions.delete\", \"Delete\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.deleteFile\",\n \"Delete file\",\n )}\n >\n <Trash2 className=\"size-5\" />\n </button>\n )}\n <button\n onClick={onClose}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200 ml-2\"\n title={tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.close\",\n \"Close preview\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-hidden\">{renderPreview()}</div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FilePreviewModal.js","names":[],"sources":["../../../src/files/components/FilePreviewModal.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { X, Download, Share2, Star, Trash2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport { ImageViewer } from \"./viewers/ImageViewer\";\nimport { PDFViewer } from \"./viewers/PDFViewer\";\nimport { VideoPlayer } from \"./viewers/VideoPlayer\";\nimport { AudioPlayer } from \"./viewers/AudioPlayer\";\nimport { CodeViewer } from \"./viewers/CodeViewer\";\nimport { TextViewer } from \"./viewers/TextViewer\";\n\ninterface FilePreviewModalProps {\n file: FileItem;\n onClose: () => void;\n onDownload: () => void;\n onShare?: () => void;\n onStar?: () => void;\n onDelete?: () => void;\n customActions?: Array<{\n label: string;\n icon: React.ReactNode;\n onClick: () => void;\n }>;\n}\n\ntype PreviewType =\n | \"image\"\n | \"pdf\"\n | \"video\"\n | \"audio\"\n | \"code\"\n | \"text\"\n | \"unsupported\";\n\nfunction getFilePreviewType(mimeType: string, filename: string): PreviewType {\n // Image files\n if (mimeType.startsWith(\"image/\")) {\n return \"image\";\n }\n\n // PDF files\n if (mimeType === \"application/pdf\") {\n return \"pdf\";\n }\n\n // Video files\n if (mimeType.startsWith(\"video/\")) {\n return \"video\";\n }\n\n // Audio files\n if (mimeType.startsWith(\"audio/\")) {\n return \"audio\";\n }\n\n // Code files (by extension)\n const codeExtensions = [\n \"js\",\n \"jsx\",\n \"ts\",\n \"tsx\",\n \"py\",\n \"java\",\n \"cpp\",\n \"c\",\n \"cs\",\n \"rb\",\n \"go\",\n \"rs\",\n \"php\",\n \"html\",\n \"css\",\n \"scss\",\n \"json\",\n \"yaml\",\n \"yml\",\n \"xml\",\n \"sql\",\n \"sh\",\n \"bash\",\n \"vue\",\n \"swift\",\n \"kt\",\n \"r\",\n \"dart\",\n \"lua\",\n ];\n const ext = filename.split(\".\").pop()?.toLowerCase();\n if (ext && codeExtensions.includes(ext)) {\n return \"code\";\n }\n\n // Text files\n if (\n mimeType.startsWith(\"text/\") ||\n mimeType === \"application/json\" ||\n mimeType === \"application/xml\"\n ) {\n return \"text\";\n }\n\n return \"unsupported\";\n}\n\nfunction detectLanguage(filename: string): string {\n const ext = filename.split(\".\").pop()?.toLowerCase();\n const languageMap: Record<string, string> = {\n js: \"javascript\",\n jsx: \"jsx\",\n ts: \"typescript\",\n tsx: \"tsx\",\n py: \"python\",\n java: \"java\",\n cpp: \"cpp\",\n c: \"c\",\n cs: \"csharp\",\n rb: \"ruby\",\n go: \"go\",\n rs: \"rust\",\n php: \"php\",\n html: \"html\",\n css: \"css\",\n json: \"json\",\n md: \"markdown\",\n yaml: \"yaml\",\n yml: \"yaml\",\n xml: \"xml\",\n sql: \"sql\",\n sh: \"bash\",\n };\n return languageMap[ext || \"\"] || \"text\";\n}\n\nexport function FilePreviewModal({\n file,\n onClose,\n onDownload,\n onShare,\n onStar,\n onDelete,\n customActions,\n}: FilePreviewModalProps) {\n const { t } = useI18n();\n const previewType = getFilePreviewType(file.mimeType, file.name);\n const url = file.downloadUrl || file.url;\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onClose();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [onClose]);\n\n const renderPreview = () => {\n switch (previewType) {\n case \"image\":\n return <ImageViewer file={file} url={url} />;\n case \"pdf\":\n return <PDFViewer file={file} url={url} />;\n case \"video\":\n return <VideoPlayer file={file} url={url} />;\n case \"audio\":\n return <AudioPlayer file={file} url={url} />;\n case \"code\":\n return (\n <CodeViewer\n file={file}\n url={url}\n language={detectLanguage(file.name)}\n />\n );\n case \"text\":\n return <TextViewer file={file} url={url} />;\n default:\n return (\n <div className=\"flex flex-col items-center justify-center h-full bg-bg-canvas p-8\">\n <div className=\"bg-bg-surface p-8 rounded-lg text-center max-w-md\">\n <p className=\"text-text-secondary mb-4\">\n {tWithFallback(\n t,\n \"files.preview.notAvailable\",\n \"Preview not available for this file type.\",\n )}\n </p>\n <p className=\"text-sm text-text-tertiary mb-6\">{file.mimeType}</p>\n <button\n type=\"button\"\n onClick={onDownload}\n className=\"px-4 py-2 bg-action-primary-bg text-text-inverse rounded-lg hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.preview.downloadFile\",\n \"Download File\",\n )}\n </button>\n </div>\n </div>\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 bg-bg-overlay flex flex-col z-[100] animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`Preview ${file.name}`}\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 bg-bg-elevated/90 backdrop-blur-sm border-b border-border-default\">\n <div className=\"flex items-center gap-4 flex-1 min-w-0\">\n <h2 className=\"text-base font-medium text-text-primary truncate\">\n {file.name}\n </h2>\n <span className=\"text-sm text-text-secondary flex-shrink-0\">\n {(file.size / 1024 / 1024).toFixed(2)} MB\n </span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {/* Custom Actions */}\n {customActions?.map((action) => (\n <button\n type=\"button\"\n key={action.label}\n onClick={action.onClick}\n className=\"flex items-center gap-1.5 px-3 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={action.label}\n >\n {action.icon}\n <span>{action.label}</span>\n </button>\n ))}\n\n {/* Standard Actions */}\n <button\n type=\"button\"\n onClick={onDownload}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.common.actions.download\",\n \"Download\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.preview.downloadFile\",\n \"Download file\",\n )}\n >\n <Download className=\"size-5\" />\n </button>\n {onShare && (\n <button\n type=\"button\"\n onClick={onShare}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(t, \"files.common.actions.share\", \"Share\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.shareFile\",\n \"Share file\",\n )}\n >\n <Share2 className=\"size-5\" />\n </button>\n )}\n {onStar && (\n <button\n type=\"button\"\n onClick={onStar}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={\n file.starred\n ? tWithFallback(t, \"files.preview.removeStar\", \"Remove star\")\n : tWithFallback(t, \"files.preview.addStar\", \"Add star\")\n }\n aria-label={\n file.starred\n ? tWithFallback(t, \"files.preview.removeStar\", \"Remove star\")\n : tWithFallback(t, \"files.preview.addStar\", \"Add star\")\n }\n >\n <Star\n className={`size-5 ${\n file.starred\n ? \"fill-status-warning-text text-status-warning-text\"\n : \"\"\n }`}\n />\n </button>\n )}\n {onDelete && (\n <button\n type=\"button\"\n onClick={onDelete}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n title={tWithFallback(t, \"files.common.actions.delete\", \"Delete\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.deleteFile\",\n \"Delete file\",\n )}\n >\n <Trash2 className=\"size-5\" />\n </button>\n )}\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200 ml-2\"\n title={tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n aria-label={tWithFallback(\n t,\n \"files.preview.close\",\n \"Close preview\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-hidden\">{renderPreview()}</div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -76,6 +76,7 @@ var C = ({ file: C, onClose: w }) => {
76
76
  children: C.name
77
77
  })] })]
78
78
  }), /* @__PURE__ */ b("button", {
79
+ type: "button",
79
80
  onClick: w,
80
81
  className: "p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
81
82
  "aria-label": r(T, "files.version.closeHistory", "Close version history"),
@@ -160,6 +161,7 @@ var C = ({ file: C, onClose: w }) => {
160
161
  }), /* @__PURE__ */ x("div", {
161
162
  className: "flex items-center gap-2",
162
163
  children: [/* @__PURE__ */ b("button", {
164
+ type: "button",
163
165
  onClick: (t) => {
164
166
  t.stopPropagation(), V(e.id);
165
167
  },
@@ -167,6 +169,7 @@ var C = ({ file: C, onClose: w }) => {
167
169
  title: r(T, "files.version.downloadThisVersion", "Download this version"),
168
170
  children: /* @__PURE__ */ b(p, { className: "size-4" })
169
171
  }), !e.isCurrentVersion && /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b("button", {
172
+ type: "button",
170
173
  onClick: (t) => {
171
174
  t.stopPropagation(), R(e.id, e.versionNumber);
172
175
  },
@@ -175,6 +178,7 @@ var C = ({ file: C, onClose: w }) => {
175
178
  title: r(T, "files.version.restoreThisVersion", "Restore this version"),
176
179
  children: F.isPending ? /* @__PURE__ */ b(m, { className: "size-4 animate-spin" }) : /* @__PURE__ */ b(h, { className: "size-4" })
177
180
  }), /* @__PURE__ */ b("button", {
181
+ type: "button",
178
182
  onClick: (t) => {
179
183
  t.stopPropagation(), z(e.id, e.versionNumber);
180
184
  },
@@ -200,6 +204,7 @@ var C = ({ file: C, onClose: w }) => {
200
204
  " ",
201
205
  r(T, "files.version.total", "total")
202
206
  ] }), /* @__PURE__ */ b("button", {
207
+ type: "button",
203
208
  onClick: w,
204
209
  className: "px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
205
210
  children: r(T, "files.common.actions.close", "Close")
@@ -1 +1 @@
1
- {"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <Clock className=\"size-12 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 hover:scale-[1.01] ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-default bg-bg-muted\"\n : \"border-border-default hover:border-border-default hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span>{formatFileSize(version.size)}</span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-default bg-bg-muted\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-default\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <Clock className=\"size-12 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 hover:scale-[1.01] ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-default bg-bg-muted\"\n : \"border-border-default hover:border-border-default hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span>{formatFileSize(version.size)}</span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-default bg-bg-muted\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}