@burdenoff/microfe-files 2026.830.1 → 2026.830.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +75 -74
- 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 +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilesRoot.d.ts","sourceRoot":"","sources":["../../src/files/FilesRoot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"FilesRoot.d.ts","sourceRoot":"","sources":["../../src/files/FilesRoot.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,SAAS,CAAC;AAyBvD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CAiGxC,CAAC"}
|
package/dist/files/FilesRoot.js
CHANGED
|
@@ -1,63 +1,68 @@
|
|
|
1
1
|
import { FilesProvider as e } from "./providers/FilesProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
2
|
+
import { useTr as t } from "../shared/hooks/useTr.js";
|
|
3
|
+
import { buildGraphQLEndpoint as n, setFilesConfig as r } from "../config/index.js";
|
|
4
|
+
import { FilesRoutes as i } from "./FilesRoutes.js";
|
|
5
|
+
import { useEffect as a } from "react";
|
|
6
|
+
import { ErrorBoundary as o } from "@burdenoff/fe-libs/shared/components";
|
|
7
|
+
import { QueryClient as s, QueryClientProvider as c } from "@tanstack/react-query";
|
|
8
|
+
import { FolderOpen as l } from "lucide-react";
|
|
9
|
+
import { FeatureFlagProvider as u } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
10
|
+
import { jsx as d } from "react/jsx-runtime";
|
|
10
11
|
//#region src/files/FilesRoot.tsx
|
|
11
|
-
var
|
|
12
|
+
var f = new s({ defaultOptions: { queries: {
|
|
12
13
|
staleTime: 1e3 * 60 * 5,
|
|
13
14
|
refetchOnWindowFocus: !1
|
|
14
|
-
} } }),
|
|
15
|
-
let { registerNavItems:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
apiGatewayUrl:
|
|
19
|
-
apiGatewayBaseUrl:
|
|
20
|
-
authToken:
|
|
15
|
+
} } }), p = (s) => {
|
|
16
|
+
let { registerNavItems: p, registerFooterItems: m, basePath: h = "/", apiGatewayUrl: g, authToken: _, workspaceId: v } = s, y = t();
|
|
17
|
+
a(() => {
|
|
18
|
+
g && r({
|
|
19
|
+
apiGatewayUrl: g.includes("/graphql") ? g : n(g, "workspaces"),
|
|
20
|
+
apiGatewayBaseUrl: g,
|
|
21
|
+
authToken: _
|
|
21
22
|
});
|
|
22
|
-
}, [
|
|
23
|
-
let
|
|
24
|
-
return
|
|
25
|
-
if (
|
|
23
|
+
}, [g, _]);
|
|
24
|
+
let b = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
25
|
+
return a(() => {
|
|
26
|
+
if (p) return p([{
|
|
26
27
|
id: "files-browse",
|
|
27
|
-
label: "All Files",
|
|
28
|
-
path:
|
|
29
|
-
icon: /* @__PURE__ */
|
|
28
|
+
label: y("files.nav.allFiles", "All Files"),
|
|
29
|
+
path: b(h, "browse"),
|
|
30
|
+
icon: /* @__PURE__ */ d(l, { size: 20 }),
|
|
30
31
|
order: 10,
|
|
31
32
|
group: "files"
|
|
32
33
|
}]);
|
|
33
|
-
}, [
|
|
34
|
-
|
|
34
|
+
}, [
|
|
35
|
+
p,
|
|
36
|
+
h,
|
|
37
|
+
y
|
|
38
|
+
]), a(() => {
|
|
39
|
+
if (m) return m([{
|
|
35
40
|
id: "files-status",
|
|
36
|
-
label: "Files Ready",
|
|
41
|
+
label: y("files.nav.statusReady", "Files Ready"),
|
|
37
42
|
type: "status",
|
|
38
43
|
order: 10,
|
|
39
44
|
section: "left"
|
|
40
45
|
}]);
|
|
41
|
-
}, [
|
|
42
|
-
fallback: /* @__PURE__ */
|
|
46
|
+
}, [m, y]), /* @__PURE__ */ d(o, {
|
|
47
|
+
fallback: /* @__PURE__ */ d("div", {
|
|
43
48
|
className: "p-6 text-center text-text-secondary",
|
|
44
|
-
children: "Something went wrong loading this module. Please refresh the page."
|
|
49
|
+
children: y("files.root.errorFallback", "Something went wrong loading this module. Please refresh the page.")
|
|
45
50
|
}),
|
|
46
|
-
children: /* @__PURE__ */
|
|
47
|
-
client:
|
|
48
|
-
children: /* @__PURE__ */ u
|
|
49
|
-
workspaceId:
|
|
51
|
+
children: /* @__PURE__ */ d(c, {
|
|
52
|
+
client: f,
|
|
53
|
+
children: /* @__PURE__ */ d(u, {
|
|
54
|
+
workspaceId: v ?? null,
|
|
50
55
|
gateway: "global",
|
|
51
56
|
defaultEnabled: !1,
|
|
52
|
-
children: /* @__PURE__ */
|
|
53
|
-
...
|
|
54
|
-
children: /* @__PURE__ */
|
|
57
|
+
children: /* @__PURE__ */ d(e, {
|
|
58
|
+
...s,
|
|
59
|
+
children: /* @__PURE__ */ d(i, {})
|
|
55
60
|
})
|
|
56
61
|
})
|
|
57
62
|
})
|
|
58
63
|
});
|
|
59
64
|
};
|
|
60
65
|
//#endregion
|
|
61
|
-
export {
|
|
66
|
+
export { p as FilesRoot };
|
|
62
67
|
|
|
63
68
|
//# sourceMappingURL=FilesRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilesRoot.js","names":[],"sources":["../../src/files/FilesRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen } from \"lucide-react\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { FilesProvider } from \"./providers/FilesProvider\";\nimport type { NavItem, FilesRootProps } from \"./types\";\nimport { FilesRoutes } from \"./FilesRoutes\";\nimport { setFilesConfig, buildGraphQLEndpoint } from \"../config\";\n\n// Create a query client instance.\n//\n// NOTE: every data hook in this MFE is Apollo (it shares the shell's Apollo\n// client) — there are no React Query queries/mutations here. Refetching after a\n// mutation is owned by the shell's Apollo layer (mutationAutoRefreshLink +\n// refetchActiveGatewayQueries in @burdenoff/fe-libs). A previous\n// `mutationCache.onSuccess -> invalidateQueries()` was added here to fix stale\n// lists, but it was inert (no React Query mutations exist to trigger it); the\n// real cause was a stale 15s GraphQL read cache in fe-libs, now fixed there.\n// This provider remains only because shared fe-libs components rendered inside\n// the MFE expect a React Query context.\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n refetchOnWindowFocus: false,\n },\n },\n});\n\n/**\n * Root component for the Files microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { FilesRoot } from '@burdenoff/micro-fe/files';\n *\n * function App() {\n * return (\n * <Route path=\"/files/*\" element={\n * <FilesRoot\n * basePath=\"/files\"\n * navigate={navigate}\n * currentUser={user}\n * workspaceId=\"ws-123\"\n * apiGatewayUrl=\"https://api.example.com\"\n * authToken=\"token\"\n * />\n * } />\n * );\n * }\n * ```\n */\nexport const FilesRoot: FC<FilesRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n apiGatewayUrl,\n authToken,\n workspaceId,\n } = props;\n\n // Initialize runtime configuration from props\n useEffect(() => {\n if (apiGatewayUrl) {\n const fullEndpoint = apiGatewayUrl.includes(\"/graphql\")\n ? apiGatewayUrl\n : buildGraphQLEndpoint(apiGatewayUrl, \"workspaces\");\n\n setFilesConfig({\n apiGatewayUrl: fullEndpoint,\n apiGatewayBaseUrl: apiGatewayUrl,\n authToken,\n });\n }\n }, [apiGatewayUrl, authToken]);\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"files-browse\",\n label: \"All Files\",\n path: joinPath(basePath, \"browse\"),\n icon: <FolderOpen size={20} />,\n order: 10,\n group: \"files\",\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath]);\n\n // Register footer items (example: storage status in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"files-status\",\n label: \"Files Ready\",\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading this module. Please refresh the page.\n </div>\n }\n >\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={false}\n >\n <FilesProvider {...props}>\n <FilesRoutes />\n </FilesProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilesRoot.js","names":[],"sources":["../../src/files/FilesRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { FolderOpen } from \"lucide-react\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { FilesProvider } from \"./providers/FilesProvider\";\nimport type { NavItem, FilesRootProps } from \"./types\";\nimport { FilesRoutes } from \"./FilesRoutes\";\nimport { setFilesConfig, buildGraphQLEndpoint } from \"../config\";\nimport { useTr } from \"../shared/hooks/useTr\";\n\n// Create a query client instance.\n//\n// NOTE: every data hook in this MFE is Apollo (it shares the shell's Apollo\n// client) — there are no React Query queries/mutations here. Refetching after a\n// mutation is owned by the shell's Apollo layer (mutationAutoRefreshLink +\n// refetchActiveGatewayQueries in @burdenoff/fe-libs). A previous\n// `mutationCache.onSuccess -> invalidateQueries()` was added here to fix stale\n// lists, but it was inert (no React Query mutations exist to trigger it); the\n// real cause was a stale 15s GraphQL read cache in fe-libs, now fixed there.\n// This provider remains only because shared fe-libs components rendered inside\n// the MFE expect a React Query context.\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n refetchOnWindowFocus: false,\n },\n },\n});\n\n/**\n * Root component for the Files microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { FilesRoot } from '@burdenoff/micro-fe/files';\n *\n * function App() {\n * return (\n * <Route path=\"/files/*\" element={\n * <FilesRoot\n * basePath=\"/files\"\n * navigate={navigate}\n * currentUser={user}\n * workspaceId=\"ws-123\"\n * apiGatewayUrl=\"https://api.example.com\"\n * authToken=\"token\"\n * />\n * } />\n * );\n * }\n * ```\n */\nexport const FilesRoot: FC<FilesRootProps> = (props) => {\n const {\n registerNavItems,\n registerFooterItems,\n basePath = \"/\",\n apiGatewayUrl,\n authToken,\n workspaceId,\n } = props;\n const tr = useTr();\n\n // Initialize runtime configuration from props\n useEffect(() => {\n if (apiGatewayUrl) {\n const fullEndpoint = apiGatewayUrl.includes(\"/graphql\")\n ? apiGatewayUrl\n : buildGraphQLEndpoint(apiGatewayUrl, \"workspaces\");\n\n setFilesConfig({\n apiGatewayUrl: fullEndpoint,\n apiGatewayBaseUrl: apiGatewayUrl,\n authToken,\n });\n }\n }, [apiGatewayUrl, authToken]);\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: \"files-browse\",\n label: tr(\"files.nav.allFiles\", \"All Files\"),\n path: joinPath(basePath, \"browse\"),\n icon: <FolderOpen size={20} />,\n order: 10,\n group: \"files\",\n },\n ];\n\n // Register and get cleanup function\n const cleanup = registerNavItems(navItems);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n // Register footer items (example: storage status in left section)\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"files-status\",\n label: tr(\"files.nav.statusReady\", \"Files Ready\"),\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n \"files.root.errorFallback\",\n \"Something went wrong loading this module. Please refresh the page.\",\n )}\n </div>\n }\n >\n <QueryClientProvider client={queryClient}>\n <FeatureFlagProvider\n workspaceId={workspaceId ?? null}\n gateway=\"global\"\n defaultEnabled={false}\n >\n <FilesProvider {...props}>\n <FilesRoutes />\n </FilesProvider>\n </FeatureFlagProvider>\n </QueryClientProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;;AAuBA,IAAM,IAAc,IAAI,EAAY,EAClC,gBAAgB,EACd,SAAS;CACP,WAAW,MAAO,KAAK;CACvB,sBAAsB;CACvB,EACF,EACF,CAAC,EA6BW,KAAiC,MAAU;CACtD,IAAM,EACJ,qBACA,wBACA,cAAW,KACX,kBACA,cACA,mBACE,GACE,IAAK,GAAO;AAGlB,SAAgB;AACd,EAAI,KAKF,EAAe;GACb,eALmB,EAAc,SAAS,WAAW,GACnD,IACA,EAAqB,GAAe,aAAa;GAInD,mBAAmB;GACnB;GACD,CAAC;IAEH,CAAC,GAAe,EAAU,CAAC;CAG9B,IAAM,KAAY,GAAc,OACZ,EAAK,SAAS,IAAI,GAAG,EAAK,MAAM,GAAG,GAAG,GAAG,MACzC,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI;AA4CtD,QAvCA,QAAgB;AACT,QAiBL,QAHgB,EAZY,CAC1B;GACE,IAAI;GACJ,OAAO,EAAG,sBAAsB,YAAY;GAC5C,MAAM,EAAS,GAAU,SAAS;GAClC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;GAC9B,OAAO;GACP,OAAO;GACR,CACF,CAGyC;IAIzC;EAAC;EAAkB;EAAU;EAAG,CAAC,EAGpC,QAAgB;AACT,QAaL,QADgB,EAVI,CAClB;GACE,IAAI;GACJ,OAAO,EAAG,yBAAyB,cAAc;GACjD,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAE+C;IAE/C,CAAC,GAAqB,EAAG,CAAC,EAG3B,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,4BACA,qEACD;GACG,CAAA;YAGR,kBAAC,GAAD;GAAqB,QAAQ;aAC3B,kBAAC,GAAD;IACE,aAAa,KAAe;IAC5B,SAAQ;IACR,gBAAgB;cAEhB,kBAAC,GAAD;KAAe,GAAI;eACjB,kBAAC,GAAD,EAAe,CAAA;KACD,CAAA;IACI,CAAA;GACF,CAAA;EACR,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageBanner.d.ts","sourceRoot":"","sources":["../../../src/files/components/StorageBanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAGhC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"StorageBanner.d.ts","sourceRoot":"","sources":["../../../src/files/components/StorageBanner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAGhC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAUlE,UAAU,kBAAkB;IAC1B,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAsGhD,CAAC;AAEF,UAAU,qBAAqB;IAC7B,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAED,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CAyBtD,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -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"}
|