@burdenoff/microfe-files 2026.830.1 → 2026.930.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.
- package/dist/files/FilesRoot.d.ts.map +1 -1
- package/dist/files/FilesRoot.js +42 -37
- package/dist/files/FilesRoot.js.map +1 -1
- package/dist/files/components/FileInfoPanel.js.map +1 -1
- package/dist/files/components/SearchBar.js +3 -3
- package/dist/files/components/SearchBar.js.map +1 -1
- package/dist/files/components/ShareDialog.d.ts.map +1 -1
- package/dist/files/components/ShareDialog.js +4 -1
- package/dist/files/components/ShareDialog.js.map +1 -1
- package/dist/files/components/StorageBanner.d.ts.map +1 -1
- package/dist/files/components/StorageBanner.js +50 -41
- package/dist/files/components/StorageBanner.js.map +1 -1
- package/dist/files/components/viewers/AudioPlayer.d.ts.map +1 -1
- package/dist/files/components/viewers/AudioPlayer.js +59 -58
- package/dist/files/components/viewers/AudioPlayer.js.map +1 -1
- package/dist/files/components/viewers/CodeViewer.d.ts.map +1 -1
- package/dist/files/components/viewers/CodeViewer.js +49 -44
- package/dist/files/components/viewers/CodeViewer.js.map +1 -1
- package/dist/files/components/viewers/ImageViewer.d.ts.map +1 -1
- package/dist/files/components/viewers/ImageViewer.js +75 -74
- package/dist/files/components/viewers/ImageViewer.js.map +1 -1
- package/dist/files/components/viewers/PDFViewer.d.ts.map +1 -1
- package/dist/files/components/viewers/PDFViewer.js +44 -41
- package/dist/files/components/viewers/PDFViewer.js.map +1 -1
- package/dist/files/components/viewers/TextViewer.d.ts.map +1 -1
- package/dist/files/components/viewers/TextViewer.js +77 -72
- package/dist/files/components/viewers/TextViewer.js.map +1 -1
- package/dist/files/components/viewers/VideoPlayer.d.ts.map +1 -1
- package/dist/files/components/viewers/VideoPlayer.js +77 -75
- package/dist/files/components/viewers/VideoPlayer.js.map +1 -1
- package/dist/shared/hooks/useTr.d.ts +17 -0
- package/dist/shared/hooks/useTr.d.ts.map +1 -0
- package/dist/shared/hooks/useTr.js +14 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/dist/translations/en.d.ts +85 -0
- package/dist/translations/en.d.ts.map +1 -1
- package/dist/translations/en.js +84 -1
- package/dist/translations/en.js.map +1 -1
- package/package.json +6 -1
- package/src/styles/dark.css +27 -0
|
@@ -1,83 +1,92 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { QuotaMeter as t } from "@burdenoff/fe-libs/shared/components";
|
|
3
|
+
import { AlertCircle as n, AlertTriangle as r } from "lucide-react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
5
|
//#region src/files/components/StorageBanner.tsx
|
|
5
|
-
function
|
|
6
|
+
function o(e) {
|
|
6
7
|
return e >= 1073741824 ? `${(e / 1073741824).toFixed(1)} GB` : e >= 1048576 ? `${(e / 1048576).toFixed(1)} MB` : e >= 1024 ? `${(e / 1024).toFixed(1)} KB` : `${e} B`;
|
|
7
8
|
}
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
var s = ({ quota: s }) => {
|
|
10
|
+
let c = e();
|
|
11
|
+
if (s.loading || s.noLimit || !s.isExhausted && !s.isNearLimit) return null;
|
|
12
|
+
let l = o(s.usedBytes), u = s.limitBytes ? o(s.limitBytes) : null, d = Math.round(s.usedBytes / 1048576), f = s.limitBytes ? Math.round(s.limitBytes / 1048576) : d;
|
|
13
|
+
return s.isExhausted ? /* @__PURE__ */ a("div", {
|
|
12
14
|
className: "flex items-center gap-3 rounded-lg border border-status-error-bg/30 bg-status-error-bg/10 px-4 py-3 text-sm",
|
|
13
15
|
children: [
|
|
14
|
-
/* @__PURE__ */
|
|
15
|
-
/* @__PURE__ */
|
|
16
|
+
/* @__PURE__ */ i(n, { className: "size-4 shrink-0 text-status-error-bg" }),
|
|
17
|
+
/* @__PURE__ */ a("div", {
|
|
16
18
|
className: "flex flex-1 items-center gap-1 text-text-primary",
|
|
17
|
-
children: [/* @__PURE__ */
|
|
19
|
+
children: [/* @__PURE__ */ i("span", {
|
|
18
20
|
className: "font-medium",
|
|
19
|
-
children: "Storage full."
|
|
20
|
-
}), /* @__PURE__ */
|
|
21
|
+
children: c("files.storage.full", "Storage full.")
|
|
22
|
+
}), /* @__PURE__ */ a("span", {
|
|
21
23
|
className: "text-text-secondary",
|
|
22
24
|
children: [
|
|
23
|
-
|
|
25
|
+
u ? c("files.storage.fullWithLimit", "{{used}} of {{limit}} used — no space left.", {
|
|
26
|
+
used: l,
|
|
27
|
+
limit: u
|
|
28
|
+
}) : c("files.storage.fullNoLimit", "{{used}} used — no space left.", { used: l }),
|
|
24
29
|
" ",
|
|
25
|
-
"Delete files to free up space or upgrade your plan."
|
|
30
|
+
c("files.storage.freeUpOrUpgrade", "Delete files to free up space or upgrade your plan.")
|
|
26
31
|
]
|
|
27
32
|
})]
|
|
28
33
|
}),
|
|
29
|
-
/* @__PURE__ */
|
|
34
|
+
/* @__PURE__ */ i("div", {
|
|
30
35
|
className: "min-w-[180px] shrink-0",
|
|
31
|
-
children: /* @__PURE__ */
|
|
32
|
-
label: "Storage",
|
|
33
|
-
used:
|
|
34
|
-
limit:
|
|
35
|
-
unit: "MB"
|
|
36
|
+
children: /* @__PURE__ */ i(t, {
|
|
37
|
+
label: c("files.storage.label", "Storage"),
|
|
38
|
+
used: d,
|
|
39
|
+
limit: f,
|
|
40
|
+
unit: c("files.storage.unit", "MB")
|
|
36
41
|
})
|
|
37
42
|
}),
|
|
38
|
-
/* @__PURE__ */
|
|
43
|
+
/* @__PURE__ */ i("div", {
|
|
39
44
|
className: "h-1.5 w-24 shrink-0 overflow-hidden rounded-full bg-surface-tertiary",
|
|
40
|
-
children: /* @__PURE__ */
|
|
45
|
+
children: /* @__PURE__ */ i("div", { className: "h-full w-full rounded-full bg-status-error-bg" })
|
|
41
46
|
})
|
|
42
47
|
]
|
|
43
|
-
}) : /* @__PURE__ */
|
|
48
|
+
}) : /* @__PURE__ */ a("div", {
|
|
44
49
|
className: "flex items-center gap-3 rounded-lg border border-status-warning-bg/30 bg-status-warning-bg/10 px-4 py-3 text-sm",
|
|
45
50
|
children: [
|
|
46
|
-
/* @__PURE__ */ r
|
|
47
|
-
/* @__PURE__ */
|
|
51
|
+
/* @__PURE__ */ i(r, { className: "size-4 shrink-0 text-status-warning-bg" }),
|
|
52
|
+
/* @__PURE__ */ a("div", {
|
|
48
53
|
className: "flex flex-1 items-center gap-1 text-text-primary",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
54
|
+
children: [/* @__PURE__ */ i("span", {
|
|
50
55
|
className: "font-medium",
|
|
51
|
-
children: "Storage almost full."
|
|
52
|
-
}), /* @__PURE__ */
|
|
56
|
+
children: c("files.storage.almostFull", "Storage almost full.")
|
|
57
|
+
}), /* @__PURE__ */ a("span", {
|
|
53
58
|
className: "text-text-secondary",
|
|
54
59
|
children: [
|
|
55
|
-
|
|
60
|
+
u ? c("files.storage.almostFullWithLimit", "{{used}} of {{limit}} used ({{percent}}%).", {
|
|
61
|
+
used: l,
|
|
62
|
+
limit: u,
|
|
63
|
+
percent: Math.round(s.usedPercent)
|
|
64
|
+
}) : c("files.storage.almostFullNoLimit", "{{used}} used.", { used: l }),
|
|
56
65
|
" ",
|
|
57
|
-
"Consider deleting files or upgrading your plan."
|
|
66
|
+
c("files.storage.deleteOrUpgrade", "Consider deleting files or upgrading your plan.")
|
|
58
67
|
]
|
|
59
68
|
})]
|
|
60
69
|
}),
|
|
61
|
-
/* @__PURE__ */
|
|
70
|
+
/* @__PURE__ */ i("div", {
|
|
62
71
|
className: "min-w-[180px] shrink-0",
|
|
63
|
-
children: /* @__PURE__ */
|
|
64
|
-
label: "Storage",
|
|
65
|
-
used:
|
|
66
|
-
limit:
|
|
67
|
-
unit: "MB"
|
|
72
|
+
children: /* @__PURE__ */ i(t, {
|
|
73
|
+
label: c("files.storage.label", "Storage"),
|
|
74
|
+
used: d,
|
|
75
|
+
limit: f,
|
|
76
|
+
unit: c("files.storage.unit", "MB")
|
|
68
77
|
})
|
|
69
78
|
}),
|
|
70
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ i("div", {
|
|
71
80
|
className: "h-1.5 w-24 shrink-0 overflow-hidden rounded-full bg-surface-tertiary",
|
|
72
|
-
children: /* @__PURE__ */
|
|
81
|
+
children: /* @__PURE__ */ i("div", {
|
|
73
82
|
className: "h-full rounded-full bg-status-warning-bg transition-all",
|
|
74
|
-
style: { width: `${
|
|
83
|
+
style: { width: `${s.usedPercent}%` }
|
|
75
84
|
})
|
|
76
85
|
})
|
|
77
86
|
]
|
|
78
87
|
});
|
|
79
88
|
};
|
|
80
89
|
//#endregion
|
|
81
|
-
export {
|
|
90
|
+
export { s as StorageBanner };
|
|
82
91
|
|
|
83
92
|
//# sourceMappingURL=StorageBanner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageBanner.js","names":[],"sources":["../../../src/files/components/StorageBanner.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { AlertCircle, AlertTriangle, HardDrive } from \"lucide-react\";\nimport { QuotaMeter } from \"@burdenoff/fe-libs/shared/components\";\nimport type { StorageQuotaState } from \"../hooks/useStorageQuota\";\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(1)} GB`;\n if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\ninterface StorageBannerProps {\n quota: StorageQuotaState;\n}\n\nexport const StorageBanner: FC<StorageBannerProps> = ({ quota }) => {\n if (\n quota.loading ||\n quota.noLimit ||\n (!quota.isExhausted && !quota.isNearLimit)\n ) {\n return null;\n }\n\n const usedLabel = formatBytes(quota.usedBytes);\n const limitLabel = quota.limitBytes ? formatBytes(quota.limitBytes) : null;\n const quotaMeterUsed = Math.round(quota.usedBytes / 1048576);\n const quotaMeterLimit = quota.limitBytes
|
|
1
|
+
{"version":3,"file":"StorageBanner.js","names":[],"sources":["../../../src/files/components/StorageBanner.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { AlertCircle, AlertTriangle, HardDrive } from \"lucide-react\";\nimport { QuotaMeter } from \"@burdenoff/fe-libs/shared/components\";\nimport type { StorageQuotaState } from \"../hooks/useStorageQuota\";\nimport { useTr } from \"../../shared/hooks/useTr\";\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(1)} GB`;\n if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\ninterface StorageBannerProps {\n quota: StorageQuotaState;\n}\n\nexport const StorageBanner: FC<StorageBannerProps> = ({ quota }) => {\n const tr = useTr();\n\n if (\n quota.loading ||\n quota.noLimit ||\n (!quota.isExhausted && !quota.isNearLimit)\n ) {\n return null;\n }\n\n const usedLabel = formatBytes(quota.usedBytes);\n const limitLabel = quota.limitBytes ? formatBytes(quota.limitBytes) : null;\n const quotaMeterUsed = Math.round(quota.usedBytes / 1048576);\n const quotaMeterLimit = quota.limitBytes\n ? Math.round(quota.limitBytes / 1048576)\n : quotaMeterUsed;\n\n if (quota.isExhausted) {\n return (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-error-bg/30 bg-status-error-bg/10 px-4 py-3 text-sm\">\n <AlertCircle className=\"size-4 shrink-0 text-status-error-bg\" />\n <div className=\"flex flex-1 items-center gap-1 text-text-primary\">\n <span className=\"font-medium\">\n {tr(\"files.storage.full\", \"Storage full.\")}\n </span>\n <span className=\"text-text-secondary\">\n {limitLabel\n ? tr(\n \"files.storage.fullWithLimit\",\n \"{{used}} of {{limit}} used — no space left.\",\n { used: usedLabel, limit: limitLabel },\n )\n : tr(\n \"files.storage.fullNoLimit\",\n \"{{used}} used — no space left.\",\n { used: usedLabel },\n )}{\" \"}\n {tr(\n \"files.storage.freeUpOrUpgrade\",\n \"Delete files to free up space or upgrade your plan.\",\n )}\n </span>\n </div>\n <div className=\"min-w-[180px] shrink-0\">\n <QuotaMeter\n label={tr(\"files.storage.label\", \"Storage\")}\n used={quotaMeterUsed}\n limit={quotaMeterLimit}\n unit={tr(\"files.storage.unit\", \"MB\")}\n />\n </div>\n <div className=\"h-1.5 w-24 shrink-0 overflow-hidden rounded-full bg-surface-tertiary\">\n <div className=\"h-full w-full rounded-full bg-status-error-bg\" />\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex items-center gap-3 rounded-lg border border-status-warning-bg/30 bg-status-warning-bg/10 px-4 py-3 text-sm\">\n <AlertTriangle className=\"size-4 shrink-0 text-status-warning-bg\" />\n <div className=\"flex flex-1 items-center gap-1 text-text-primary\">\n <span className=\"font-medium\">\n {tr(\"files.storage.almostFull\", \"Storage almost full.\")}\n </span>\n <span className=\"text-text-secondary\">\n {limitLabel\n ? tr(\n \"files.storage.almostFullWithLimit\",\n \"{{used}} of {{limit}} used ({{percent}}%).\",\n {\n used: usedLabel,\n limit: limitLabel,\n percent: Math.round(quota.usedPercent),\n },\n )\n : tr(\"files.storage.almostFullNoLimit\", \"{{used}} used.\", {\n used: usedLabel,\n })}{\" \"}\n {tr(\n \"files.storage.deleteOrUpgrade\",\n \"Consider deleting files or upgrading your plan.\",\n )}\n </span>\n </div>\n <div className=\"min-w-[180px] shrink-0\">\n <QuotaMeter\n label={tr(\"files.storage.label\", \"Storage\")}\n used={quotaMeterUsed}\n limit={quotaMeterLimit}\n unit={tr(\"files.storage.unit\", \"MB\")}\n />\n </div>\n <div className=\"h-1.5 w-24 shrink-0 overflow-hidden rounded-full bg-surface-tertiary\">\n <div\n className=\"h-full rounded-full bg-status-warning-bg transition-all\"\n style={{ width: `${quota.usedPercent}%` }}\n />\n </div>\n </div>\n );\n};\n\ninterface StorageIndicatorProps {\n quota: StorageQuotaState;\n}\n\nexport const StorageIndicator: FC<StorageIndicatorProps> = ({ quota }) => {\n if (quota.loading || quota.noLimit || !quota.limitBytes) return null;\n\n const usedLabel = formatBytes(quota.usedBytes);\n const limitLabel = formatBytes(quota.limitBytes);\n const color = quota.isExhausted\n ? \"bg-status-error-bg\"\n : quota.isNearLimit\n ? \"bg-status-warning-bg\"\n : \"bg-status-info-bg\";\n\n return (\n <div className=\"flex items-center gap-2 text-xs text-text-muted\">\n <HardDrive className=\"size-3.5 shrink-0\" />\n <span className=\"tabular-nums\">\n {usedLabel} / {limitLabel}\n </span>\n <div className=\"h-1 w-16 overflow-hidden rounded-full bg-surface-tertiary\">\n <div\n className={`h-full rounded-full transition-all ${color}`}\n style={{ width: `${quota.usedPercent}%` }}\n />\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;AAMA,SAAS,EAAY,GAAuB;AAI1C,QAHI,KAAS,aAAmB,IAAI,IAAQ,YAAY,QAAQ,EAAE,CAAC,OAC/D,KAAS,UAAgB,IAAI,IAAQ,SAAS,QAAQ,EAAE,CAAC,OACzD,KAAS,OAAa,IAAI,IAAQ,MAAM,QAAQ,EAAE,CAAC,OAChD,GAAG,EAAM;;AAOlB,IAAa,KAAyC,EAAE,eAAY;CAClE,IAAM,IAAK,GAAO;AAElB,KACE,EAAM,WACN,EAAM,WACL,CAAC,EAAM,eAAe,CAAC,EAAM,YAE9B,QAAO;CAGT,IAAM,IAAY,EAAY,EAAM,UAAU,EACxC,IAAa,EAAM,aAAa,EAAY,EAAM,WAAW,GAAG,MAChE,IAAiB,KAAK,MAAM,EAAM,YAAY,QAAQ,EACtD,IAAkB,EAAM,aAC1B,KAAK,MAAM,EAAM,aAAa,QAAQ,GACtC;AA2CJ,QAzCI,EAAM,cAEN,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAa,WAAU,wCAAyC,CAAA;GAChE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAG,sBAAsB,gBAAgB;KACrC,CAAA,EACP,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,IACG,EACE,+BACA,+CACA;OAAE,MAAM;OAAW,OAAO;OAAY,CACvC,GACD,EACE,6BACA,kCACA,EAAE,MAAM,GAAW,CACpB;MAAE;MACN,EACC,iCACA,sDACD;MACI;OACH;;GACN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,OAAO,EAAG,uBAAuB,UAAU;KAC3C,MAAM;KACN,OAAO;KACP,MAAM,EAAG,sBAAsB,KAAK;KACpC,CAAA;IACE,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD,EAAK,WAAU,iDAAkD,CAAA;IAC7D,CAAA;GACF;MAKR,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAe,WAAU,0CAA2C,CAAA;GACpE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAG,4BAA4B,uBAAuB;KAClD,CAAA,EACP,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,IACG,EACE,qCACA,8CACA;OACE,MAAM;OACN,OAAO;OACP,SAAS,KAAK,MAAM,EAAM,YAAY;OACvC,CACF,GACD,EAAG,mCAAmC,kBAAkB,EACtD,MAAM,GACP,CAAC;MAAE;MACP,EACC,iCACA,kDACD;MACI;OACH;;GACN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,OAAO,EAAG,uBAAuB,UAAU;KAC3C,MAAM;KACN,OAAO;KACP,MAAM,EAAG,sBAAsB,KAAK;KACpC,CAAA;IACE,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,EAAE,OAAO,GAAG,EAAM,YAAY,IAAI;KACzC,CAAA;IACE,CAAA;GACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioPlayer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/AudioPlayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"AudioPlayer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/AudioPlayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,gBAAgB,+BAkK1D"}
|
|
@@ -1,99 +1,100 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../../shared/hooks/useTr.js";
|
|
2
|
+
import { useEffect as t, useRef as n, useState as r } from "react";
|
|
3
|
+
import { Music as i, Pause as a, Play as o, Volume2 as s, VolumeX as c } from "lucide-react";
|
|
4
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
4
5
|
//#region src/files/components/viewers/AudioPlayer.tsx
|
|
5
|
-
function
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
let e =
|
|
6
|
+
function d({ file: d, url: f }) {
|
|
7
|
+
let p = e(), m = n(null), [h, g] = r(!1), [_, v] = r(0), [y, b] = r(0), [x, S] = r(1), [C, w] = r(!1);
|
|
8
|
+
t(() => {
|
|
9
|
+
let e = m.current;
|
|
9
10
|
if (!e) return;
|
|
10
|
-
let t = () =>
|
|
11
|
+
let t = () => v(e.currentTime), n = () => b(e.duration), r = () => g(!1);
|
|
11
12
|
return e.addEventListener("timeupdate", t), e.addEventListener("loadedmetadata", n), e.addEventListener("ended", r), () => {
|
|
12
13
|
e.removeEventListener("timeupdate", t), e.removeEventListener("loadedmetadata", n), e.removeEventListener("ended", r);
|
|
13
14
|
};
|
|
14
15
|
}, []);
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
},
|
|
22
|
-
if (
|
|
23
|
-
let e = !
|
|
24
|
-
|
|
16
|
+
let T = () => {
|
|
17
|
+
m.current && (h ? m.current.pause() : m.current.play(), g(!h));
|
|
18
|
+
}, E = (e) => {
|
|
19
|
+
m.current && (m.current.currentTime = e, v(e));
|
|
20
|
+
}, D = (e) => {
|
|
21
|
+
S(e), w(e === 0), m.current && (m.current.volume = e);
|
|
22
|
+
}, O = () => {
|
|
23
|
+
if (m.current) {
|
|
24
|
+
let e = !C;
|
|
25
|
+
w(e), m.current.muted = e;
|
|
25
26
|
}
|
|
26
|
-
},
|
|
27
|
-
return /* @__PURE__ */
|
|
27
|
+
}, k = (e) => `${Math.floor(e / 60)}:${Math.floor(e % 60).toString().padStart(2, "0")}`;
|
|
28
|
+
return /* @__PURE__ */ u("div", {
|
|
28
29
|
className: "flex flex-col h-full bg-bg-canvas",
|
|
29
30
|
children: [
|
|
30
|
-
/* @__PURE__ */
|
|
31
|
-
ref:
|
|
32
|
-
src:
|
|
33
|
-
"aria-label":
|
|
34
|
-
children: /* @__PURE__ */
|
|
31
|
+
/* @__PURE__ */ l("audio", {
|
|
32
|
+
ref: m,
|
|
33
|
+
src: f,
|
|
34
|
+
"aria-label": d.name,
|
|
35
|
+
children: /* @__PURE__ */ l("track", { kind: "captions" })
|
|
35
36
|
}),
|
|
36
|
-
/* @__PURE__ */
|
|
37
|
+
/* @__PURE__ */ u("div", {
|
|
37
38
|
className: "flex-1 flex flex-col items-center justify-center p-8",
|
|
38
39
|
children: [
|
|
39
|
-
/* @__PURE__ */
|
|
40
|
+
/* @__PURE__ */ l("div", {
|
|
40
41
|
className: "size-64 rounded-lg bg-bg-sunken flex items-center justify-center mb-8 shadow-lg",
|
|
41
|
-
children: /* @__PURE__ */
|
|
42
|
+
children: /* @__PURE__ */ l(i, { className: "size-32 text-text-tertiary" })
|
|
42
43
|
}),
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ l("h2", {
|
|
44
45
|
className: "text-xl font-semibold text-text-primary mb-2",
|
|
45
|
-
children:
|
|
46
|
+
children: d.name
|
|
46
47
|
}),
|
|
47
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ u("p", {
|
|
48
49
|
className: "text-sm text-text-secondary mb-1",
|
|
49
|
-
children: [(
|
|
50
|
+
children: [(d.size / 1024 / 1024).toFixed(2), " MB"]
|
|
50
51
|
}),
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ l("p", {
|
|
52
53
|
className: "text-sm text-text-secondary",
|
|
53
|
-
children:
|
|
54
|
+
children: k(y)
|
|
54
55
|
})
|
|
55
56
|
]
|
|
56
57
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
58
|
+
/* @__PURE__ */ u("div", {
|
|
58
59
|
className: "bg-bg-surface border-t border-border-default p-6",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
60
|
+
children: [/* @__PURE__ */ u("div", {
|
|
60
61
|
className: "mb-4",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
62
|
+
children: [/* @__PURE__ */ l("input", {
|
|
62
63
|
type: "range",
|
|
63
64
|
min: 0,
|
|
64
|
-
max:
|
|
65
|
-
value:
|
|
66
|
-
onChange: (e) =>
|
|
67
|
-
"aria-label": "Seek",
|
|
65
|
+
max: y || 0,
|
|
66
|
+
value: _,
|
|
67
|
+
onChange: (e) => E(Number(e.target.value)),
|
|
68
|
+
"aria-label": p("files.preview.audio.seek", "Seek"),
|
|
68
69
|
className: "w-full h-2 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg"
|
|
69
|
-
}), /* @__PURE__ */
|
|
70
|
+
}), /* @__PURE__ */ u("div", {
|
|
70
71
|
className: "flex justify-between mt-2 text-sm text-text-secondary",
|
|
71
|
-
children: [/* @__PURE__ */
|
|
72
|
+
children: [/* @__PURE__ */ l("span", { children: k(_) }), /* @__PURE__ */ l("span", { children: k(y) })]
|
|
72
73
|
})]
|
|
73
|
-
}), /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ u("div", {
|
|
74
75
|
className: "flex items-center justify-center gap-4",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
76
|
+
children: [/* @__PURE__ */ l("button", {
|
|
76
77
|
type: "button",
|
|
77
|
-
onClick:
|
|
78
|
+
onClick: T,
|
|
78
79
|
className: "p-4 text-text-inverse bg-action-primary-bg hover:bg-action-primary-bg-hover rounded-full transition-colors duration-200",
|
|
79
|
-
title:
|
|
80
|
-
children:
|
|
81
|
-
}), /* @__PURE__ */
|
|
80
|
+
title: h ? p("files.preview.audio.pause", "Pause") : p("files.preview.audio.play", "Play"),
|
|
81
|
+
children: l(h ? a : o, { className: "size-6" })
|
|
82
|
+
}), /* @__PURE__ */ u("div", {
|
|
82
83
|
className: "flex items-center gap-2 ml-4",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
84
|
+
children: [/* @__PURE__ */ l("button", {
|
|
84
85
|
type: "button",
|
|
85
|
-
onClick:
|
|
86
|
+
onClick: O,
|
|
86
87
|
className: "p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200",
|
|
87
|
-
title:
|
|
88
|
-
children:
|
|
89
|
-
}), /* @__PURE__ */
|
|
88
|
+
title: C ? p("files.preview.audio.unmute", "Unmute") : p("files.preview.audio.mute", "Mute"),
|
|
89
|
+
children: l(C ? c : s, { className: "size-5" })
|
|
90
|
+
}), /* @__PURE__ */ l("input", {
|
|
90
91
|
type: "range",
|
|
91
92
|
min: 0,
|
|
92
93
|
max: 1,
|
|
93
94
|
step: .1,
|
|
94
|
-
value:
|
|
95
|
-
onChange: (e) =>
|
|
96
|
-
"aria-label": "Volume",
|
|
95
|
+
value: x,
|
|
96
|
+
onChange: (e) => D(Number(e.target.value)),
|
|
97
|
+
"aria-label": p("files.preview.audio.volume", "Volume"),
|
|
97
98
|
className: "w-24 h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg"
|
|
98
99
|
})]
|
|
99
100
|
})]
|
|
@@ -103,6 +104,6 @@ function u({ file: u, url: d }) {
|
|
|
103
104
|
});
|
|
104
105
|
}
|
|
105
106
|
//#endregion
|
|
106
|
-
export {
|
|
107
|
+
export { d as AudioPlayer };
|
|
107
108
|
|
|
108
109
|
//# sourceMappingURL=AudioPlayer.js.map
|
|
@@ -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} aria-label={file.name}>\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-sunken 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 aria-label
|
|
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\";\nimport { useTr } from \"../../../shared/hooks/useTr\";\n\ninterface AudioPlayerProps {\n file: FileItem;\n url: string;\n}\n\nexport function AudioPlayer({ file, url }: AudioPlayerProps) {\n const tr = useTr();\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} aria-label={file.name}>\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-sunken 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 aria-label={tr(\"files.preview.audio.seek\", \"Seek\")}\n className=\"w-full h-2 bg-bg-sunken 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={\n isPlaying\n ? tr(\"files.preview.audio.pause\", \"Pause\")\n : tr(\"files.preview.audio.play\", \"Play\")\n }\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={\n isMuted\n ? tr(\"files.preview.audio.unmute\", \"Unmute\")\n : tr(\"files.preview.audio.mute\", \"Mute\")\n }\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 aria-label={tr(\"files.preview.audio.volume\", \"Volume\")}\n className=\"w-24 h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAUA,SAAgB,EAAY,EAAE,SAAM,UAAyB;CAC3D,IAAM,IAAK,GAAO,EACZ,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;IAAK,cAAY,EAAK;cAC/C,kBAAC,SAAD,EAAO,MAAK,YAAa,CAAA;IACnB,CAAA;GAGR,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,cAAY,EAAG,4BAA4B,OAAO;MAClD,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,MAAK;MACL,SAAS;MACT,WAAU;MACV,OACE,IACI,EAAG,6BAA6B,QAAQ,GACxC,EAAG,4BAA4B,OAAO;gBAI1C,EADD,IACE,IAEA,GAFD,EAAO,WAAU,UAAW,CAED;MAEtB,CAAA,EAGT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;OACV,OACE,IACI,EAAG,8BAA8B,SAAS,GAC1C,EAAG,4BAA4B,OAAO;iBAI1C,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,cAAY,EAAG,8BAA8B,SAAS;OACtD,WAAU;OACV,CAAA,CACE;QACF;OACF;;GACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeViewer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/CodeViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeViewer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/CodeViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,eAAe;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,eAAe,+BAoIlE"}
|
|
@@ -1,86 +1,91 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../../shared/hooks/useTr.js";
|
|
2
|
+
import { useEffect as t, useState as n } from "react";
|
|
3
|
+
import { Check as r, Copy as i, WrapText as a } from "lucide-react";
|
|
4
|
+
import { Fragment as o, jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
4
5
|
//#region src/files/components/viewers/CodeViewer.tsx
|
|
5
|
-
function
|
|
6
|
-
let [
|
|
7
|
-
return
|
|
8
|
-
fetch(
|
|
9
|
-
|
|
6
|
+
function l({ file: l, url: d, language: f }) {
|
|
7
|
+
let p = e(), [m, h] = n(""), [g, _] = n(!0), [v, y] = n(!1), [b, x] = n(!1), S = f || u(l.name);
|
|
8
|
+
return t(() => {
|
|
9
|
+
fetch(d).then((e) => e.text()).then((e) => {
|
|
10
|
+
h(e), _(!1);
|
|
10
11
|
}).catch(() => {
|
|
11
|
-
|
|
12
|
+
_(!1);
|
|
12
13
|
});
|
|
13
|
-
}, [
|
|
14
|
-
if (
|
|
15
|
-
let e = setTimeout(() =>
|
|
14
|
+
}, [d]), t(() => {
|
|
15
|
+
if (b) {
|
|
16
|
+
let e = setTimeout(() => x(!1), 2e3);
|
|
16
17
|
return () => clearTimeout(e);
|
|
17
18
|
}
|
|
18
|
-
}, [
|
|
19
|
+
}, [b]), g ? /* @__PURE__ */ s("div", {
|
|
19
20
|
className: "flex items-center justify-center h-full bg-bg-canvas",
|
|
20
|
-
children: /* @__PURE__ */
|
|
21
|
+
children: /* @__PURE__ */ s("span", {
|
|
21
22
|
className: "text-text-secondary",
|
|
22
|
-
children: "Loading code..."
|
|
23
|
+
children: p("files.preview.code.loading", "Loading code...")
|
|
23
24
|
})
|
|
24
|
-
}) : /* @__PURE__ */
|
|
25
|
+
}) : /* @__PURE__ */ c("div", {
|
|
25
26
|
className: "flex flex-col h-full bg-bg-canvas",
|
|
26
27
|
children: [
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ c("div", {
|
|
28
29
|
className: "flex items-center justify-between p-3 bg-bg-surface border-b border-border-default",
|
|
29
|
-
children: [/* @__PURE__ */
|
|
30
|
+
children: [/* @__PURE__ */ c("div", {
|
|
30
31
|
className: "flex items-center gap-2",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
32
|
+
children: [/* @__PURE__ */ s("span", {
|
|
32
33
|
className: "px-2 py-1 text-xs font-medium text-text-link bg-bg-accent rounded",
|
|
33
|
-
children:
|
|
34
|
-
}), /* @__PURE__ */
|
|
34
|
+
children: S || p("files.preview.code.text", "Text")
|
|
35
|
+
}), /* @__PURE__ */ s("span", {
|
|
35
36
|
className: "text-sm text-text-secondary",
|
|
36
|
-
children:
|
|
37
|
+
children: l.name
|
|
37
38
|
})]
|
|
38
|
-
}), /* @__PURE__ */
|
|
39
|
+
}), /* @__PURE__ */ c("div", {
|
|
39
40
|
className: "flex items-center gap-2",
|
|
40
|
-
children: [/* @__PURE__ */
|
|
41
|
+
children: [/* @__PURE__ */ s("button", {
|
|
41
42
|
type: "button",
|
|
42
|
-
onClick: () =>
|
|
43
|
-
className: `p-2 rounded transition-colors duration-200 ${
|
|
44
|
-
title:
|
|
45
|
-
"aria-label":
|
|
46
|
-
children: /* @__PURE__ */
|
|
47
|
-
}), /* @__PURE__ */
|
|
43
|
+
onClick: () => y(!v),
|
|
44
|
+
className: `p-2 rounded transition-colors duration-200 ${v ? "text-text-link bg-bg-accent" : "text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover"}`,
|
|
45
|
+
title: v ? p("files.preview.code.disableWordWrap", "Disable word wrap") : p("files.preview.code.enableWordWrap", "Enable word wrap"),
|
|
46
|
+
"aria-label": v ? p("files.preview.code.disableWordWrap", "Disable word wrap") : p("files.preview.code.enableWordWrap", "Enable word wrap"),
|
|
47
|
+
children: /* @__PURE__ */ s(a, { className: "size-4" })
|
|
48
|
+
}), /* @__PURE__ */ s("button", {
|
|
48
49
|
type: "button",
|
|
49
50
|
onClick: () => {
|
|
50
|
-
navigator.clipboard.writeText(
|
|
51
|
+
navigator.clipboard.writeText(m), x(!0);
|
|
51
52
|
},
|
|
52
53
|
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",
|
|
53
|
-
title: "Copy code",
|
|
54
|
-
"aria-label": "Copy code content",
|
|
55
|
-
children:
|
|
54
|
+
title: p("files.preview.code.copyCode", "Copy code"),
|
|
55
|
+
"aria-label": p("files.preview.code.copyCodeContent", "Copy code content"),
|
|
56
|
+
children: b ? /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s(r, { className: "size-4" }), p("files.preview.code.copied", "Copied!")] }) : /* @__PURE__ */ c(o, { children: [/* @__PURE__ */ s(i, { className: "size-4" }), p("files.preview.code.copy", "Copy")] })
|
|
56
57
|
})]
|
|
57
58
|
})]
|
|
58
59
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ s("div", {
|
|
60
61
|
className: "flex-1 overflow-auto",
|
|
61
|
-
children: /* @__PURE__ */
|
|
62
|
-
className: `p-4 text-sm font-mono leading-relaxed ${
|
|
62
|
+
children: /* @__PURE__ */ s("pre", {
|
|
63
|
+
className: `p-4 text-sm font-mono leading-relaxed ${v ? "whitespace-pre-wrap" : "whitespace-pre"}`,
|
|
63
64
|
style: { tabSize: 2 },
|
|
64
|
-
children:
|
|
65
|
+
children: m.split("\n").map((e, t) => /* @__PURE__ */ c("div", {
|
|
65
66
|
className: "flex",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ s("span", {
|
|
67
68
|
className: "inline-block w-12 text-right pr-4 text-text-tertiary select-none",
|
|
68
69
|
children: t + 1
|
|
69
|
-
}), /* @__PURE__ */
|
|
70
|
+
}), /* @__PURE__ */ s("span", {
|
|
70
71
|
className: "text-text-primary",
|
|
71
72
|
children: e || " "
|
|
72
73
|
})]
|
|
73
74
|
}, t))
|
|
74
75
|
})
|
|
75
76
|
}),
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ c("div", {
|
|
77
78
|
className: "flex items-center justify-between p-3 bg-bg-surface border-t border-border-default text-xs text-text-secondary",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ c("span", { children: [
|
|
80
|
+
m.split("\n").length,
|
|
81
|
+
" ",
|
|
82
|
+
p("files.preview.code.lines", "lines")
|
|
83
|
+
] }), /* @__PURE__ */ c("span", { children: [(l.size / 1024).toFixed(2), " KB"] })]
|
|
79
84
|
})
|
|
80
85
|
]
|
|
81
86
|
});
|
|
82
87
|
}
|
|
83
|
-
function
|
|
88
|
+
function u(e) {
|
|
84
89
|
return {
|
|
85
90
|
js: "JavaScript",
|
|
86
91
|
jsx: "React JSX",
|
|
@@ -115,6 +120,6 @@ function l(e) {
|
|
|
115
120
|
}[e.split(".").pop()?.toLowerCase() || ""] || "Text";
|
|
116
121
|
}
|
|
117
122
|
//#endregion
|
|
118
|
-
export {
|
|
123
|
+
export { l as CodeViewer };
|
|
119
124
|
|
|
120
125
|
//# sourceMappingURL=CodeViewer.js.map
|
|
@@ -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\"
|
|
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\";\nimport { useTr } from \"../../../shared/hooks/useTr\";\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 tr = useTr();\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\">\n {tr(\"files.preview.code.loading\", \"Loading code...\")}\n </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 || tr(\"files.preview.code.text\", \"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={\n wordWrap\n ? tr(\"files.preview.code.disableWordWrap\", \"Disable word wrap\")\n : tr(\"files.preview.code.enableWordWrap\", \"Enable word wrap\")\n }\n aria-label={\n wordWrap\n ? tr(\"files.preview.code.disableWordWrap\", \"Disable word wrap\")\n : tr(\"files.preview.code.enableWordWrap\", \"Enable word wrap\")\n }\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={tr(\"files.preview.code.copyCode\", \"Copy code\")}\n aria-label={tr(\n \"files.preview.code.copyCodeContent\",\n \"Copy code content\",\n )}\n >\n {copied ? (\n <>\n <Check className=\"size-4\" />\n {tr(\"files.preview.code.copied\", \"Copied!\")}\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n {tr(\"files.preview.code.copy\", \"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>\n {code.split(\"\\n\").length} {tr(\"files.preview.code.lines\", \"lines\")}\n </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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageViewer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/ImageViewer.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ImageViewer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/ImageViewer.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,gBAAgB,+BAoK1D"}
|