@burdenoff/microfe-files 2026.525.2 → 2026.527.1

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 (221) 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 +6 -1
  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 +3 -0
  22. package/dist/files/components/FolderCard.js.map +1 -1
  23. package/dist/files/components/MoveDialog.js +8 -0
  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/useFileVersions.js.map +1 -1
  51. package/dist/files/pages/BrowserPage.js +18 -2
  52. package/dist/files/pages/BrowserPage.js.map +1 -1
  53. package/dist/files/pages/ShareAccessPage.js +6 -5
  54. package/dist/files/pages/ShareAccessPage.js.map +1 -1
  55. package/dist/files/utils/index.js +1 -1
  56. package/dist/files/utils/index.js.map +1 -1
  57. package/dist/files/utils/thumbnail.js.map +1 -1
  58. package/dist/files.js +2 -1
  59. package/dist/translations/en.js +259 -0
  60. package/dist/translations/en.js.map +1 -0
  61. package/package.json +1 -4
  62. package/dist/config/index.d.ts +0 -33
  63. package/dist/config/index.d.ts.map +0 -1
  64. package/dist/dev/main.d.ts +0 -1
  65. package/dist/dev/main.d.ts.map +0 -1
  66. package/dist/files/FilesRoot.d.ts +0 -31
  67. package/dist/files/FilesRoot.d.ts.map +0 -1
  68. package/dist/files/FilesRoutes.d.ts +0 -12
  69. package/dist/files/FilesRoutes.d.ts.map +0 -1
  70. package/dist/files/components/ActionMenu.d.ts +0 -19
  71. package/dist/files/components/ActionMenu.d.ts.map +0 -1
  72. package/dist/files/components/ConfirmDialog.d.ts +0 -14
  73. package/dist/files/components/ConfirmDialog.d.ts.map +0 -1
  74. package/dist/files/components/ContextMenu.d.ts +0 -59
  75. package/dist/files/components/ContextMenu.d.ts.map +0 -1
  76. package/dist/files/components/CopyDialog.d.ts +0 -12
  77. package/dist/files/components/CopyDialog.d.ts.map +0 -1
  78. package/dist/files/components/EmptyState.d.ts +0 -8
  79. package/dist/files/components/EmptyState.d.ts.map +0 -1
  80. package/dist/files/components/FileBrowser.d.ts +0 -37
  81. package/dist/files/components/FileBrowser.d.ts.map +0 -1
  82. package/dist/files/components/FileCard.d.ts +0 -30
  83. package/dist/files/components/FileCard.d.ts.map +0 -1
  84. package/dist/files/components/FileInfoPanel.d.ts +0 -9
  85. package/dist/files/components/FileInfoPanel.d.ts.map +0 -1
  86. package/dist/files/components/FileListItem.d.ts +0 -29
  87. package/dist/files/components/FileListItem.d.ts.map +0 -1
  88. package/dist/files/components/FilePreviewModal.d.ts +0 -17
  89. package/dist/files/components/FilePreviewModal.d.ts.map +0 -1
  90. package/dist/files/components/FileVersionPanel.d.ts +0 -9
  91. package/dist/files/components/FileVersionPanel.d.ts.map +0 -1
  92. package/dist/files/components/FolderCard.d.ts +0 -34
  93. package/dist/files/components/FolderCard.d.ts.map +0 -1
  94. package/dist/files/components/MoveDialog.d.ts +0 -12
  95. package/dist/files/components/MoveDialog.d.ts.map +0 -1
  96. package/dist/files/components/SearchBar.d.ts +0 -13
  97. package/dist/files/components/SearchBar.d.ts.map +0 -1
  98. package/dist/files/components/ShareDialog.d.ts +0 -24
  99. package/dist/files/components/ShareDialog.d.ts.map +0 -1
  100. package/dist/files/components/TabBar.d.ts +0 -14
  101. package/dist/files/components/TabBar.d.ts.map +0 -1
  102. package/dist/files/components/Toast.d.ts +0 -14
  103. package/dist/files/components/Toast.d.ts.map +0 -1
  104. package/dist/files/components/UploadQueue.d.ts +0 -21
  105. package/dist/files/components/UploadQueue.d.ts.map +0 -1
  106. package/dist/files/components/index.d.ts +0 -43
  107. package/dist/files/components/index.d.ts.map +0 -1
  108. package/dist/files/components/viewers/AudioPlayer.d.ts +0 -8
  109. package/dist/files/components/viewers/AudioPlayer.d.ts.map +0 -1
  110. package/dist/files/components/viewers/CodeViewer.d.ts +0 -16
  111. package/dist/files/components/viewers/CodeViewer.d.ts.map +0 -1
  112. package/dist/files/components/viewers/ImageViewer.d.ts +0 -8
  113. package/dist/files/components/viewers/ImageViewer.d.ts.map +0 -1
  114. package/dist/files/components/viewers/PDFViewer.d.ts +0 -15
  115. package/dist/files/components/viewers/PDFViewer.d.ts.map +0 -1
  116. package/dist/files/components/viewers/TextViewer.d.ts +0 -8
  117. package/dist/files/components/viewers/TextViewer.d.ts.map +0 -1
  118. package/dist/files/components/viewers/VideoPlayer.d.ts +0 -8
  119. package/dist/files/components/viewers/VideoPlayer.d.ts.map +0 -1
  120. package/dist/files/components/viewers/index.d.ts +0 -7
  121. package/dist/files/components/viewers/index.d.ts.map +0 -1
  122. package/dist/files/components/viewers/index.js +0 -6
  123. package/dist/files/graphql-types.d.ts +0 -317
  124. package/dist/files/graphql-types.d.ts.map +0 -1
  125. package/dist/files/hooks/useBatchOperations.d.ts +0 -32
  126. package/dist/files/hooks/useBatchOperations.d.ts.map +0 -1
  127. package/dist/files/hooks/useBinItems.d.ts +0 -11
  128. package/dist/files/hooks/useBinItems.d.ts.map +0 -1
  129. package/dist/files/hooks/useBrowsePage.d.ts +0 -18
  130. package/dist/files/hooks/useBrowsePage.d.ts.map +0 -1
  131. package/dist/files/hooks/useCopyFile.d.ts +0 -14
  132. package/dist/files/hooks/useCopyFile.d.ts.map +0 -1
  133. package/dist/files/hooks/useCreateFolder.d.ts +0 -14
  134. package/dist/files/hooks/useCreateFolder.d.ts.map +0 -1
  135. package/dist/files/hooks/useCreateFolderShare.d.ts +0 -16
  136. package/dist/files/hooks/useCreateFolderShare.d.ts.map +0 -1
  137. package/dist/files/hooks/useCreateShare.d.ts +0 -16
  138. package/dist/files/hooks/useCreateShare.d.ts.map +0 -1
  139. package/dist/files/hooks/useDeleteFile.d.ts +0 -9
  140. package/dist/files/hooks/useDeleteFile.d.ts.map +0 -1
  141. package/dist/files/hooks/useDeleteFolder.d.ts +0 -10
  142. package/dist/files/hooks/useDeleteFolder.d.ts.map +0 -1
  143. package/dist/files/hooks/useDragDrop.d.ts +0 -31
  144. package/dist/files/hooks/useDragDrop.d.ts.map +0 -1
  145. package/dist/files/hooks/useFileVersions.d.ts +0 -52
  146. package/dist/files/hooks/useFileVersions.d.ts.map +0 -1
  147. package/dist/files/hooks/useFiles.d.ts +0 -18
  148. package/dist/files/hooks/useFiles.d.ts.map +0 -1
  149. package/dist/files/hooks/useFolder.d.ts +0 -7
  150. package/dist/files/hooks/useFolder.d.ts.map +0 -1
  151. package/dist/files/hooks/useFolderPath.d.ts +0 -2
  152. package/dist/files/hooks/useFolderPath.d.ts.map +0 -1
  153. package/dist/files/hooks/useFoldersQuery.d.ts +0 -7
  154. package/dist/files/hooks/useFoldersQuery.d.ts.map +0 -1
  155. package/dist/files/hooks/useKeyboardNavigation.d.ts +0 -17
  156. package/dist/files/hooks/useKeyboardNavigation.d.ts.map +0 -1
  157. package/dist/files/hooks/useMoveFile.d.ts +0 -10
  158. package/dist/files/hooks/useMoveFile.d.ts.map +0 -1
  159. package/dist/files/hooks/useMoveFolder.d.ts +0 -10
  160. package/dist/files/hooks/useMoveFolder.d.ts.map +0 -1
  161. package/dist/files/hooks/usePermanentlyDeleteFile.d.ts +0 -9
  162. package/dist/files/hooks/usePermanentlyDeleteFile.d.ts.map +0 -1
  163. package/dist/files/hooks/usePermanentlyDeleteFolder.d.ts +0 -9
  164. package/dist/files/hooks/usePermanentlyDeleteFolder.d.ts.map +0 -1
  165. package/dist/files/hooks/useRecentFiles.d.ts +0 -10
  166. package/dist/files/hooks/useRecentFiles.d.ts.map +0 -1
  167. package/dist/files/hooks/useRenameFile.d.ts +0 -10
  168. package/dist/files/hooks/useRenameFile.d.ts.map +0 -1
  169. package/dist/files/hooks/useRenameFolder.d.ts +0 -10
  170. package/dist/files/hooks/useRenameFolder.d.ts.map +0 -1
  171. package/dist/files/hooks/useRestoreFile.d.ts +0 -9
  172. package/dist/files/hooks/useRestoreFile.d.ts.map +0 -1
  173. package/dist/files/hooks/useRestoreFolder.d.ts +0 -9
  174. package/dist/files/hooks/useRestoreFolder.d.ts.map +0 -1
  175. package/dist/files/hooks/useSearch.d.ts +0 -15
  176. package/dist/files/hooks/useSearch.d.ts.map +0 -1
  177. package/dist/files/hooks/useStarredItems.d.ts +0 -10
  178. package/dist/files/hooks/useStarredItems.d.ts.map +0 -1
  179. package/dist/files/hooks/useToast.d.ts +0 -11
  180. package/dist/files/hooks/useToast.d.ts.map +0 -1
  181. package/dist/files/hooks/useToggleStar.d.ts +0 -10
  182. package/dist/files/hooks/useToggleStar.d.ts.map +0 -1
  183. package/dist/files/hooks/useUploadFile.d.ts +0 -25
  184. package/dist/files/hooks/useUploadFile.d.ts.map +0 -1
  185. package/dist/files/index.d.ts +0 -29
  186. package/dist/files/index.d.ts.map +0 -1
  187. package/dist/files/pages/BrowserPage.d.ts +0 -5
  188. package/dist/files/pages/BrowserPage.d.ts.map +0 -1
  189. package/dist/files/pages/ShareAccessPage.d.ts +0 -2
  190. package/dist/files/pages/ShareAccessPage.d.ts.map +0 -1
  191. package/dist/files/providers/FilesProvider.d.ts +0 -25
  192. package/dist/files/providers/FilesProvider.d.ts.map +0 -1
  193. package/dist/files/store/filesStore.d.ts +0 -99
  194. package/dist/files/store/filesStore.d.ts.map +0 -1
  195. package/dist/files/types.d.ts +0 -266
  196. package/dist/files/types.d.ts.map +0 -1
  197. package/dist/files/utils/events.d.ts +0 -6
  198. package/dist/files/utils/events.d.ts.map +0 -1
  199. package/dist/files/utils/i18n.d.ts +0 -5
  200. package/dist/files/utils/i18n.d.ts.map +0 -1
  201. package/dist/files/utils/index.d.ts +0 -74
  202. package/dist/files/utils/index.d.ts.map +0 -1
  203. package/dist/files/utils/thumbnail.d.ts +0 -5
  204. package/dist/files/utils/thumbnail.d.ts.map +0 -1
  205. package/dist/files.d.ts +0 -2
  206. package/dist/generated/global-operations.d.ts +0 -2
  207. package/dist/generated/global-operations.d.ts.map +0 -1
  208. package/dist/generated/global-types.d.ts +0 -23527
  209. package/dist/generated/global-types.d.ts.map +0 -1
  210. package/dist/generated/wspace-operations.d.ts +0 -2706
  211. package/dist/generated/wspace-operations.d.ts.map +0 -1
  212. package/dist/generated/wspace-types.d.ts +0 -57851
  213. package/dist/generated/wspace-types.d.ts.map +0 -1
  214. package/dist/index.d.ts +0 -30
  215. package/dist/index.d.ts.map +0 -1
  216. package/dist/shared/types/index.d.ts +0 -101
  217. package/dist/shared/types/index.d.ts.map +0 -1
  218. package/dist/translations/en.d.ts +0 -315
  219. package/dist/translations/en.d.ts.map +0 -1
  220. package/dist/utils/nativeBridge.d.ts +0 -3
  221. package/dist/utils/nativeBridge.d.ts.map +0 -1
@@ -80,24 +80,28 @@ function f({ item: f, onPause: p, onResume: m, onRetry: h, onCancel: g }) {
80
80
  className: "flex items-center gap-1 flex-shrink-0",
81
81
  children: [
82
82
  f.status === "uploading" && /* @__PURE__ */ l("button", {
83
+ type: "button",
83
84
  onClick: () => p(f.id),
84
85
  className: "p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors",
85
86
  title: t(_, "files.upload.pause", "Pause"),
86
87
  children: /* @__PURE__ */ l(a, { className: "size-4" })
87
88
  }),
88
89
  f.status === "paused" && /* @__PURE__ */ l("button", {
90
+ type: "button",
89
91
  onClick: () => m(f.id),
90
92
  className: "p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors",
91
93
  title: t(_, "files.upload.resume", "Resume"),
92
94
  children: /* @__PURE__ */ l(o, { className: "size-4" })
93
95
  }),
94
96
  f.status === "failed" && /* @__PURE__ */ l("button", {
97
+ type: "button",
95
98
  onClick: () => h(f.id),
96
99
  className: "p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors",
97
100
  title: t(_, "files.upload.retry", "Retry"),
98
101
  children: /* @__PURE__ */ l(s, { className: "size-4" })
99
102
  }),
100
103
  (f.status === "pending" || f.status === "paused" || f.status === "failed") && /* @__PURE__ */ l("button", {
104
+ type: "button",
101
105
  onClick: () => g(f.id),
102
106
  className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors",
103
107
  title: t(_, "files.upload.cancel", "Cancel"),
@@ -146,6 +150,7 @@ function p({ items: e, isOpen: n, onClose: r, onPause: i, onResume: a, onRetry:
146
150
  ]
147
151
  })
148
152
  ] }), /* @__PURE__ */ l("button", {
153
+ type: "button",
149
154
  onClick: r,
150
155
  className: "p-1 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors",
151
156
  children: /* @__PURE__ */ l(c, { className: "size-4" })
@@ -1 +1 @@
1
- {"version":3,"file":"UploadQueue.js","names":[],"sources":["../../../src/files/components/UploadQueue.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport {\n Pause,\n Play,\n RotateCcw,\n X,\n CheckCircle,\n AlertCircle,\n Loader2,\n Image as ImageIcon,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { UploadItem } from \"../types\";\nimport { isImageType } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface UploadProgressBarProps {\n item: UploadItem;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nexport function UploadProgressBar({\n item,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadProgressBarProps) {\n const { t } = useI18n();\n const progressRef = useRef<HTMLDivElement>(null);\n const isImage = isImageType(item.file.type);\n\n useEffect(() => {\n if (progressRef.current) {\n progressRef.current.style.width = `${item.progress}%`;\n }\n }, [item.progress]);\n\n const getStatusIcon = () => {\n switch (item.status) {\n case \"uploading\":\n return (\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n );\n case \"paused\":\n return <Pause className=\"size-4 text-status-warning-text\" />;\n case \"completed\":\n return <CheckCircle className=\"size-4 text-status-success-text\" />;\n case \"failed\":\n return <AlertCircle className=\"size-4 text-status-error-text\" />;\n default:\n return <Loader2 className=\"size-4 animate-spin text-text-tertiary\" />;\n }\n };\n\n const getStatusColor = () => {\n switch (item.status) {\n case \"uploading\":\n return \"bg-action-primary-bg\";\n case \"paused\":\n return \"bg-status-warning-bg\";\n case \"completed\":\n return \"bg-status-success-bg\";\n case \"failed\":\n return \"bg-status-error-bg\";\n default:\n return \"bg-text-tertiary\";\n }\n };\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <div className=\"bg-bg-elevated border border-border-default rounded-lg p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-shrink-0 w-10 h-10 bg-bg-muted rounded flex items-center justify-center overflow-hidden\">\n {item.thumbnailUrl ? (\n <img\n src={item.thumbnailUrl}\n alt={item.file.name}\n className=\"w-full h-full object-cover\"\n />\n ) : isImage ? (\n <ImageIcon className=\"size-5 text-text-tertiary\" />\n ) : (\n <div className=\"w-full h-full bg-bg-accent\" />\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2 mb-1.5\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {item.file.name}\n </span>\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatFileSize(item.file.size)}\n </span>\n </div>\n\n <div className=\"relative h-1.5 bg-bg-muted rounded-full overflow-hidden\">\n <div\n ref={progressRef}\n className={`absolute inset-y-0 left-0 ${getStatusColor()} transition-all duration-300 ease-out rounded-full`}\n style={{ width: `${item.progress}%` }}\n />\n </div>\n\n <div className=\"flex items-center justify-between mt-1.5\">\n <span className=\"text-xs text-text-secondary\">\n {item.status === \"paused\" &&\n tWithFallback(t, \"files.upload.paused\", \"Paused\")}\n {item.status === \"uploading\" && `${item.progress}%`}\n {item.status === \"completed\" &&\n tWithFallback(t, \"files.upload.completed\", \"Completed\")}\n {item.status === \"failed\" &&\n (item.error ||\n tWithFallback(t, \"files.upload.failed\", \"Failed\"))}\n {item.status === \"pending\" &&\n tWithFallback(t, \"files.upload.pending\", \"Pending...\")}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n {item.status === \"uploading\" && (\n <button\n onClick={() => onPause(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.pause\", \"Pause\")}\n >\n <Pause className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"paused\" && (\n <button\n onClick={() => onResume(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.resume\", \"Resume\")}\n >\n <Play className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"failed\" && (\n <button\n onClick={() => onRetry(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.retry\", \"Retry\")}\n >\n <RotateCcw className=\"size-4\" />\n </button>\n )}\n\n {(item.status === \"pending\" ||\n item.status === \"paused\" ||\n item.status === \"failed\") && (\n <button\n onClick={() => onCancel(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.cancel\", \"Cancel\")}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n\ninterface UploadQueueProps {\n items: UploadItem[];\n isOpen: boolean;\n onClose: () => void;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nexport function UploadQueue({\n items,\n isOpen,\n onClose,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadQueueProps) {\n const { t } = useI18n();\n if (!isOpen) return null;\n\n const completedCount = items.filter((i) => i.status === \"completed\").length;\n const failedCount = items.filter((i) => i.status === \"failed\").length;\n const activeCount = items.filter(\n (i) => i.status === \"uploading\" || i.status === \"pending\",\n ).length;\n\n return (\n <div className=\"fixed bottom-4 right-4 w-80 md:w-96 max-w-[90vw] bg-bg-elevated border border-border-default rounded-lg shadow-xl z-50\">\n <div className=\"flex items-center justify-between p-3 border-b border-border-default\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.upload.uploads\", \"Uploads\")} ({activeCount}{\" \"}\n {tWithFallback(t, \"files.upload.active\", \"active\")})\n </h3>\n {completedCount > 0 && (\n <p className=\"text-xs text-status-success-text\">\n {completedCount}{\" \"}\n {tWithFallback(t, \"files.upload.completed\", \"completed\")}\n </p>\n )}\n {failedCount > 0 && (\n <p className=\"text-xs text-status-error-text\">\n {failedCount} {tWithFallback(t, \"files.upload.failed\", \"failed\")}\n </p>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto p-2 space-y-2\">\n {items.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n {tWithFallback(t, \"files.upload.noUploads\", \"No uploads\")}\n </p>\n ) : (\n items.map((item) => (\n <UploadProgressBar\n key={item.id}\n item={item}\n onPause={onPause}\n onResume={onResume}\n onRetry={onRetry}\n onCancel={onCancel}\n />\n ))\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"UploadQueue.js","names":[],"sources":["../../../src/files/components/UploadQueue.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport {\n Pause,\n Play,\n RotateCcw,\n X,\n CheckCircle,\n AlertCircle,\n Loader2,\n Image as ImageIcon,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { UploadItem } from \"../types\";\nimport { isImageType } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface UploadProgressBarProps {\n item: UploadItem;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nfunction UploadProgressBar({\n item,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadProgressBarProps) {\n const { t } = useI18n();\n const progressRef = useRef<HTMLDivElement>(null);\n const isImage = isImageType(item.file.type);\n\n useEffect(() => {\n if (progressRef.current) {\n progressRef.current.style.width = `${item.progress}%`;\n }\n }, [item.progress]);\n\n const getStatusIcon = () => {\n switch (item.status) {\n case \"uploading\":\n return (\n <Loader2 className=\"size-4 animate-spin text-action-primary-bg\" />\n );\n case \"paused\":\n return <Pause className=\"size-4 text-status-warning-text\" />;\n case \"completed\":\n return <CheckCircle className=\"size-4 text-status-success-text\" />;\n case \"failed\":\n return <AlertCircle className=\"size-4 text-status-error-text\" />;\n default:\n return <Loader2 className=\"size-4 animate-spin text-text-tertiary\" />;\n }\n };\n\n const getStatusColor = () => {\n switch (item.status) {\n case \"uploading\":\n return \"bg-action-primary-bg\";\n case \"paused\":\n return \"bg-status-warning-bg\";\n case \"completed\":\n return \"bg-status-success-bg\";\n case \"failed\":\n return \"bg-status-error-bg\";\n default:\n return \"bg-text-tertiary\";\n }\n };\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <div className=\"bg-bg-elevated border border-border-default rounded-lg p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex-shrink-0 w-10 h-10 bg-bg-muted rounded flex items-center justify-center overflow-hidden\">\n {item.thumbnailUrl ? (\n <img\n src={item.thumbnailUrl}\n alt={item.file.name}\n className=\"w-full h-full object-cover\"\n />\n ) : isImage ? (\n <ImageIcon className=\"size-5 text-text-tertiary\" />\n ) : (\n <div className=\"w-full h-full bg-bg-accent\" />\n )}\n </div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2 mb-1.5\">\n <span className=\"text-sm font-medium text-text-primary truncate\">\n {item.file.name}\n </span>\n <span className=\"text-xs text-text-secondary whitespace-nowrap\">\n {formatFileSize(item.file.size)}\n </span>\n </div>\n\n <div className=\"relative h-1.5 bg-bg-muted rounded-full overflow-hidden\">\n <div\n ref={progressRef}\n className={`absolute inset-y-0 left-0 ${getStatusColor()} transition-all duration-300 ease-out rounded-full`}\n style={{ width: `${item.progress}%` }}\n />\n </div>\n\n <div className=\"flex items-center justify-between mt-1.5\">\n <span className=\"text-xs text-text-secondary\">\n {item.status === \"paused\" &&\n tWithFallback(t, \"files.upload.paused\", \"Paused\")}\n {item.status === \"uploading\" && `${item.progress}%`}\n {item.status === \"completed\" &&\n tWithFallback(t, \"files.upload.completed\", \"Completed\")}\n {item.status === \"failed\" &&\n (item.error ||\n tWithFallback(t, \"files.upload.failed\", \"Failed\"))}\n {item.status === \"pending\" &&\n tWithFallback(t, \"files.upload.pending\", \"Pending...\")}\n </span>\n </div>\n </div>\n\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n {item.status === \"uploading\" && (\n <button\n type=\"button\"\n onClick={() => onPause(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.pause\", \"Pause\")}\n >\n <Pause className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"paused\" && (\n <button\n type=\"button\"\n onClick={() => onResume(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.resume\", \"Resume\")}\n >\n <Play className=\"size-4\" />\n </button>\n )}\n\n {item.status === \"failed\" && (\n <button\n type=\"button\"\n onClick={() => onRetry(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.retry\", \"Retry\")}\n >\n <RotateCcw className=\"size-4\" />\n </button>\n )}\n\n {(item.status === \"pending\" ||\n item.status === \"paused\" ||\n item.status === \"failed\") && (\n <button\n type=\"button\"\n onClick={() => onCancel(item.id)}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded transition-colors\"\n title={tWithFallback(t, \"files.upload.cancel\", \"Cancel\")}\n >\n <X className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n\ninterface UploadQueueProps {\n items: UploadItem[];\n isOpen: boolean;\n onClose: () => void;\n onPause: (id: string) => void;\n onResume: (id: string) => void;\n onRetry: (id: string) => void;\n onCancel: (id: string) => void;\n}\n\nexport function UploadQueue({\n items,\n isOpen,\n onClose,\n onPause,\n onResume,\n onRetry,\n onCancel,\n}: UploadQueueProps) {\n const { t } = useI18n();\n if (!isOpen) return null;\n\n const completedCount = items.filter((i) => i.status === \"completed\").length;\n const failedCount = items.filter((i) => i.status === \"failed\").length;\n const activeCount = items.filter(\n (i) => i.status === \"uploading\" || i.status === \"pending\",\n ).length;\n\n return (\n <div className=\"fixed bottom-4 right-4 w-80 md:w-96 max-w-[90vw] bg-bg-elevated border border-border-default rounded-lg shadow-xl z-50\">\n <div className=\"flex items-center justify-between p-3 border-b border-border-default\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.upload.uploads\", \"Uploads\")} ({activeCount}{\" \"}\n {tWithFallback(t, \"files.upload.active\", \"active\")})\n </h3>\n {completedCount > 0 && (\n <p className=\"text-xs text-status-success-text\">\n {completedCount}{\" \"}\n {tWithFallback(t, \"files.upload.completed\", \"completed\")}\n </p>\n )}\n {failedCount > 0 && (\n <p className=\"text-xs text-status-error-text\">\n {failedCount} {tWithFallback(t, \"files.upload.failed\", \"failed\")}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto p-2 space-y-2\">\n {items.length === 0 ? (\n <p className=\"text-sm text-text-secondary text-center py-4\">\n {tWithFallback(t, \"files.upload.noUploads\", \"No uploads\")}\n </p>\n ) : (\n items.map((item) => (\n <UploadProgressBar\n key={item.id}\n item={item}\n onPause={onPause}\n onResume={onResume}\n onRetry={onRetry}\n onCancel={onCancel}\n />\n ))\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { tWithFallback as e } from "../utils/i18n.js";
2
+ import { Clock as t, Copy as n, Download as r, Edit3 as i, FolderInput as a, Info as o, RotateCcw as s, Share2 as c, Star as l, Trash2 as u } from "lucide-react";
3
+ import { jsx as d } from "react/jsx-runtime";
4
+ //#region src/files/components/contextMenuItems.tsx
5
+ function f(f, p) {
6
+ return [
7
+ ...f.onShare ? [{
8
+ label: e(p, "files.contextMenu.share", "Share"),
9
+ icon: /* @__PURE__ */ d(c, { className: "size-4" }),
10
+ onClick: f.onShare
11
+ }] : [],
12
+ ...f.onDownload ? [{
13
+ label: e(p, "files.contextMenu.download", "Download"),
14
+ icon: /* @__PURE__ */ d(r, { className: "size-4" }),
15
+ onClick: f.onDownload
16
+ }] : [],
17
+ ...f.onCopy ? [{
18
+ label: e(p, "files.contextMenu.makeCopy", "Make a copy"),
19
+ icon: /* @__PURE__ */ d(n, { className: "size-4" }),
20
+ onClick: f.onCopy
21
+ }] : [],
22
+ ...f.onRename ? [{
23
+ label: e(p, "files.contextMenu.rename", "Rename"),
24
+ icon: /* @__PURE__ */ d(i, { className: "size-4" }),
25
+ onClick: f.onRename
26
+ }] : [],
27
+ ...f.onStar ? [{
28
+ label: f.isStarred ? e(p, "files.contextMenu.removeStar", "Remove star") : e(p, "files.contextMenu.addToStarred", "Add to starred"),
29
+ icon: /* @__PURE__ */ d(l, { className: "size-4" }),
30
+ onClick: f.onStar
31
+ }] : [],
32
+ ...f.onMove ? [{
33
+ label: e(p, "files.contextMenu.moveTo", "Move to"),
34
+ icon: /* @__PURE__ */ d(a, { className: "size-4" }),
35
+ onClick: f.onMove
36
+ }] : [],
37
+ ...f.onInfo ? [{
38
+ label: e(p, "files.contextMenu.fileInfo", "File information"),
39
+ icon: /* @__PURE__ */ d(o, { className: "size-4" }),
40
+ onClick: f.onInfo
41
+ }] : [],
42
+ ...f.onActivity ? [{
43
+ label: e(p, "files.contextMenu.activity", "Activity"),
44
+ icon: /* @__PURE__ */ d(t, { className: "size-4" }),
45
+ onClick: f.onActivity
46
+ }] : [],
47
+ ...f.onRestore ? [{
48
+ label: e(p, "files.contextMenu.restore", "Restore"),
49
+ icon: /* @__PURE__ */ d(s, { className: "size-4" }),
50
+ onClick: f.onRestore
51
+ }] : [],
52
+ ...f.onDelete ? [{
53
+ label: e(p, "files.contextMenu.delete", "Delete"),
54
+ icon: /* @__PURE__ */ d(u, { className: "size-4" }),
55
+ onClick: f.onDelete,
56
+ variant: "danger"
57
+ }] : []
58
+ ];
59
+ }
60
+ function p(t, n) {
61
+ return [
62
+ ...t.onShare ? [{
63
+ label: e(n, "files.contextMenu.share", "Share"),
64
+ icon: /* @__PURE__ */ d(c, { className: "size-4" }),
65
+ onClick: t.onShare
66
+ }] : [],
67
+ ...t.onRename ? [{
68
+ label: e(n, "files.contextMenu.rename", "Rename"),
69
+ icon: /* @__PURE__ */ d(i, { className: "size-4" }),
70
+ onClick: t.onRename
71
+ }] : [],
72
+ ...t.onStar ? [{
73
+ label: t.isStarred ? e(n, "files.contextMenu.removeStar", "Remove star") : e(n, "files.contextMenu.addToStarred", "Add to starred"),
74
+ icon: /* @__PURE__ */ d(l, { className: "size-4" }),
75
+ onClick: t.onStar
76
+ }] : [],
77
+ ...t.onMove ? [{
78
+ label: e(n, "files.contextMenu.moveTo", "Move to"),
79
+ icon: /* @__PURE__ */ d(a, { className: "size-4" }),
80
+ onClick: t.onMove
81
+ }] : [],
82
+ ...t.onInfo ? [{
83
+ label: e(n, "files.contextMenu.folderInfo", "Folder information"),
84
+ icon: /* @__PURE__ */ d(o, { className: "size-4" }),
85
+ onClick: t.onInfo
86
+ }] : [],
87
+ ...t.onRestore ? [{
88
+ label: e(n, "files.contextMenu.restore", "Restore"),
89
+ icon: /* @__PURE__ */ d(s, { className: "size-4" }),
90
+ onClick: t.onRestore
91
+ }] : [],
92
+ ...t.onDelete ? [{
93
+ label: e(n, "files.contextMenu.delete", "Delete"),
94
+ icon: /* @__PURE__ */ d(u, { className: "size-4" }),
95
+ onClick: t.onDelete,
96
+ variant: "danger"
97
+ }] : []
98
+ ];
99
+ }
100
+ //#endregion
101
+ export { f as createFileMenuItems, p as createFolderMenuItems };
102
+
103
+ //# sourceMappingURL=contextMenuItems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextMenuItems.js","names":[],"sources":["../../../src/files/components/contextMenuItems.tsx"],"sourcesContent":["import {\n Share2,\n Download,\n Copy,\n Trash2,\n Edit3,\n Star,\n FolderInput,\n Info,\n Clock,\n RotateCcw,\n} from \"lucide-react\";\nimport type { I18nContextValue } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { tWithFallback } from \"../utils/i18n\";\n\nexport function createFileMenuItems(\n options: {\n onShare?: () => void;\n onDownload?: () => void;\n onCopy?: () => void;\n onRename?: () => void;\n onStar?: () => void;\n onMove?: () => void;\n onInfo?: () => void;\n onActivity?: () => void;\n onRestore?: () => void;\n onDelete?: () => void;\n isStarred?: boolean;\n },\n t: I18nContextValue[\"t\"],\n) {\n return [\n ...(options.onShare\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.share\", \"Share\"),\n icon: <Share2 className=\"size-4\" />,\n onClick: options.onShare,\n },\n ]\n : []),\n ...(options.onDownload\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.download\", \"Download\"),\n icon: <Download className=\"size-4\" />,\n onClick: options.onDownload,\n },\n ]\n : []),\n ...(options.onCopy\n ? [\n {\n label: tWithFallback(\n t,\n \"files.contextMenu.makeCopy\",\n \"Make a copy\",\n ),\n icon: <Copy className=\"size-4\" />,\n onClick: options.onCopy,\n },\n ]\n : []),\n ...(options.onRename\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.rename\", \"Rename\"),\n icon: <Edit3 className=\"size-4\" />,\n onClick: options.onRename,\n },\n ]\n : []),\n ...(options.onStar\n ? [\n {\n label: options.isStarred\n ? tWithFallback(t, \"files.contextMenu.removeStar\", \"Remove star\")\n : tWithFallback(\n t,\n \"files.contextMenu.addToStarred\",\n \"Add to starred\",\n ),\n icon: <Star className=\"size-4\" />,\n onClick: options.onStar,\n },\n ]\n : []),\n ...(options.onMove\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.moveTo\", \"Move to\"),\n icon: <FolderInput className=\"size-4\" />,\n onClick: options.onMove,\n },\n ]\n : []),\n ...(options.onInfo\n ? [\n {\n label: tWithFallback(\n t,\n \"files.contextMenu.fileInfo\",\n \"File information\",\n ),\n icon: <Info className=\"size-4\" />,\n onClick: options.onInfo,\n },\n ]\n : []),\n ...(options.onActivity\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.activity\", \"Activity\"),\n icon: <Clock className=\"size-4\" />,\n onClick: options.onActivity,\n },\n ]\n : []),\n ...(options.onRestore\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.restore\", \"Restore\"),\n icon: <RotateCcw className=\"size-4\" />,\n onClick: options.onRestore,\n },\n ]\n : []),\n ...(options.onDelete\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.delete\", \"Delete\"),\n icon: <Trash2 className=\"size-4\" />,\n onClick: options.onDelete,\n variant: \"danger\" as const,\n },\n ]\n : []),\n ];\n}\n\nexport function createFolderMenuItems(\n options: {\n onShare?: () => void;\n onRename?: () => void;\n onStar?: () => void;\n onMove?: () => void;\n onInfo?: () => void;\n onRestore?: () => void;\n onDelete?: () => void;\n isStarred?: boolean;\n },\n t: I18nContextValue[\"t\"],\n) {\n return [\n ...(options.onShare\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.share\", \"Share\"),\n icon: <Share2 className=\"size-4\" />,\n onClick: options.onShare,\n },\n ]\n : []),\n ...(options.onRename\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.rename\", \"Rename\"),\n icon: <Edit3 className=\"size-4\" />,\n onClick: options.onRename,\n },\n ]\n : []),\n ...(options.onStar\n ? [\n {\n label: options.isStarred\n ? tWithFallback(t, \"files.contextMenu.removeStar\", \"Remove star\")\n : tWithFallback(\n t,\n \"files.contextMenu.addToStarred\",\n \"Add to starred\",\n ),\n icon: <Star className=\"size-4\" />,\n onClick: options.onStar,\n },\n ]\n : []),\n ...(options.onMove\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.moveTo\", \"Move to\"),\n icon: <FolderInput className=\"size-4\" />,\n onClick: options.onMove,\n },\n ]\n : []),\n ...(options.onInfo\n ? [\n {\n label: tWithFallback(\n t,\n \"files.contextMenu.folderInfo\",\n \"Folder information\",\n ),\n icon: <Info className=\"size-4\" />,\n onClick: options.onInfo,\n },\n ]\n : []),\n ...(options.onRestore\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.restore\", \"Restore\"),\n icon: <RotateCcw className=\"size-4\" />,\n onClick: options.onRestore,\n },\n ]\n : []),\n ...(options.onDelete\n ? [\n {\n label: tWithFallback(t, \"files.contextMenu.delete\", \"Delete\"),\n icon: <Trash2 className=\"size-4\" />,\n onClick: options.onDelete,\n variant: \"danger\" as const,\n },\n ]\n : []),\n ];\n}\n"],"mappings":"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"}
@@ -29,7 +29,8 @@ function u({ file: u, url: d }) {
29
29
  children: [
30
30
  /* @__PURE__ */ c("audio", {
31
31
  ref: f,
32
- src: d
32
+ src: d,
33
+ children: /* @__PURE__ */ c("track", { kind: "captions" })
33
34
  }),
34
35
  /* @__PURE__ */ l("div", {
35
36
  className: "flex-1 flex flex-col items-center justify-center p-8",
@@ -70,6 +71,7 @@ function u({ file: u, url: d }) {
70
71
  }), /* @__PURE__ */ l("div", {
71
72
  className: "flex items-center justify-center gap-4",
72
73
  children: [/* @__PURE__ */ c("button", {
74
+ type: "button",
73
75
  onClick: C,
74
76
  className: "p-4 text-text-inverse bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-full transition-colors duration-200",
75
77
  title: p ? "Pause" : "Play",
@@ -77,6 +79,7 @@ function u({ file: u, url: d }) {
77
79
  }), /* @__PURE__ */ l("div", {
78
80
  className: "flex items-center gap-2 ml-4",
79
81
  children: [/* @__PURE__ */ c("button", {
82
+ type: "button",
80
83
  onClick: E,
81
84
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
82
85
  title: x ? "Unmute" : "Mute",
@@ -1 +1 @@
1
- {"version":3,"file":"AudioPlayer.js","names":[],"sources":["../../../../src/files/components/viewers/AudioPlayer.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from \"react\";\nimport { Play, Pause, Volume2, VolumeX, Music } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface AudioPlayerProps {\n file: FileItem;\n url: string;\n}\n\nexport function AudioPlayer({ file, url }: AudioPlayerProps) {\n const audioRef = useRef<HTMLAudioElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [volume, setVolume] = useState(1);\n const [isMuted, setIsMuted] = useState(false);\n\n useEffect(() => {\n const audio = audioRef.current;\n if (!audio) return;\n\n const handleTimeUpdate = () => setCurrentTime(audio.currentTime);\n const handleLoadedMetadata = () => setDuration(audio.duration);\n const handleEnded = () => setIsPlaying(false);\n\n audio.addEventListener(\"timeupdate\", handleTimeUpdate);\n audio.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.addEventListener(\"ended\", handleEnded);\n\n return () => {\n audio.removeEventListener(\"timeupdate\", handleTimeUpdate);\n audio.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.removeEventListener(\"ended\", handleEnded);\n };\n }, []);\n\n const togglePlay = () => {\n if (audioRef.current) {\n if (isPlaying) {\n audioRef.current.pause();\n } else {\n audioRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n };\n\n const handleSeek = (time: number) => {\n if (audioRef.current) {\n audioRef.current.currentTime = time;\n setCurrentTime(time);\n }\n };\n\n const handleVolumeChange = (newVolume: number) => {\n setVolume(newVolume);\n setIsMuted(newVolume === 0);\n if (audioRef.current) {\n audioRef.current.volume = newVolume;\n }\n };\n\n const toggleMute = () => {\n if (audioRef.current) {\n const newMuted = !isMuted;\n setIsMuted(newMuted);\n audioRef.current.muted = newMuted;\n }\n };\n\n const formatTime = (seconds: number): string => {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n <audio ref={audioRef} src={url} />\n\n {/* Album Art Placeholder */}\n <div className=\"flex-1 flex flex-col items-center justify-center p-8\">\n <div className=\"size-64 rounded-lg bg-bg-muted flex items-center justify-center mb-8 shadow-lg\">\n <Music className=\"size-32 text-text-tertiary\" />\n </div>\n\n {/* Track Info */}\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {file.name}\n </h2>\n <p className=\"text-sm text-text-secondary mb-1\">\n {(file.size / 1024 / 1024).toFixed(2)} MB\n </p>\n <p className=\"text-sm text-text-secondary\">{formatTime(duration)}</p>\n </div>\n\n {/* Controls */}\n <div className=\"bg-bg-surface border-t border-border-default p-6\">\n {/* Progress Bar */}\n <div className=\"mb-4\">\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n value={currentTime}\n onChange={(e) => handleSeek(Number(e.target.value))}\n className=\"w-full h-2 bg-bg-muted rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n <div className=\"flex justify-between mt-2 text-sm text-text-secondary\">\n <span>{formatTime(currentTime)}</span>\n <span>{formatTime(duration)}</span>\n </div>\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-center gap-4\">\n {/* Play/Pause */}\n <button\n onClick={togglePlay}\n className=\"p-4 text-text-inverse bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-full transition-colors duration-200\"\n title={isPlaying ? \"Pause\" : \"Play\"}\n >\n {isPlaying ? (\n <Pause className=\"size-6\" />\n ) : (\n <Play className=\"size-6\" />\n )}\n </button>\n\n {/* Volume Controls */}\n <div className=\"flex items-center gap-2 ml-4\">\n <button\n onClick={toggleMute}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={isMuted ? \"Unmute\" : \"Mute\"}\n >\n {isMuted ? (\n <VolumeX className=\"size-5\" />\n ) : (\n <Volume2 className=\"size-5\" />\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.1}\n value={volume}\n onChange={(e) => handleVolumeChange(Number(e.target.value))}\n className=\"w-24 h-1 bg-bg-muted rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AASA,SAAgB,EAAY,EAAE,SAAM,UAAyB;CAC3D,IAAM,IAAW,EAAyB,KAAK,EACzC,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAa,KAAkB,EAAS,EAAE,EAC3C,CAAC,GAAU,KAAe,EAAS,EAAE,EACrC,CAAC,GAAQ,KAAa,EAAS,EAAE,EACjC,CAAC,GAAS,KAAc,EAAS,GAAM;AAE7C,SAAgB;EACd,IAAM,IAAQ,EAAS;AACvB,MAAI,CAAC,EAAO;EAEZ,IAAM,UAAyB,EAAe,EAAM,YAAY,EAC1D,UAA6B,EAAY,EAAM,SAAS,EACxD,UAAoB,EAAa,GAAM;AAM7C,SAJA,EAAM,iBAAiB,cAAc,EAAiB,EACtD,EAAM,iBAAiB,kBAAkB,EAAqB,EAC9D,EAAM,iBAAiB,SAAS,EAAY,QAE/B;AAGX,GAFA,EAAM,oBAAoB,cAAc,EAAiB,EACzD,EAAM,oBAAoB,kBAAkB,EAAqB,EACjE,EAAM,oBAAoB,SAAS,EAAY;;IAEhD,EAAE,CAAC;CAEN,IAAM,UAAmB;AACvB,EAAI,EAAS,YACP,IACF,EAAS,QAAQ,OAAO,GAExB,EAAS,QAAQ,MAAM,EAEzB,EAAa,CAAC,EAAU;IAItB,KAAc,MAAiB;AACnC,EAAI,EAAS,YACX,EAAS,QAAQ,cAAc,GAC/B,EAAe,EAAK;IAIlB,KAAsB,MAAsB;AAGhD,EAFA,EAAU,EAAU,EACpB,EAAW,MAAc,EAAE,EACvB,EAAS,YACX,EAAS,QAAQ,SAAS;IAIxB,UAAmB;AACvB,MAAI,EAAS,SAAS;GACpB,IAAM,IAAW,CAAC;AAElB,GADA,EAAW,EAAS,EACpB,EAAS,QAAQ,QAAQ;;IAIvB,KAAc,MAGX,GAFM,KAAK,MAAM,IAAU,GAAG,CAEtB,GADF,KAAK,MAAM,IAAU,GAAG,CACd,UAAU,CAAC,SAAS,GAAG,IAAI;AAGpD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,SAAD;IAAO,KAAK;IAAU,KAAK;IAAO,CAAA;GAGlC,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAO,WAAU,8BAA+B,CAAA;MAC5C,CAAA;KAGN,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAK;MACH,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBAAb,EACI,EAAK,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAC,MACpC;;KACJ,kBAAC,KAAD;MAAG,WAAU;gBAA+B,EAAW,EAAS;MAAK,CAAA;KACjE;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MACE,MAAK;MACL,KAAK;MACL,KAAK,KAAY;MACjB,OAAO;MACP,WAAW,MAAM,EAAW,OAAO,EAAE,OAAO,MAAM,CAAC;MACnD,WAAU;MACV,CAAA,EACF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAY,EAAQ,CAAA,EACtC,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAS,EAAQ,CAAA,CAC/B;QACF;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;MACV,OAAO,IAAY,UAAU;gBAG3B,EADD,IACE,IAEA,GAFD,EAAO,WAAU,UAAW,CAED;MAEtB,CAAA,EAGT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,SAAS;OACT,WAAU;OACV,OAAO,IAAU,WAAW;iBAG1B,EADD,IACE,IAEA,GAFD,EAAS,WAAU,UAAW,CAEA;OAEzB,CAAA,EACT,kBAAC,SAAD;OACE,MAAK;OACL,KAAK;OACL,KAAK;OACL,MAAM;OACN,OAAO;OACP,WAAW,MAAM,EAAmB,OAAO,EAAE,OAAO,MAAM,CAAC;OAC3D,WAAU;OACV,CAAA,CACE;QACF;OACF;;GACF"}
1
+ {"version":3,"file":"AudioPlayer.js","names":[],"sources":["../../../../src/files/components/viewers/AudioPlayer.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from \"react\";\nimport { Play, Pause, Volume2, VolumeX, Music } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface AudioPlayerProps {\n file: FileItem;\n url: string;\n}\n\nexport function AudioPlayer({ file, url }: AudioPlayerProps) {\n const audioRef = useRef<HTMLAudioElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [volume, setVolume] = useState(1);\n const [isMuted, setIsMuted] = useState(false);\n\n useEffect(() => {\n const audio = audioRef.current;\n if (!audio) return;\n\n const handleTimeUpdate = () => setCurrentTime(audio.currentTime);\n const handleLoadedMetadata = () => setDuration(audio.duration);\n const handleEnded = () => setIsPlaying(false);\n\n audio.addEventListener(\"timeupdate\", handleTimeUpdate);\n audio.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.addEventListener(\"ended\", handleEnded);\n\n return () => {\n audio.removeEventListener(\"timeupdate\", handleTimeUpdate);\n audio.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n audio.removeEventListener(\"ended\", handleEnded);\n };\n }, []);\n\n const togglePlay = () => {\n if (audioRef.current) {\n if (isPlaying) {\n audioRef.current.pause();\n } else {\n audioRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n };\n\n const handleSeek = (time: number) => {\n if (audioRef.current) {\n audioRef.current.currentTime = time;\n setCurrentTime(time);\n }\n };\n\n const handleVolumeChange = (newVolume: number) => {\n setVolume(newVolume);\n setIsMuted(newVolume === 0);\n if (audioRef.current) {\n audioRef.current.volume = newVolume;\n }\n };\n\n const toggleMute = () => {\n if (audioRef.current) {\n const newMuted = !isMuted;\n setIsMuted(newMuted);\n audioRef.current.muted = newMuted;\n }\n };\n\n const formatTime = (seconds: number): string => {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n <audio ref={audioRef} src={url}>\n <track kind=\"captions\" />\n </audio>\n\n {/* Album Art Placeholder */}\n <div className=\"flex-1 flex flex-col items-center justify-center p-8\">\n <div className=\"size-64 rounded-lg bg-bg-muted flex items-center justify-center mb-8 shadow-lg\">\n <Music className=\"size-32 text-text-tertiary\" />\n </div>\n\n {/* Track Info */}\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {file.name}\n </h2>\n <p className=\"text-sm text-text-secondary mb-1\">\n {(file.size / 1024 / 1024).toFixed(2)} MB\n </p>\n <p className=\"text-sm text-text-secondary\">{formatTime(duration)}</p>\n </div>\n\n {/* Controls */}\n <div className=\"bg-bg-surface border-t border-border-default p-6\">\n {/* Progress Bar */}\n <div className=\"mb-4\">\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n value={currentTime}\n onChange={(e) => handleSeek(Number(e.target.value))}\n className=\"w-full h-2 bg-bg-muted rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n <div className=\"flex justify-between mt-2 text-sm text-text-secondary\">\n <span>{formatTime(currentTime)}</span>\n <span>{formatTime(duration)}</span>\n </div>\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-center gap-4\">\n {/* Play/Pause */}\n <button\n type=\"button\"\n onClick={togglePlay}\n className=\"p-4 text-text-inverse bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-full transition-colors duration-200\"\n title={isPlaying ? \"Pause\" : \"Play\"}\n >\n {isPlaying ? (\n <Pause className=\"size-6\" />\n ) : (\n <Play className=\"size-6\" />\n )}\n </button>\n\n {/* Volume Controls */}\n <div className=\"flex items-center gap-2 ml-4\">\n <button\n type=\"button\"\n onClick={toggleMute}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={isMuted ? \"Unmute\" : \"Mute\"}\n >\n {isMuted ? (\n <VolumeX className=\"size-5\" />\n ) : (\n <Volume2 className=\"size-5\" />\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.1}\n value={volume}\n onChange={(e) => handleVolumeChange(Number(e.target.value))}\n className=\"w-24 h-1 bg-bg-muted rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -38,12 +38,14 @@ function c({ file: c, url: u, language: d }) {
38
38
  }), /* @__PURE__ */ s("div", {
39
39
  className: "flex items-center gap-2",
40
40
  children: [/* @__PURE__ */ o("button", {
41
+ type: "button",
41
42
  onClick: () => _(!g),
42
43
  className: `p-2 rounded transition-colors duration-200 ${g ? "text-text-link bg-bg-accent" : "text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover"}`,
43
44
  title: g ? "Disable word wrap" : "Enable word wrap",
44
45
  "aria-label": g ? "Disable word wrap" : "Enable word wrap",
45
46
  children: /* @__PURE__ */ o(i, { className: "size-4" })
46
47
  }), /* @__PURE__ */ o("button", {
48
+ type: "button",
47
49
  onClick: () => {
48
50
  navigator.clipboard.writeText(f), y(!0);
49
51
  },
@@ -1 +1 @@
1
- {"version":3,"file":"CodeViewer.js","names":[],"sources":["../../../../src/files/components/viewers/CodeViewer.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { Copy, Check, WrapText } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface CodeViewerProps {\n file: FileItem;\n url: string;\n language?: string;\n}\n\n/**\n * CodeViewer - Displays code files with syntax highlighting\n *\n * Note: This is a simplified implementation with basic highlighting.\n * For full syntax highlighting, install prism-react-renderer:\n * bun add prism-react-renderer\n */\nexport function CodeViewer({ file, url, language }: CodeViewerProps) {\n const [code, setCode] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [wordWrap, setWordWrap] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const detectedLanguage = language || detectLanguage(file.name);\n\n useEffect(() => {\n fetch(url)\n .then((res) => res.text())\n .then((text) => {\n setCode(text);\n setLoading(false);\n })\n .catch(() => {\n setLoading(false);\n });\n }, [url]);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(code);\n setCopied(true);\n };\n\n // Cleanup timeout on unmount to prevent React state update warning\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full bg-bg-canvas\">\n <span className=\"text-text-secondary\">Loading code...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <span className=\"px-2 py-1 text-xs font-medium text-text-link bg-bg-accent rounded\">\n {detectedLanguage || \"Text\"}\n </span>\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setWordWrap(!wordWrap)}\n className={`p-2 rounded transition-colors duration-200 ${\n wordWrap\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n aria-label={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n >\n <WrapText className=\"size-4\" />\n </button>\n\n <button\n onClick={handleCopy}\n className=\"flex items-center gap-1.5 rounded px-3 py-1.5 text-sm font-medium text-text-inverse bg-action-primary-bg transition-colors duration-200 hover:bg-action-primary-bg-hover\"\n title=\"Copy code\"\n aria-label=\"Copy code content\"\n >\n {copied ? (\n <>\n <Check className=\"size-4\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Code Content */}\n <div className=\"flex-1 overflow-auto\">\n <pre\n className={`p-4 text-sm font-mono leading-relaxed ${\n wordWrap ? \"whitespace-pre-wrap\" : \"whitespace-pre\"\n }`}\n style={{ tabSize: 2 }}\n >\n {code.split(\"\\n\").map((line, i) => (\n <div key={i} className=\"flex\">\n <span className=\"inline-block w-12 text-right pr-4 text-text-tertiary select-none\">\n {i + 1}\n </span>\n <span className=\"text-text-primary\">{line || \" \"}</span>\n </div>\n ))}\n </pre>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{code.split(\"\\n\").length} lines</span>\n <span>{(file.size / 1024).toFixed(2)} KB</span>\n </div>\n </div>\n );\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: \"React JSX\",\n ts: \"TypeScript\",\n tsx: \"React TSX\",\n py: \"Python\",\n java: \"Java\",\n cpp: \"C++\",\n c: \"C\",\n cs: \"C#\",\n rb: \"Ruby\",\n go: \"Go\",\n rs: \"Rust\",\n php: \"PHP\",\n html: \"HTML\",\n css: \"CSS\",\n scss: \"SCSS\",\n json: \"JSON\",\n md: \"Markdown\",\n yaml: \"YAML\",\n yml: \"YAML\",\n xml: \"XML\",\n sql: \"SQL\",\n sh: \"Shell\",\n bash: \"Bash\",\n vue: \"Vue\",\n swift: \"Swift\",\n kt: \"Kotlin\",\n r: \"R\",\n dart: \"Dart\",\n lua: \"Lua\",\n };\n return languageMap[ext || \"\"] || \"Text\";\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CodeViewer.js","names":[],"sources":["../../../../src/files/components/viewers/CodeViewer.tsx"],"sourcesContent":["import { useState, useEffect } from \"react\";\nimport { Copy, Check, WrapText } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface CodeViewerProps {\n file: FileItem;\n url: string;\n language?: string;\n}\n\n/**\n * CodeViewer - Displays code files with syntax highlighting\n *\n * Note: This is a simplified implementation with basic highlighting.\n * For full syntax highlighting, install prism-react-renderer:\n * bun add prism-react-renderer\n */\nexport function CodeViewer({ file, url, language }: CodeViewerProps) {\n const [code, setCode] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [wordWrap, setWordWrap] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const detectedLanguage = language || detectLanguage(file.name);\n\n useEffect(() => {\n fetch(url)\n .then((res) => res.text())\n .then((text) => {\n setCode(text);\n setLoading(false);\n })\n .catch(() => {\n setLoading(false);\n });\n }, [url]);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(code);\n setCopied(true);\n };\n\n // Cleanup timeout on unmount to prevent React state update warning\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full bg-bg-canvas\">\n <span className=\"text-text-secondary\">Loading code...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <span className=\"px-2 py-1 text-xs font-medium text-text-link bg-bg-accent rounded\">\n {detectedLanguage || \"Text\"}\n </span>\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setWordWrap(!wordWrap)}\n className={`p-2 rounded transition-colors duration-200 ${\n wordWrap\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n aria-label={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n >\n <WrapText className=\"size-4\" />\n </button>\n\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"flex items-center gap-1.5 rounded px-3 py-1.5 text-sm font-medium text-text-inverse bg-action-primary-bg transition-colors duration-200 hover:bg-action-primary-bg-hover\"\n title=\"Copy code\"\n aria-label=\"Copy code content\"\n >\n {copied ? (\n <>\n <Check className=\"size-4\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Code Content */}\n <div className=\"flex-1 overflow-auto\">\n <pre\n className={`p-4 text-sm font-mono leading-relaxed ${\n wordWrap ? \"whitespace-pre-wrap\" : \"whitespace-pre\"\n }`}\n style={{ tabSize: 2 }}\n >\n {code.split(\"\\n\").map((line, i) => (\n <div key={i} className=\"flex\">\n <span className=\"inline-block w-12 text-right pr-4 text-text-tertiary select-none\">\n {i + 1}\n </span>\n <span className=\"text-text-primary\">{line || \" \"}</span>\n </div>\n ))}\n </pre>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{code.split(\"\\n\").length} lines</span>\n <span>{(file.size / 1024).toFixed(2)} KB</span>\n </div>\n </div>\n );\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: \"React JSX\",\n ts: \"TypeScript\",\n tsx: \"React TSX\",\n py: \"Python\",\n java: \"Java\",\n cpp: \"C++\",\n c: \"C\",\n cs: \"C#\",\n rb: \"Ruby\",\n go: \"Go\",\n rs: \"Rust\",\n php: \"PHP\",\n html: \"HTML\",\n css: \"CSS\",\n scss: \"SCSS\",\n json: \"JSON\",\n md: \"Markdown\",\n yaml: \"YAML\",\n yml: \"YAML\",\n xml: \"XML\",\n sql: \"SQL\",\n sh: \"Shell\",\n bash: \"Bash\",\n vue: \"Vue\",\n swift: \"Swift\",\n kt: \"Kotlin\",\n r: \"R\",\n dart: \"Dart\",\n lua: \"Lua\",\n };\n return languageMap[ext || \"\"] || \"Text\";\n}\n"],"mappings":"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"}
@@ -37,6 +37,7 @@ function l({ file: l, url: u }) {
37
37
  className: "flex items-center justify-center gap-2 p-4 bg-bg-surface border-b border-border-default",
38
38
  children: [
39
39
  /* @__PURE__ */ s("button", {
40
+ type: "button",
40
41
  onClick: D,
41
42
  disabled: d <= .25,
42
43
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
@@ -48,6 +49,7 @@ function l({ file: l, url: u }) {
48
49
  children: [Math.round(d * 100), "%"]
49
50
  }),
50
51
  /* @__PURE__ */ s("button", {
52
+ type: "button",
51
53
  onClick: E,
52
54
  disabled: d >= 4,
53
55
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
@@ -56,12 +58,14 @@ function l({ file: l, url: u }) {
56
58
  }),
57
59
  /* @__PURE__ */ s("div", { className: "w-px h-6 bg-border-default mx-2" }),
58
60
  /* @__PURE__ */ s("button", {
61
+ type: "button",
59
62
  onClick: O,
60
63
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
61
64
  title: "Fit to screen",
62
65
  children: /* @__PURE__ */ s(r, { className: "size-5" })
63
66
  }),
64
67
  /* @__PURE__ */ s("button", {
68
+ type: "button",
65
69
  onClick: k,
66
70
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
67
71
  title: "Rotate 90°",
@@ -1 +1 @@
1
- {"version":3,"file":"ImageViewer.js","names":[],"sources":["../../../../src/files/components/viewers/ImageViewer.tsx"],"sourcesContent":["import { useState, useRef } from \"react\";\nimport {\n ZoomIn,\n ZoomOut,\n RotateCw,\n Maximize2,\n Minimize2,\n AlertCircle,\n} from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface ImageViewerProps {\n file: FileItem;\n url: string;\n}\n\nexport function ImageViewer({ file, url }: ImageViewerProps) {\n const [zoom, setZoom] = useState(1);\n const [rotation, setRotation] = useState(0);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const [dragStart, setDragStart] = useState({ x: 0, y: 0 });\n const [imageError, setImageError] = useState<string | null>(null);\n const imageRef = useRef<HTMLImageElement>(null);\n const [imageDimensions, setImageDimensions] = useState({\n width: 0,\n height: 0,\n });\n\n const handleZoomIn = () => setZoom((prev) => Math.min(prev + 0.25, 4));\n const handleZoomOut = () => setZoom((prev) => Math.max(prev - 0.25, 0.25));\n const handleResetZoom = () => {\n setZoom(1);\n setPosition({ x: 0, y: 0 });\n setRotation(0);\n };\n const handleRotate = () => setRotation((prev) => (prev + 90) % 360);\n\n const handleMouseDown = (e: React.MouseEvent) => {\n if (zoom > 1) {\n setIsDragging(true);\n setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });\n }\n };\n\n const handleMouseMove = (e: React.MouseEvent) => {\n if (isDragging && zoom > 1) {\n setPosition({\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n });\n }\n };\n\n const handleMouseUp = () => {\n setIsDragging(false);\n };\n\n const handleImageLoad = () => {\n if (imageRef.current) {\n setImageDimensions({\n width: imageRef.current.naturalWidth,\n height: imageRef.current.naturalHeight,\n });\n }\n };\n\n const handleImageError = () => {\n setImageError(`Failed to load image from URL`);\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-center gap-2 p-4 bg-bg-surface border-b border-border-default\">\n <button\n onClick={handleZoomOut}\n disabled={zoom <= 0.25}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom out\"\n >\n <ZoomOut className=\"size-5\" />\n </button>\n\n <span className=\"min-w-16 text-center text-sm font-medium text-text-primary\">\n {Math.round(zoom * 100)}%\n </span>\n\n <button\n onClick={handleZoomIn}\n disabled={zoom >= 4}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom in\"\n >\n <ZoomIn className=\"size-5\" />\n </button>\n\n <div className=\"w-px h-6 bg-border-default mx-2\" />\n\n <button\n onClick={handleResetZoom}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title=\"Fit to screen\"\n >\n <Minimize2 className=\"size-5\" />\n </button>\n\n <button\n onClick={handleRotate}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title=\"Rotate 90°\"\n >\n <RotateCw className=\"size-5\" />\n </button>\n </div>\n\n {/* Image Container */}\n <div\n className=\"flex-1 overflow-hidden flex items-center justify-center relative\"\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n style={{\n cursor: zoom > 1 ? (isDragging ? \"grabbing\" : \"grab\") : \"default\",\n }}\n >\n <img\n ref={imageRef}\n src={url}\n alt={file.name}\n onLoad={handleImageLoad}\n onError={handleImageError}\n draggable={false}\n className=\"max-w-full max-h-full object-contain transition-transform duration-200\"\n style={{\n transform: `scale(${zoom}) rotate(${rotation}deg) translate(${position.x / zoom}px, ${position.y / zoom}px)`,\n userSelect: \"none\",\n }}\n />\n {imageError && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-bg-canvas/90 z-10\">\n <AlertCircle className=\"size-12 text-status-error-text mb-4\" />\n <p className=\"text-text-primary font-medium mb-2\">\n Failed to load image\n </p>\n <p className=\"text-text-secondary text-sm max-w-md text-center px-4\">\n {imageError}\n </p>\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mt-4 px-4 py-2 bg-action-primary-bg text-text-inverse rounded-lg hover:bg-action-primary-bg-hover\"\n >\n Open in new tab\n </a>\n </div>\n )}\n </div>\n\n {/* Metadata */}\n <div className=\"flex items-center justify-center gap-6 p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{file.name}</span>\n {imageDimensions.width > 0 && (\n <span>\n {imageDimensions.width} × {imageDimensions.height}\n </span>\n )}\n <span>{(file.size / 1024 / 1024).toFixed(2)} MB</span>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAgBA,SAAgB,EAAY,EAAE,SAAM,UAAyB;CAC3D,IAAM,CAAC,GAAM,KAAW,EAAS,EAAE,EAC7B,CAAC,GAAU,KAAe,EAAS,EAAE,EACrC,CAAC,GAAU,KAAe,EAAS;EAAE,GAAG;EAAG,GAAG;EAAG,CAAC,EAClD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAW,KAAgB,EAAS;EAAE,GAAG;EAAG,GAAG;EAAG,CAAC,EACpD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAC3D,IAAW,EAAyB,KAAK,EACzC,CAAC,GAAiB,KAAsB,EAAS;EACrD,OAAO;EACP,QAAQ;EACT,CAAC,EAEI,UAAqB,GAAS,MAAS,KAAK,IAAI,IAAO,KAAM,EAAE,CAAC,EAChE,UAAsB,GAAS,MAAS,KAAK,IAAI,IAAO,KAAM,IAAK,CAAC,EACpE,UAAwB;AAG5B,EAFA,EAAQ,EAAE,EACV,EAAY;GAAE,GAAG;GAAG,GAAG;GAAG,CAAC,EAC3B,EAAY,EAAE;IAEV,UAAqB,GAAa,OAAU,IAAO,MAAM,IAAI,EAE7D,KAAmB,MAAwB;AAC/C,EAAI,IAAO,MACT,EAAc,GAAK,EACnB,EAAa;GAAE,GAAG,EAAE,UAAU,EAAS;GAAG,GAAG,EAAE,UAAU,EAAS;GAAG,CAAC;IAIpE,KAAmB,MAAwB;AAC/C,EAAI,KAAc,IAAO,KACvB,EAAY;GACV,GAAG,EAAE,UAAU,EAAU;GACzB,GAAG,EAAE,UAAU,EAAU;GAC1B,CAAC;IAIA,UAAsB;AAC1B,IAAc,GAAM;;AAgBtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,SAAS;MACT,UAAU,KAAQ;MAClB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;MACvB,CAAA;KAET,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,KAAK,MAAM,IAAO,IAAI,EAAC,IACnB;;KAEP,kBAAC,UAAD;MACE,SAAS;MACT,UAAU,KAAQ;MAClB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;MACtB,CAAA;KAET,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA;KAEnD,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACzB,CAAA;KAET,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;MACxB,CAAA;KACL;;GAGN,kBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,aAAa;IACb,WAAW;IACX,cAAc;IACd,OAAO,EACL,QAAQ,IAAO,IAAK,IAAa,aAAa,SAAU,WACzD;cARH,CAUE,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,KAAK,EAAK;KACV,cAzEsB;AAC5B,MAAI,EAAS,WACX,EAAmB;OACjB,OAAO,EAAS,QAAQ;OACxB,QAAQ,EAAS,QAAQ;OAC1B,CAAC;;KAqEE,eAjEuB;AAC7B,QAAc,gCAAgC;;KAiExC,WAAW;KACX,WAAU;KACV,OAAO;MACL,WAAW,SAAS,EAAK,WAAW,EAAS,iBAAiB,EAAS,IAAI,EAAK,MAAM,EAAS,IAAI,EAAK;MACxG,YAAY;MACb;KACD,CAAA,EACD,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAa,WAAU,uCAAwC,CAAA;MAC/D,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAE9C,CAAA;MACJ,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACJ,kBAAC,KAAD;OACE,MAAM;OACN,QAAO;OACP,KAAI;OACJ,WAAU;iBACX;OAEG,CAAA;MACA;OAEJ;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAO,EAAK,MAAY,CAAA;KACvB,EAAgB,QAAQ,KACvB,kBAAC,QAAD,EAAA,UAAA;MACG,EAAgB;MAAM;MAAI,EAAgB;MACtC,EAAA,CAAA;KAET,kBAAC,QAAD,EAAA,UAAA,EAAQ,EAAK,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAC,MAAU,EAAA,CAAA;KAClD;;GACF"}
1
+ {"version":3,"file":"ImageViewer.js","names":[],"sources":["../../../../src/files/components/viewers/ImageViewer.tsx"],"sourcesContent":["import { useState, useRef } from \"react\";\nimport {\n ZoomIn,\n ZoomOut,\n RotateCw,\n Maximize2,\n Minimize2,\n AlertCircle,\n} from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface ImageViewerProps {\n file: FileItem;\n url: string;\n}\n\nexport function ImageViewer({ file, url }: ImageViewerProps) {\n const [zoom, setZoom] = useState(1);\n const [rotation, setRotation] = useState(0);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isDragging, setIsDragging] = useState(false);\n const [dragStart, setDragStart] = useState({ x: 0, y: 0 });\n const [imageError, setImageError] = useState<string | null>(null);\n const imageRef = useRef<HTMLImageElement>(null);\n const [imageDimensions, setImageDimensions] = useState({\n width: 0,\n height: 0,\n });\n\n const handleZoomIn = () => setZoom((prev) => Math.min(prev + 0.25, 4));\n const handleZoomOut = () => setZoom((prev) => Math.max(prev - 0.25, 0.25));\n const handleResetZoom = () => {\n setZoom(1);\n setPosition({ x: 0, y: 0 });\n setRotation(0);\n };\n const handleRotate = () => setRotation((prev) => (prev + 90) % 360);\n\n const handleMouseDown = (e: React.MouseEvent) => {\n if (zoom > 1) {\n setIsDragging(true);\n setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y });\n }\n };\n\n const handleMouseMove = (e: React.MouseEvent) => {\n if (isDragging && zoom > 1) {\n setPosition({\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n });\n }\n };\n\n const handleMouseUp = () => {\n setIsDragging(false);\n };\n\n const handleImageLoad = () => {\n if (imageRef.current) {\n setImageDimensions({\n width: imageRef.current.naturalWidth,\n height: imageRef.current.naturalHeight,\n });\n }\n };\n\n const handleImageError = () => {\n setImageError(`Failed to load image from URL`);\n };\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-center gap-2 p-4 bg-bg-surface border-b border-border-default\">\n <button\n type=\"button\"\n onClick={handleZoomOut}\n disabled={zoom <= 0.25}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom out\"\n >\n <ZoomOut className=\"size-5\" />\n </button>\n\n <span className=\"min-w-16 text-center text-sm font-medium text-text-primary\">\n {Math.round(zoom * 100)}%\n </span>\n\n <button\n type=\"button\"\n onClick={handleZoomIn}\n disabled={zoom >= 4}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom in\"\n >\n <ZoomIn className=\"size-5\" />\n </button>\n\n <div className=\"w-px h-6 bg-border-default mx-2\" />\n\n <button\n type=\"button\"\n onClick={handleResetZoom}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title=\"Fit to screen\"\n >\n <Minimize2 className=\"size-5\" />\n </button>\n\n <button\n type=\"button\"\n onClick={handleRotate}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title=\"Rotate 90°\"\n >\n <RotateCw className=\"size-5\" />\n </button>\n </div>\n\n {/* Image Container */}\n <div\n className=\"flex-1 overflow-hidden flex items-center justify-center relative\"\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n onMouseLeave={handleMouseUp}\n style={{\n cursor: zoom > 1 ? (isDragging ? \"grabbing\" : \"grab\") : \"default\",\n }}\n >\n <img\n ref={imageRef}\n src={url}\n alt={file.name}\n onLoad={handleImageLoad}\n onError={handleImageError}\n draggable={false}\n className=\"max-w-full max-h-full object-contain transition-transform duration-200\"\n style={{\n transform: `scale(${zoom}) rotate(${rotation}deg) translate(${position.x / zoom}px, ${position.y / zoom}px)`,\n userSelect: \"none\",\n }}\n />\n {imageError && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-bg-canvas/90 z-10\">\n <AlertCircle className=\"size-12 text-status-error-text mb-4\" />\n <p className=\"text-text-primary font-medium mb-2\">\n Failed to load image\n </p>\n <p className=\"text-text-secondary text-sm max-w-md text-center px-4\">\n {imageError}\n </p>\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"mt-4 px-4 py-2 bg-action-primary-bg text-text-inverse rounded-lg hover:bg-action-primary-bg-hover\"\n >\n Open in new tab\n </a>\n </div>\n )}\n </div>\n\n {/* Metadata */}\n <div className=\"flex items-center justify-center gap-6 p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{file.name}</span>\n {imageDimensions.width > 0 && (\n <span>\n {imageDimensions.width} × {imageDimensions.height}\n </span>\n )}\n <span>{(file.size / 1024 / 1024).toFixed(2)} MB</span>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAgBA,SAAgB,EAAY,EAAE,SAAM,UAAyB;CAC3D,IAAM,CAAC,GAAM,KAAW,EAAS,EAAE,EAC7B,CAAC,GAAU,KAAe,EAAS,EAAE,EACrC,CAAC,GAAU,KAAe,EAAS;EAAE,GAAG;EAAG,GAAG;EAAG,CAAC,EAClD,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAW,KAAgB,EAAS;EAAE,GAAG;EAAG,GAAG;EAAG,CAAC,EACpD,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAC3D,IAAW,EAAyB,KAAK,EACzC,CAAC,GAAiB,KAAsB,EAAS;EACrD,OAAO;EACP,QAAQ;EACT,CAAC,EAEI,UAAqB,GAAS,MAAS,KAAK,IAAI,IAAO,KAAM,EAAE,CAAC,EAChE,UAAsB,GAAS,MAAS,KAAK,IAAI,IAAO,KAAM,IAAK,CAAC,EACpE,UAAwB;AAG5B,EAFA,EAAQ,EAAE,EACV,EAAY;GAAE,GAAG;GAAG,GAAG;GAAG,CAAC,EAC3B,EAAY,EAAE;IAEV,UAAqB,GAAa,OAAU,IAAO,MAAM,IAAI,EAE7D,KAAmB,MAAwB;AAC/C,EAAI,IAAO,MACT,EAAc,GAAK,EACnB,EAAa;GAAE,GAAG,EAAE,UAAU,EAAS;GAAG,GAAG,EAAE,UAAU,EAAS;GAAG,CAAC;IAIpE,KAAmB,MAAwB;AAC/C,EAAI,KAAc,IAAO,KACvB,EAAY;GACV,GAAG,EAAE,UAAU,EAAU;GACzB,GAAG,EAAE,UAAU,EAAU;GAC1B,CAAC;IAIA,UAAsB;AAC1B,IAAc,GAAM;;AAgBtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,KAAQ;MAClB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;MACvB,CAAA;KAET,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,KAAK,MAAM,IAAO,IAAI,EAAC,IACnB;;KAEP,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU,KAAQ;MAClB,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;MACtB,CAAA;KAET,kBAAC,OAAD,EAAK,WAAU,mCAAoC,CAAA;KAEnD,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACzB,CAAA;KAET,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,OAAM;gBAEN,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;MACxB,CAAA;KACL;;GAGN,kBAAC,OAAD;IACE,WAAU;IACV,aAAa;IACb,aAAa;IACb,WAAW;IACX,cAAc;IACd,OAAO,EACL,QAAQ,IAAO,IAAK,IAAa,aAAa,SAAU,WACzD;cARH,CAUE,kBAAC,OAAD;KACE,KAAK;KACL,KAAK;KACL,KAAK,EAAK;KACV,cA7EsB;AAC5B,MAAI,EAAS,WACX,EAAmB;OACjB,OAAO,EAAS,QAAQ;OACxB,QAAQ,EAAS,QAAQ;OAC1B,CAAC;;KAyEE,eArEuB;AAC7B,QAAc,gCAAgC;;KAqExC,WAAW;KACX,WAAU;KACV,OAAO;MACL,WAAW,SAAS,EAAK,WAAW,EAAS,iBAAiB,EAAS,IAAI,EAAK,MAAM,EAAS,IAAI,EAAK;MACxG,YAAY;MACb;KACD,CAAA,EACD,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAa,WAAU,uCAAwC,CAAA;MAC/D,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAE9C,CAAA;MACJ,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MACJ,kBAAC,KAAD;OACE,MAAM;OACN,QAAO;OACP,KAAI;OACJ,WAAU;iBACX;OAEG,CAAA;MACA;OAEJ;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAA,UAAO,EAAK,MAAY,CAAA;KACvB,EAAgB,QAAQ,KACvB,kBAAC,QAAD,EAAA,UAAA;MACG,EAAgB;MAAM;MAAI,EAAgB;MACtC,EAAA,CAAA;KAET,kBAAC,QAAD,EAAA,UAAA,EAAQ,EAAK,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAC,MAAU,EAAA,CAAA;KAClD;;GACF"}
@@ -13,6 +13,7 @@ function a({ file: a, url: o }) {
13
13
  className: "flex items-center gap-2",
14
14
  children: [
15
15
  /* @__PURE__ */ r("button", {
16
+ type: "button",
16
17
  onClick: u,
17
18
  disabled: s <= 50,
18
19
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
@@ -24,6 +25,7 @@ function a({ file: a, url: o }) {
24
25
  children: [s, "%"]
25
26
  }),
26
27
  /* @__PURE__ */ r("button", {
28
+ type: "button",
27
29
  onClick: l,
28
30
  disabled: s >= 200,
29
31
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
@@ -1 +1 @@
1
- {"version":3,"file":"PDFViewer.js","names":[],"sources":["../../../../src/files/components/viewers/PDFViewer.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { ZoomIn, ZoomOut } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface PDFViewerProps {\n file: FileItem;\n url: string;\n}\n\n/**\n * PDFViewer - Displays PDF files\n *\n * Note: This uses <object> tag for better browser PDF compatibility.\n * For a full-featured PDF viewer with more controls, install react-pdf:\n * bun add react-pdf pdfjs-dist\n */\nexport function PDFViewer({ file, url }: PDFViewerProps) {\n const [scale, setScale] = useState(100);\n\n const handleZoomIn = () => setScale((prev) => Math.min(prev + 25, 200));\n const handleZoomOut = () => setScale((prev) => Math.max(prev - 25, 50));\n\n // Create zoom-adjusted URL\n const pdfUrl = `${url}#view=FitH&zoom=${scale}`;\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-4 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleZoomOut}\n disabled={scale <= 50}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom out\"\n >\n <ZoomOut className=\"size-5\" />\n </button>\n\n <span className=\"min-w-16 text-center text-sm font-medium text-text-primary\">\n {scale}%\n </span>\n\n <button\n onClick={handleZoomIn}\n disabled={scale >= 200}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom in\"\n >\n <ZoomIn className=\"size-5\" />\n </button>\n </div>\n\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n {/* PDF Object - Using object tag instead of iframe for better compatibility */}\n <div className=\"flex-1 overflow-hidden\">\n <object\n data={pdfUrl}\n type=\"application/pdf\"\n className=\"w-full h-full\"\n title={file.name}\n >\n {/* Fallback content if PDF cannot be displayed */}\n <div className=\"flex flex-col items-center justify-center h-full gap-4 p-8\">\n <div className=\"text-text-secondary text-center\">\n <p className=\"font-text-lg font-weight-medium mb-2\">\n Unable to display PDF\n </p>\n <p className=\"font-text-sm\">\n Your browser may not support embedded PDF viewing.\n </p>\n </div>\n <a\n href={url}\n download={file.name}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-btn hover:bg-action-primary-bg-hover transition-colors\"\n >\n Download PDF\n </a>\n </div>\n </object>\n </div>\n\n {/* Info Footer */}\n <div className=\"flex items-center justify-center p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>PDF Document · {(file.size / 1024 / 1024).toFixed(2)} MB</span>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAgBA,SAAgB,EAAU,EAAE,SAAM,UAAuB;CACvD,IAAM,CAAC,GAAO,KAAY,EAAS,IAAI,EAEjC,UAAqB,GAAU,MAAS,KAAK,IAAI,IAAO,IAAI,IAAI,CAAC,EACjE,UAAsB,GAAU,MAAS,KAAK,IAAI,IAAO,IAAI,GAAG,CAAC,EAGjE,IAAS,GAAG,EAAI,kBAAkB;AAExC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,SAAS;OACT,UAAU,KAAS;OACnB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;OACvB,CAAA;MAET,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,GAAM,IACF;;MAEP,kBAAC,UAAD;OACE,SAAS;OACT,UAAU,KAAS;OACnB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACtB,CAAA;MACL;QAEN,kBAAC,QAAD;KAAM,WAAU;eAA+B,EAAK;KAAY,CAAA,CAC5D;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAM;KACN,MAAK;KACL,WAAU;KACV,OAAO,EAAK;eAGZ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAEhD,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAe;QAExB,CAAA,CACA;UACN,kBAAC,KAAD;OACE,MAAM;OACN,UAAU,EAAK;OACf,WAAU;iBACX;OAEG,CAAA,CACA;;KACC,CAAA;IACL,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,QAAD,EAAA,UAAA;KAAM;MAAiB,EAAK,OAAO,OAAO,MAAM,QAAQ,EAAE;KAAC;KAAU,EAAA,CAAA;IACjE,CAAA;GACF"}
1
+ {"version":3,"file":"PDFViewer.js","names":[],"sources":["../../../../src/files/components/viewers/PDFViewer.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { ZoomIn, ZoomOut } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface PDFViewerProps {\n file: FileItem;\n url: string;\n}\n\n/**\n * PDFViewer - Displays PDF files\n *\n * Note: This uses <object> tag for better browser PDF compatibility.\n * For a full-featured PDF viewer with more controls, install react-pdf:\n * bun add react-pdf pdfjs-dist\n */\nexport function PDFViewer({ file, url }: PDFViewerProps) {\n const [scale, setScale] = useState(100);\n\n const handleZoomIn = () => setScale((prev) => Math.min(prev + 25, 200));\n const handleZoomOut = () => setScale((prev) => Math.max(prev - 25, 50));\n\n // Create zoom-adjusted URL\n const pdfUrl = `${url}#view=FitH&zoom=${scale}`;\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-4 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleZoomOut}\n disabled={scale <= 50}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom out\"\n >\n <ZoomOut className=\"size-5\" />\n </button>\n\n <span className=\"min-w-16 text-center text-sm font-medium text-text-primary\">\n {scale}%\n </span>\n\n <button\n type=\"button\"\n onClick={handleZoomIn}\n disabled={scale >= 200}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title=\"Zoom in\"\n >\n <ZoomIn className=\"size-5\" />\n </button>\n </div>\n\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n {/* PDF Object - Using object tag instead of iframe for better compatibility */}\n <div className=\"flex-1 overflow-hidden\">\n <object\n data={pdfUrl}\n type=\"application/pdf\"\n className=\"w-full h-full\"\n title={file.name}\n >\n {/* Fallback content if PDF cannot be displayed */}\n <div className=\"flex flex-col items-center justify-center h-full gap-4 p-8\">\n <div className=\"text-text-secondary text-center\">\n <p className=\"font-text-lg font-weight-medium mb-2\">\n Unable to display PDF\n </p>\n <p className=\"font-text-sm\">\n Your browser may not support embedded PDF viewing.\n </p>\n </div>\n <a\n href={url}\n download={file.name}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-btn hover:bg-action-primary-bg-hover transition-colors\"\n >\n Download PDF\n </a>\n </div>\n </object>\n </div>\n\n {/* Info Footer */}\n <div className=\"flex items-center justify-center p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>PDF Document · {(file.size / 1024 / 1024).toFixed(2)} MB</span>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAgBA,SAAgB,EAAU,EAAE,SAAM,UAAuB;CACvD,IAAM,CAAC,GAAO,KAAY,EAAS,IAAI,EAEjC,UAAqB,GAAU,MAAS,KAAK,IAAI,IAAO,IAAI,IAAI,CAAC,EACjE,UAAsB,GAAU,MAAS,KAAK,IAAI,IAAO,IAAI,GAAG,CAAC,EAGjE,IAAS,GAAG,EAAI,kBAAkB;AAExC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU,KAAS;OACnB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;OACvB,CAAA;MAET,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,GAAM,IACF;;MAEP,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU,KAAS;OACnB,WAAU;OACV,OAAM;iBAEN,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACtB,CAAA;MACL;QAEN,kBAAC,QAAD;KAAM,WAAU;eAA+B,EAAK;KAAY,CAAA,CAC5D;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAM;KACN,MAAK;KACL,WAAU;KACV,OAAO,EAAK;eAGZ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAEhD,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBAAe;QAExB,CAAA,CACA;UACN,kBAAC,KAAD;OACE,MAAM;OACN,UAAU,EAAK;OACf,WAAU;iBACX;OAEG,CAAA,CACA;;KACC,CAAA;IACL,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,QAAD,EAAA,UAAA;KAAM;MAAiB,EAAK,OAAO,OAAO,MAAM,QAAQ,EAAE;KAAC;KAAU,EAAA,CAAA;IACjE,CAAA;GACF"}
@@ -68,8 +68,7 @@ function d({ file: d, url: f }) {
68
68
  value: y,
69
69
  onChange: (e) => b(e.target.value),
70
70
  placeholder: "Search...",
71
- className: "w-40 bg-transparent text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none",
72
- autoFocus: !0
71
+ className: "w-40 bg-transparent text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none"
73
72
  }),
74
73
  D > 0 && /* @__PURE__ */ l("span", {
75
74
  className: "text-xs text-text-secondary",
@@ -82,6 +81,7 @@ function d({ file: d, url: f }) {
82
81
  ]
83
82
  }),
84
83
  /* @__PURE__ */ c("button", {
84
+ type: "button",
85
85
  onClick: () => S(!x),
86
86
  className: `p-2 rounded transition-colors duration-200 ${x ? "text-text-link bg-bg-accent" : "text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover"}`,
87
87
  title: "Search",
@@ -89,6 +89,7 @@ function d({ file: d, url: f }) {
89
89
  children: /* @__PURE__ */ c(a, { className: "size-4" })
90
90
  }),
91
91
  /* @__PURE__ */ c("button", {
92
+ type: "button",
92
93
  onClick: () => v(!_),
93
94
  className: `p-2 rounded transition-colors duration-200 ${_ ? "text-text-link bg-bg-accent" : "text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover"}`,
94
95
  title: _ ? "Disable word wrap" : "Enable word wrap",
@@ -96,6 +97,7 @@ function d({ file: d, url: f }) {
96
97
  children: /* @__PURE__ */ c(o, { className: "size-4" })
97
98
  }),
98
99
  /* @__PURE__ */ c("button", {
100
+ type: "button",
99
101
  onClick: T,
100
102
  className: "flex items-center gap-1.5 rounded px-3 py-1.5 text-sm font-medium text-text-inverse bg-action-primary-bg transition-colors duration-200 hover:bg-action-primary-bg-hover",
101
103
  title: "Copy text",
@@ -1 +1 @@
1
- {"version":3,"file":"TextViewer.js","names":[],"sources":["../../../../src/files/components/viewers/TextViewer.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { Copy, Check, WrapText, Search } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface TextViewerProps {\n file: FileItem;\n url: string;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function TextViewer({ file, url }: TextViewerProps) {\n const [content, setContent] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [wordWrap, setWordWrap] = useState(true);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [showSearch, setShowSearch] = useState(false);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n fetch(url)\n .then((res) => res.text())\n .then((text) => {\n setContent(text);\n setLoading(false);\n })\n .catch(() => {\n setLoading(false);\n });\n }, [url]);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(content);\n setCopied(true);\n };\n\n // Cleanup timeout on unmount to prevent React state update warning\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n // Highlight search matches\n const highlightedLines = useMemo(() => {\n const lines = content.split(\"\\n\");\n\n if (!searchTerm.trim()) {\n return lines.map((line) => [{ text: line, isMatch: false }]);\n }\n\n const matcher = new RegExp(`(${escapeRegExp(searchTerm)})`, \"gi\");\n return lines.map((line) =>\n line.split(matcher).map((part) => ({\n text: part,\n isMatch: part.toLowerCase() === searchTerm.toLowerCase(),\n })),\n );\n }, [content, searchTerm]);\n\n const matchCount = useMemo(() => {\n if (!searchTerm.trim()) return 0;\n const regex = new RegExp(searchTerm, \"gi\");\n return (content.match(regex) || []).length;\n }, [content, searchTerm]);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full bg-bg-canvas\">\n <span className=\"text-text-secondary\">Loading text...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {showSearch && (\n <div className=\"flex items-center gap-2 px-2 py-1 border border-border-default rounded\">\n <Search className=\"size-4 text-text-secondary\" />\n <input\n type=\"text\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n placeholder=\"Search...\"\n className=\"w-40 bg-transparent text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n autoFocus\n />\n {matchCount > 0 && (\n <span className=\"text-xs text-text-secondary\">\n {matchCount} match{matchCount > 1 ? \"es\" : \"\"}\n </span>\n )}\n </div>\n )}\n\n <button\n onClick={() => setShowSearch(!showSearch)}\n className={`p-2 rounded transition-colors duration-200 ${\n showSearch\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title=\"Search\"\n aria-label=\"Search within text file\"\n >\n <Search className=\"size-4\" />\n </button>\n\n <button\n onClick={() => setWordWrap(!wordWrap)}\n className={`p-2 rounded transition-colors duration-200 ${\n wordWrap\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n aria-label={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n >\n <WrapText className=\"size-4\" />\n </button>\n\n <button\n onClick={handleCopy}\n className=\"flex items-center gap-1.5 rounded px-3 py-1.5 text-sm font-medium text-text-inverse bg-action-primary-bg transition-colors duration-200 hover:bg-action-primary-bg-hover\"\n title=\"Copy text\"\n aria-label=\"Copy text content\"\n >\n {copied ? (\n <>\n <Check className=\"size-4\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Text Content */}\n <div className=\"flex-1 overflow-auto\">\n <pre\n className={`p-4 text-sm font-mono leading-relaxed text-text-primary ${\n wordWrap ? \"whitespace-pre-wrap\" : \"whitespace-pre\"\n }`}\n >\n {highlightedLines.map((segments, lineIndex) => (\n <div key={lineIndex}>\n {segments.length === 0\n ? \" \"\n : segments.map((segment, partIndex) =>\n segment.isMatch ? (\n <mark\n key={`${lineIndex}-${partIndex}`}\n className=\"bg-status-warning-bg text-text-primary\"\n >\n {segment.text}\n </mark>\n ) : (\n <span key={`${lineIndex}-${partIndex}`}>\n {segment.text || \" \"}\n </span>\n ),\n )}\n </div>\n ))}\n </pre>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{content.split(\"\\n\").length} lines</span>\n <span>{(file.size / 1024).toFixed(2)} KB</span>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TextViewer.js","names":[],"sources":["../../../../src/files/components/viewers/TextViewer.tsx"],"sourcesContent":["import { useState, useEffect, useMemo } from \"react\";\nimport { Copy, Check, WrapText, Search } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\n\ninterface TextViewerProps {\n file: FileItem;\n url: string;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function TextViewer({ file, url }: TextViewerProps) {\n const [content, setContent] = useState(\"\");\n const [loading, setLoading] = useState(true);\n const [wordWrap, setWordWrap] = useState(true);\n const [searchTerm, setSearchTerm] = useState(\"\");\n const [showSearch, setShowSearch] = useState(false);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n fetch(url)\n .then((res) => res.text())\n .then((text) => {\n setContent(text);\n setLoading(false);\n })\n .catch(() => {\n setLoading(false);\n });\n }, [url]);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(content);\n setCopied(true);\n };\n\n // Cleanup timeout on unmount to prevent React state update warning\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n // Highlight search matches\n const highlightedLines = useMemo(() => {\n const lines = content.split(\"\\n\");\n\n if (!searchTerm.trim()) {\n return lines.map((line) => [{ text: line, isMatch: false }]);\n }\n\n const matcher = new RegExp(`(${escapeRegExp(searchTerm)})`, \"gi\");\n return lines.map((line) =>\n line.split(matcher).map((part) => ({\n text: part,\n isMatch: part.toLowerCase() === searchTerm.toLowerCase(),\n })),\n );\n }, [content, searchTerm]);\n\n const matchCount = useMemo(() => {\n if (!searchTerm.trim()) return 0;\n const regex = new RegExp(searchTerm, \"gi\");\n return (content.match(regex) || []).length;\n }, [content, searchTerm]);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full bg-bg-canvas\">\n <span className=\"text-text-secondary\">Loading text...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full bg-bg-canvas\">\n {/* Toolbar */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-b border-border-default\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {showSearch && (\n <div className=\"flex items-center gap-2 px-2 py-1 border border-border-default rounded\">\n <Search className=\"size-4 text-text-secondary\" />\n <input\n type=\"text\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n placeholder=\"Search...\"\n className=\"w-40 bg-transparent text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none\"\n />\n {matchCount > 0 && (\n <span className=\"text-xs text-text-secondary\">\n {matchCount} match{matchCount > 1 ? \"es\" : \"\"}\n </span>\n )}\n </div>\n )}\n\n <button\n type=\"button\"\n onClick={() => setShowSearch(!showSearch)}\n className={`p-2 rounded transition-colors duration-200 ${\n showSearch\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title=\"Search\"\n aria-label=\"Search within text file\"\n >\n <Search className=\"size-4\" />\n </button>\n\n <button\n type=\"button\"\n onClick={() => setWordWrap(!wordWrap)}\n className={`p-2 rounded transition-colors duration-200 ${\n wordWrap\n ? \"text-text-link bg-bg-accent\"\n : \"text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover\"\n }`}\n title={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n aria-label={wordWrap ? \"Disable word wrap\" : \"Enable word wrap\"}\n >\n <WrapText className=\"size-4\" />\n </button>\n\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"flex items-center gap-1.5 rounded px-3 py-1.5 text-sm font-medium text-text-inverse bg-action-primary-bg transition-colors duration-200 hover:bg-action-primary-bg-hover\"\n title=\"Copy text\"\n aria-label=\"Copy text content\"\n >\n {copied ? (\n <>\n <Check className=\"size-4\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Text Content */}\n <div className=\"flex-1 overflow-auto\">\n <pre\n className={`p-4 text-sm font-mono leading-relaxed text-text-primary ${\n wordWrap ? \"whitespace-pre-wrap\" : \"whitespace-pre\"\n }`}\n >\n {highlightedLines.map((segments, lineIndex) => (\n <div key={lineIndex}>\n {segments.length === 0\n ? \" \"\n : segments.map((segment, partIndex) =>\n segment.isMatch ? (\n <mark\n key={`${lineIndex}-${partIndex}`}\n className=\"bg-status-warning-bg text-text-primary\"\n >\n {segment.text}\n </mark>\n ) : (\n <span key={`${lineIndex}-${partIndex}`}>\n {segment.text || \" \"}\n </span>\n ),\n )}\n </div>\n ))}\n </pre>\n </div>\n\n {/* Footer */}\n <div className=\"flex items-center justify-between p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary\">\n <span>{content.split(\"\\n\").length} lines</span>\n <span>{(file.size / 1024).toFixed(2)} KB</span>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AASA,SAAS,EAAa,GAAuB;AAC3C,QAAO,EAAM,QAAQ,uBAAuB,OAAO;;AAGrD,SAAgB,EAAW,EAAE,SAAM,UAAwB;CACzD,IAAM,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAS,KAAc,EAAS,GAAK,EACtC,CAAC,GAAU,KAAe,EAAS,GAAK,EACxC,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAQ,KAAa,EAAS,GAAM;AAE3C,SAAgB;AACd,QAAM,EAAI,CACP,MAAM,MAAQ,EAAI,MAAM,CAAC,CACzB,MAAM,MAAS;AAEd,GADA,EAAW,EAAK,EAChB,EAAW,GAAM;IACjB,CACD,YAAY;AACX,KAAW,GAAM;IACjB;IACH,CAAC,EAAI,CAAC;CAET,IAAM,UAAmB;AAEvB,EADA,UAAU,UAAU,UAAU,EAAQ,EACtC,EAAU,GAAK;;AAIjB,SAAgB;AACd,MAAI,GAAQ;GACV,IAAM,IAAQ,iBAAiB,EAAU,GAAM,EAAE,IAAK;AACtD,gBAAa,aAAa,EAAM;;IAGjC,CAAC,EAAO,CAAC;CAGZ,IAAM,IAAmB,QAAc;EACrC,IAAM,IAAQ,EAAQ,MAAM,KAAK;AAEjC,MAAI,CAAC,EAAW,MAAM,CACpB,QAAO,EAAM,KAAK,MAAS,CAAC;GAAE,MAAM;GAAM,SAAS;GAAO,CAAC,CAAC;EAG9D,IAAM,IAAc,OAAO,IAAI,EAAa,EAAW,CAAC,IAAI,KAAK;AACjE,SAAO,EAAM,KAAK,MAChB,EAAK,MAAM,EAAQ,CAAC,KAAK,OAAU;GACjC,MAAM;GACN,SAAS,EAAK,aAAa,KAAK,EAAW,aAAa;GACzD,EAAE,CACJ;IACA,CAAC,GAAS,EAAW,CAAC,EAEnB,IAAa,QAAc;AAC/B,MAAI,CAAC,EAAW,MAAM,CAAE,QAAO;EAC/B,IAAM,IAAQ,IAAI,OAAO,GAAY,KAAK;AAC1C,UAAQ,EAAQ,MAAM,EAAM,IAAI,EAAE,EAAE;IACnC,CAAC,GAAS,EAAW,CAAC;AAUzB,QARI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,QAAD;GAAM,WAAU;aAAsB;GAAsB,CAAA;EACxD,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,QAAD;MAAM,WAAU;gBAA+B,EAAK;MAAY,CAAA;KAC5D,CAAA,EAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAQ,WAAU,8BAA+B,CAAA;QACjD,kBAAC,SAAD;SACE,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;SAC9C,aAAY;SACZ,WAAU;SACV,CAAA;QACD,IAAa,KACZ,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG;UAAW;UAAO,IAAa,IAAI,OAAO;UACtC;;QAEL;;MAGR,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAc,CAAC,EAAW;OACzC,WAAW,8CACT,IACI,gCACA;OAEN,OAAM;OACN,cAAW;iBAEX,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;OACtB,CAAA;MAET,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAY,CAAC,EAAS;OACrC,WAAW,8CACT,IACI,gCACA;OAEN,OAAO,IAAW,sBAAsB;OACxC,cAAY,IAAW,sBAAsB;iBAE7C,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;OACxB,CAAA;MAET,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,OAAM;OACN,cAAW;iBAEV,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAAA,UAE3B,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,OAE1B,EAAA,CAAA;OAEE,CAAA;MACL;OACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAW,2DACT,IAAW,wBAAwB;eAGpC,EAAiB,KAAK,GAAU,MAC/B,kBAAC,OAAD,EAAA,UACG,EAAS,WAAW,IACjB,MACA,EAAS,KAAK,GAAS,MACrB,EAAQ,UACN,kBAAC,QAAD;MAEE,WAAU;gBAET,EAAQ;MACJ,EAJA,GAAG,EAAU,GAAG,IAIhB,GAEP,kBAAC,QAAD,EAAA,UACG,EAAQ,QAAQ,KACZ,EAFI,GAAG,EAAU,GAAG,IAEpB,CAEV,EACD,EAjBI,EAiBJ,CACN;KACE,CAAA;IACF,CAAA;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAQ,MAAM,KAAK,CAAC,QAAO,SAAa,EAAA,CAAA,EAC/C,kBAAC,QAAD,EAAA,UAAA,EAAQ,EAAK,OAAO,MAAM,QAAQ,EAAE,EAAC,MAAU,EAAA,CAAA,CAC3C;;GACF"}
@@ -36,7 +36,8 @@ function u({ file: u, url: d }) {
36
36
  ref: f,
37
37
  src: d,
38
38
  className: "max-w-full max-h-full",
39
- onClick: T
39
+ onClick: T,
40
+ children: /* @__PURE__ */ c("track", { kind: "captions" })
40
41
  })
41
42
  }), /* @__PURE__ */ l("div", {
42
43
  className: "bg-bg-surface border-t border-border-default p-4",
@@ -59,6 +60,7 @@ function u({ file: u, url: d }) {
59
60
  className: "flex items-center gap-2",
60
61
  children: [
61
62
  /* @__PURE__ */ c("button", {
63
+ type: "button",
62
64
  onClick: T,
63
65
  className: "p-2 text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
64
66
  title: p ? "Pause" : "Play",
@@ -68,6 +70,7 @@ function u({ file: u, url: d }) {
68
70
  /* @__PURE__ */ l("div", {
69
71
  className: "flex items-center gap-2",
70
72
  children: [/* @__PURE__ */ c("button", {
73
+ type: "button",
71
74
  onClick: O,
72
75
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
73
76
  title: x ? "Unmute" : "Mute",
@@ -121,6 +124,7 @@ function u({ file: u, url: d }) {
121
124
  className: "text-sm text-text-secondary",
122
125
  children: u.name
123
126
  }), /* @__PURE__ */ c("button", {
127
+ type: "button",
124
128
  onClick: A,
125
129
  className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
126
130
  title: "Fullscreen",