@burdenoff/microfe-workspaces 2026.802.1 → 2026.829.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/generated/global-operations.js +44 -1
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/wspace-operations.js +18 -18
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/hooks/useWorkspaceMutations.js +84 -61
- package/dist/hooks/useWorkspaceMutations.js.map +1 -1
- package/dist/pages/MembersListPage.js +3 -3
- package/dist/pages/MembersListPage.js.map +1 -1
- package/dist/pages/ProjectsListPage.js +3 -3
- package/dist/pages/ProjectsListPage.js.map +1 -1
- package/dist/pages/WorkspaceDetailPage.js +152 -114
- package/dist/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/pages/WorkspaceInvitePage.js +2 -2
- package/dist/pages/WorkspaceInvitePage.js.map +1 -1
- package/dist/pages/dashboard/DashboardCustomizer.js +62 -62
- package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
- package/dist/pages/dashboard/DashboardPage.js +88 -69
- package/dist/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/pages/dashboard/config.js +77 -1
- package/dist/pages/dashboard/config.js.map +1 -1
- package/dist/pages/dashboard/widgets/AgentsWidget.js +56 -54
- package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/GroupsWidget.js +29 -31
- package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/MembersWidget.js +33 -33
- package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/ProjectsWidget.js +42 -44
- package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/QuickActionsWidget.js +171 -161
- package/dist/pages/dashboard/widgets/QuickActionsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js +54 -44
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/SessionsWidget.js +57 -41
- package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/StorageWidget.js +52 -54
- package/dist/pages/dashboard/widgets/StorageWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/TagCloudWidget.js +28 -30
- package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/TipsWidget.js +141 -131
- package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +46 -40
- package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/VibesWidget.js +44 -34
- package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WelcomeWidget.js +43 -41
- package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js +68 -62
- package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +50 -44
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js +59 -49
- package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkspacesWidget.js +35 -33
- package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,76 +1,92 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { tWithFallback as e } from "../../../utils/i18n.js";
|
|
2
|
+
import { WidgetWrapper as t } from "../WidgetWrapper.js";
|
|
3
|
+
import { ArrowRight as n, Terminal as r } from "lucide-react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Badge as o, Button as s, IllustratedEmptyState as c } from "@burdenoff/fe-libs/ui";
|
|
6
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
7
|
//#region src/pages/dashboard/widgets/SessionsWidget.tsx
|
|
6
|
-
var
|
|
8
|
+
var u = {
|
|
7
9
|
RUNNING: "bg-status-success-bg text-status-success-text",
|
|
8
10
|
ACTIVE: "bg-status-success-bg text-status-success-text",
|
|
9
11
|
STARTING: "bg-status-warning-bg text-status-warning-text",
|
|
10
12
|
STOPPED: "bg-text-tertiary/20 text-text-tertiary",
|
|
11
13
|
ERROR: "bg-status-error-bg text-status-error-text"
|
|
12
|
-
},
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
}, d = new Set(["RUNNING", "ACTIVE"]);
|
|
15
|
+
function f(t, n) {
|
|
16
|
+
switch (n) {
|
|
17
|
+
case "RUNNING": return e(t, "dashboard.sessions.statusRunning", "running");
|
|
18
|
+
case "ACTIVE": return e(t, "dashboard.sessions.statusActive", "active");
|
|
19
|
+
case "STARTING": return e(t, "dashboard.sessions.statusStarting", "starting");
|
|
20
|
+
case "STOPPED": return e(t, "dashboard.sessions.statusStopped", "stopped");
|
|
21
|
+
case "ERROR": return e(t, "dashboard.sessions.statusError", "error");
|
|
22
|
+
default: return n.toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function p(t, n) {
|
|
26
|
+
if (!n) return "";
|
|
27
|
+
let r = Date.now() - new Date(n).getTime(), i = Math.floor(r / 6e4);
|
|
28
|
+
if (i < 1) return e(t, "dashboard.sessions.justNow", "just now");
|
|
29
|
+
if (i < 60) return e(t, "dashboard.sessions.minutesAgo", "{count}m", { count: i });
|
|
30
|
+
let a = Math.floor(i / 60);
|
|
31
|
+
return a < 24 ? e(t, "dashboard.sessions.hoursAgo", "{count}h", { count: a }) : new Date(n).toLocaleDateString(void 0, {
|
|
20
32
|
month: "short",
|
|
21
33
|
day: "numeric"
|
|
22
34
|
});
|
|
23
35
|
}
|
|
24
|
-
function
|
|
25
|
-
let
|
|
26
|
-
return /* @__PURE__ */
|
|
27
|
-
title: "Active Sessions",
|
|
28
|
-
icon: /* @__PURE__ */ r
|
|
36
|
+
function m({ workspaceData: m, isLoading: h, navigate: g }) {
|
|
37
|
+
let { t: _ } = l(), v = m?.activeSessions ?? [];
|
|
38
|
+
return /* @__PURE__ */ i(t, {
|
|
39
|
+
title: e(_, "dashboard.sessions.title", "Active Sessions"),
|
|
40
|
+
icon: /* @__PURE__ */ i(r, { className: "size-4" }),
|
|
29
41
|
size: "medium",
|
|
30
|
-
isLoading:
|
|
31
|
-
unavailable: !
|
|
32
|
-
headerAction: /* @__PURE__ */
|
|
42
|
+
isLoading: h,
|
|
43
|
+
unavailable: !m,
|
|
44
|
+
headerAction: /* @__PURE__ */ a(s, {
|
|
33
45
|
variant: "ghost",
|
|
34
46
|
size: "sm",
|
|
35
47
|
className: "text-xs",
|
|
36
|
-
onClick: () =>
|
|
37
|
-
children: [
|
|
48
|
+
onClick: () => g("/vibecontrols/sessions"),
|
|
49
|
+
children: [
|
|
50
|
+
e(_, "dashboard.sessions.viewAll", "View all"),
|
|
51
|
+
" ",
|
|
52
|
+
/* @__PURE__ */ i(n, { className: "ml-1 size-3" })
|
|
53
|
+
]
|
|
38
54
|
}),
|
|
39
|
-
children:
|
|
55
|
+
children: v.length === 0 ? /* @__PURE__ */ i(c, {
|
|
40
56
|
illustration: "empty-sessions",
|
|
41
|
-
title: "No active sessions",
|
|
57
|
+
title: e(_, "dashboard.sessions.emptyTitle", "No active sessions"),
|
|
42
58
|
className: "py-6"
|
|
43
|
-
}) : /* @__PURE__ */
|
|
59
|
+
}) : /* @__PURE__ */ i("ul", {
|
|
44
60
|
className: "space-y-1.5",
|
|
45
|
-
children:
|
|
61
|
+
children: v.slice(0, 5).map((t) => /* @__PURE__ */ i("li", { children: /* @__PURE__ */ a("button", {
|
|
46
62
|
type: "button",
|
|
47
|
-
onClick: () =>
|
|
63
|
+
onClick: () => g(`/vibecontrols/sessions/${t.id}`),
|
|
48
64
|
className: "flex w-full items-center justify-between gap-2 rounded-md p-2 text-left transition-colors hover:bg-accent/5",
|
|
49
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ a("div", {
|
|
50
66
|
className: "min-w-0 flex-1",
|
|
51
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ i("p", {
|
|
52
68
|
className: "truncate text-sm font-medium text-text-primary",
|
|
53
|
-
children:
|
|
54
|
-
}),
|
|
69
|
+
children: t.name || e(_, "dashboard.sessions.unnamedSession", "Session {id}", { id: t.id.slice(0, 8) })
|
|
70
|
+
}), t.agent?.name ? /* @__PURE__ */ i("p", {
|
|
55
71
|
className: "truncate text-[11px] text-text-secondary",
|
|
56
|
-
children:
|
|
72
|
+
children: t.agent.name
|
|
57
73
|
}) : null]
|
|
58
|
-
}), /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ a("div", {
|
|
59
75
|
className: "flex flex-col items-end gap-1",
|
|
60
|
-
children: [/* @__PURE__ */
|
|
76
|
+
children: [/* @__PURE__ */ i(o, {
|
|
61
77
|
variant: "outline",
|
|
62
|
-
className: `text-[10px] ${
|
|
63
|
-
children:
|
|
64
|
-
}),
|
|
78
|
+
className: `text-[10px] ${u[t.status] ?? ""} ${d.has(t.status) ? "motion-safe:animate-vc-glow-pulse" : ""}`,
|
|
79
|
+
children: f(_, t.status)
|
|
80
|
+
}), t.startedAt ? /* @__PURE__ */ i("span", {
|
|
65
81
|
className: "text-[10px] text-text-secondary",
|
|
66
|
-
children:
|
|
82
|
+
children: p(_, t.startedAt)
|
|
67
83
|
}) : null]
|
|
68
84
|
})]
|
|
69
|
-
}) },
|
|
85
|
+
}) }, t.id))
|
|
70
86
|
})
|
|
71
87
|
});
|
|
72
88
|
}
|
|
73
89
|
//#endregion
|
|
74
|
-
export {
|
|
90
|
+
export { m as SessionsWidget };
|
|
75
91
|
|
|
76
92
|
//# sourceMappingURL=SessionsWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/SessionsWidget.tsx"],"sourcesContent":["import { Terminal, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst STATUS_BADGE: Record<string, string> = {\n RUNNING: 'bg-status-success-bg text-status-success-text',\n ACTIVE: 'bg-status-success-bg text-status-success-text',\n STARTING: 'bg-status-warning-bg text-status-warning-text',\n STOPPED: 'bg-text-tertiary/20 text-text-tertiary',\n ERROR: 'bg-status-error-bg text-status-error-text',\n};\n\n// Live statuses get a subtle opacity pulse (reduced-motion-safe, no movement).\nconst LIVE_STATUSES = new Set(['RUNNING', 'ACTIVE']);\n\nfunction formatRelative(dateString: string | null | undefined): string {\n if (!dateString) return '';\n const diffMs = Date.now() - new Date(dateString).getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return 'just now';\n if (diffMin < 60)
|
|
1
|
+
{"version":3,"file":"SessionsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/SessionsWidget.tsx"],"sourcesContent":["import { Terminal, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nconst STATUS_BADGE: Record<string, string> = {\n RUNNING: 'bg-status-success-bg text-status-success-text',\n ACTIVE: 'bg-status-success-bg text-status-success-text',\n STARTING: 'bg-status-warning-bg text-status-warning-text',\n STOPPED: 'bg-text-tertiary/20 text-text-tertiary',\n ERROR: 'bg-status-error-bg text-status-error-text',\n};\n\n// Live statuses get a subtle opacity pulse (reduced-motion-safe, no movement).\nconst LIVE_STATUSES = new Set(['RUNNING', 'ACTIVE']);\n\n/**\n * Badge copy for a session status, resolved through i18n with **literal**\n * keys — a lookup table would force a computed key, which the key extractor\n * (fluidgrids-app/scripts/extract-i18n-keys.ts) cannot see and the seeder can\n * therefore never ship. Unknown statuses keep the previous behaviour of\n * lower-casing the raw enum value. [BOFF-5997]\n */\nfunction resolveStatusLabel(t: I18nContextValue['t'], status: string): string {\n switch (status) {\n case 'RUNNING':\n return tWithFallback(t, 'dashboard.sessions.statusRunning', 'running');\n case 'ACTIVE':\n return tWithFallback(t, 'dashboard.sessions.statusActive', 'active');\n case 'STARTING':\n return tWithFallback(t, 'dashboard.sessions.statusStarting', 'starting');\n case 'STOPPED':\n return tWithFallback(t, 'dashboard.sessions.statusStopped', 'stopped');\n case 'ERROR':\n return tWithFallback(t, 'dashboard.sessions.statusError', 'error');\n default:\n return status.toLowerCase();\n }\n}\n\nfunction formatRelative(t: I18nContextValue['t'], dateString: string | null | undefined): string {\n if (!dateString) return '';\n const diffMs = Date.now() - new Date(dateString).getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tWithFallback(t, 'dashboard.sessions.justNow', 'just now');\n if (diffMin < 60)\n return tWithFallback(t, 'dashboard.sessions.minutesAgo', '{count}m', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tWithFallback(t, 'dashboard.sessions.hoursAgo', '{count}h', { count: diffHr });\n return new Date(dateString).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n });\n}\n\nexport function SessionsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const sessions = workspaceData?.activeSessions ?? [];\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.sessions.title', 'Active Sessions')}\n icon={<Terminal className=\"size-4\" />}\n size=\"medium\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n onClick={() => navigate('/vibecontrols/sessions')}\n >\n {tWithFallback(t, 'dashboard.sessions.viewAll', 'View all')}{' '}\n <ArrowRight className=\"ml-1 size-3\" />\n </Button>\n }\n >\n {sessions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-sessions\"\n title={tWithFallback(t, 'dashboard.sessions.emptyTitle', 'No active sessions')}\n className=\"py-6\"\n />\n ) : (\n <ul className=\"space-y-1.5\">\n {sessions.slice(0, 5).map((s) => (\n <li key={s.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`/vibecontrols/sessions/${s.id}`)}\n className=\"flex w-full items-center justify-between gap-2 rounded-md p-2 text-left transition-colors hover:bg-accent/5\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {s.name ||\n tWithFallback(t, 'dashboard.sessions.unnamedSession', 'Session {id}', {\n id: s.id.slice(0, 8),\n })}\n </p>\n {s.agent?.name ? (\n <p className=\"truncate text-[11px] text-text-secondary\">{s.agent.name}</p>\n ) : null}\n </div>\n <div className=\"flex flex-col items-end gap-1\">\n <Badge\n variant=\"outline\"\n className={`text-[10px] ${STATUS_BADGE[s.status] ?? ''} ${\n LIVE_STATUSES.has(s.status) ? 'motion-safe:animate-vc-glow-pulse' : ''\n }`}\n >\n {resolveStatusLabel(t, s.status)}\n </Badge>\n {s.startedAt ? (\n <span className=\"text-[10px] text-text-secondary\">\n {formatRelative(t, s.startedAt)}\n </span>\n ) : null}\n </div>\n </button>\n </li>\n ))}\n </ul>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,84 +1,82 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { tWithFallback as e } from "../../../utils/i18n.js";
|
|
2
|
+
import { WidgetWrapper as t } from "../WidgetWrapper.js";
|
|
3
|
+
import { HardDrive as n } from "lucide-react";
|
|
4
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
5
|
+
import { useUpgradePrompt as a } from "@burdenoff/fe-libs/shared/hooks";
|
|
6
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
7
|
//#region src/pages/dashboard/widgets/StorageWidget.tsx
|
|
6
|
-
var
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"TB"
|
|
15
|
-
], r = Math.floor(Math.log(e) / Math.log(t));
|
|
16
|
-
return `${parseFloat((e / t ** +r).toFixed(1))} ${n[r]}`;
|
|
17
|
-
}
|
|
18
|
-
function c(e, t) {
|
|
19
|
-
let n = 1024, r = [
|
|
20
|
-
"B",
|
|
21
|
-
"KB",
|
|
22
|
-
"MB",
|
|
23
|
-
"GB",
|
|
24
|
-
"TB"
|
|
8
|
+
var s = 50 * 1024 * 1024, c = 5 * 1024 * 1024 * 1024;
|
|
9
|
+
function l(t) {
|
|
10
|
+
return [
|
|
11
|
+
e(t, "dashboard.storage.unitBytes", "B"),
|
|
12
|
+
e(t, "dashboard.storage.unitKilobytes", "KB"),
|
|
13
|
+
e(t, "dashboard.storage.unitMegabytes", "MB"),
|
|
14
|
+
e(t, "dashboard.storage.unitGigabytes", "GB"),
|
|
15
|
+
e(t, "dashboard.storage.unitTerabytes", "TB")
|
|
25
16
|
];
|
|
26
|
-
if (t <= 0) return s(e);
|
|
27
|
-
let i = Math.min(Math.floor(Math.log(t) / Math.log(n)), r.length - 1), a = e / n ** +i;
|
|
28
|
-
return `${Number.isInteger(a) ? a.toString() : a.toFixed(1)} ${r[i]}`;
|
|
29
17
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
18
|
+
function u(t, n) {
|
|
19
|
+
if (n === 0) return `0 ${e(t, "dashboard.storage.unitBytes", "B")}`;
|
|
20
|
+
let r = 1024, i = l(t), a = Math.floor(Math.log(n) / Math.log(r));
|
|
21
|
+
return `${parseFloat((n / r ** +a).toFixed(1))} ${i[a]}`;
|
|
22
|
+
}
|
|
23
|
+
function d(e, t, n) {
|
|
24
|
+
let r = 1024, i = l(e);
|
|
25
|
+
if (n <= 0) return u(e, t);
|
|
26
|
+
let a = Math.min(Math.floor(Math.log(n) / Math.log(r)), i.length - 1), o = t / r ** +a;
|
|
27
|
+
return `${Number.isInteger(o) ? o.toString() : o.toFixed(1)} ${i[a]}`;
|
|
28
|
+
}
|
|
29
|
+
function f({ workspaceData: l, isLoading: f }) {
|
|
30
|
+
let { t: p } = o(), { isFree: m } = a({ resource: "storage" }), h = l?.storageUsage, g = m ? s : c, _ = h?.usedBytes ?? 0, v = h?.totalBytes && h.totalBytes > 0 ? h.totalBytes : g, y = v > 0 ? Math.min(_ / v * 100, 100) : 0, b = y > 80 ? "bg-status-error-bg" : y > 60 ? "bg-status-warning-bg" : "bg-status-success-bg";
|
|
31
|
+
return /* @__PURE__ */ r(t, {
|
|
32
|
+
title: e(p, "dashboard.storage.title", "Storage"),
|
|
33
|
+
icon: /* @__PURE__ */ r(n, { className: "size-4" }),
|
|
35
34
|
size: "small",
|
|
36
|
-
isLoading:
|
|
37
|
-
children: /* @__PURE__ */
|
|
35
|
+
isLoading: f,
|
|
36
|
+
children: /* @__PURE__ */ i("div", {
|
|
38
37
|
className: "space-y-3",
|
|
39
|
-
children: [/* @__PURE__ */
|
|
38
|
+
children: [/* @__PURE__ */ i("div", {
|
|
40
39
|
className: "space-y-1.5",
|
|
41
40
|
children: [
|
|
42
|
-
/* @__PURE__ */
|
|
41
|
+
/* @__PURE__ */ i("div", {
|
|
43
42
|
className: "flex justify-between text-xs",
|
|
44
|
-
children: [/* @__PURE__ */
|
|
43
|
+
children: [/* @__PURE__ */ r("span", {
|
|
45
44
|
className: "text-text-secondary",
|
|
46
|
-
children: "Used"
|
|
45
|
+
children: e(p, "dashboard.storage.used", "Used")
|
|
47
46
|
}), /* @__PURE__ */ r("span", {
|
|
48
47
|
className: "font-medium text-text-primary",
|
|
49
|
-
children:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]
|
|
48
|
+
children: e(p, "dashboard.storage.usedOfTotal", "{used} / {total}", {
|
|
49
|
+
used: d(p, _, v),
|
|
50
|
+
total: u(p, v)
|
|
51
|
+
})
|
|
54
52
|
})]
|
|
55
53
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ r("div", {
|
|
57
55
|
className: "h-2 w-full bg-bg-canvas rounded-full overflow-hidden",
|
|
58
|
-
children: /* @__PURE__ */
|
|
59
|
-
className: `h-full rounded-full ${
|
|
60
|
-
style: { width: `${
|
|
56
|
+
children: /* @__PURE__ */ r("div", {
|
|
57
|
+
className: `h-full rounded-full ${b} transition-all duration-500`,
|
|
58
|
+
style: { width: `${y}%` }
|
|
61
59
|
})
|
|
62
60
|
}),
|
|
63
|
-
/* @__PURE__ */
|
|
61
|
+
/* @__PURE__ */ r("p", {
|
|
64
62
|
className: "text-[11px] text-text-secondary text-right",
|
|
65
|
-
children:
|
|
63
|
+
children: e(p, "dashboard.storage.percentUsed", "{percent}% used", { percent: y.toFixed(1) })
|
|
66
64
|
})
|
|
67
65
|
]
|
|
68
|
-
}),
|
|
66
|
+
}), h?.fileCount != null && /* @__PURE__ */ i("div", {
|
|
69
67
|
className: "flex items-center justify-between text-xs",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
68
|
+
children: [/* @__PURE__ */ r("span", {
|
|
71
69
|
className: "text-text-secondary",
|
|
72
|
-
children: "Total files"
|
|
73
|
-
}), /* @__PURE__ */
|
|
70
|
+
children: e(p, "dashboard.storage.totalFiles", "Total files")
|
|
71
|
+
}), /* @__PURE__ */ r("span", {
|
|
74
72
|
className: "font-medium text-text-primary",
|
|
75
|
-
children:
|
|
73
|
+
children: h.fileCount.toLocaleString()
|
|
76
74
|
})]
|
|
77
75
|
})]
|
|
78
76
|
})
|
|
79
77
|
});
|
|
80
78
|
}
|
|
81
79
|
//#endregion
|
|
82
|
-
export {
|
|
80
|
+
export { f as StorageWidget };
|
|
83
81
|
|
|
84
82
|
//# sourceMappingURL=StorageWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/StorageWidget.tsx"],"sourcesContent":["import { HardDrive } from 'lucide-react';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst FREE_TIER_TOTAL_BYTES = 50 * 1024 * 1024; // 50 MB\nconst PAID_TIER_TOTAL_BYTES = 5 * 1024 * 1024 * 1024; // 5 GB\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) {\n // For 0 we want to render the unit the user will see for the total\n // (e.g. \"0 MB / 50 MB\"), so let the caller pick a unit explicitly.\n return
|
|
1
|
+
{"version":3,"file":"StorageWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/StorageWidget.tsx"],"sourcesContent":["import { HardDrive } from 'lucide-react';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { I18nContextValue } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport { tWithFallback } from '../../../utils/i18n';\nimport type { WidgetProps } from '../types';\n\nconst FREE_TIER_TOTAL_BYTES = 50 * 1024 * 1024; // 50 MB\nconst PAID_TIER_TOTAL_BYTES = 5 * 1024 * 1024 * 1024; // 5 GB\n\n/**\n * Byte-size unit symbols, each translated through a **literal** key — an array\n * of keys indexed by magnitude would hand the translator a computed key, which\n * the extractor (fluidgrids-app/scripts/extract-i18n-keys.ts) cannot see and\n * the seeder can therefore never ship. Order is load-bearing: the index is the\n * power of 1024. [BOFF-5997]\n */\nfunction byteUnits(t: I18nContextValue['t']): string[] {\n return [\n tWithFallback(t, 'dashboard.storage.unitBytes', 'B'),\n tWithFallback(t, 'dashboard.storage.unitKilobytes', 'KB'),\n tWithFallback(t, 'dashboard.storage.unitMegabytes', 'MB'),\n tWithFallback(t, 'dashboard.storage.unitGigabytes', 'GB'),\n tWithFallback(t, 'dashboard.storage.unitTerabytes', 'TB'),\n ];\n}\n\nfunction formatBytes(t: I18nContextValue['t'], bytes: number): string {\n if (bytes === 0) {\n // For 0 we want to render the unit the user will see for the total\n // (e.g. \"0 MB / 50 MB\"), so let the caller pick a unit explicitly.\n return `0 ${tWithFallback(t, 'dashboard.storage.unitBytes', 'B')}`;\n }\n const k = 1024;\n const sizes = byteUnits(t);\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;\n}\n\n/**\n * Pick the display unit of the *total* so the \"used / total\" line is\n * consistent (e.g. \"0 MB / 50 MB\" not \"0 B / 50 MB\").\n */\nfunction formatBytesAs(t: I18nContextValue['t'], bytes: number, totalBytes: number): string {\n const k = 1024;\n const sizes = byteUnits(t);\n if (totalBytes <= 0) return formatBytes(t, bytes);\n const totalIdx = Math.min(Math.floor(Math.log(totalBytes) / Math.log(k)), sizes.length - 1);\n const value = bytes / Math.pow(k, totalIdx);\n // Render as integer when exact (e.g. 0, 50); else one decimal.\n const rendered = Number.isInteger(value) ? value.toString() : value.toFixed(1);\n return `${rendered} ${sizes[totalIdx]}`;\n}\n\nexport function StorageWidget({ workspaceData, isLoading }: WidgetProps) {\n const { t } = useI18n();\n const { isFree } = useUpgradePrompt({ resource: 'storage' });\n const storage = workspaceData?.storageUsage;\n\n // Fall back to plan-tier defaults when the backend hasn't reported usage\n // yet. Storage is core, so \"metrics unavailable\" is not an acceptable UX.\n const fallbackTotalBytes = isFree ? FREE_TIER_TOTAL_BYTES : PAID_TIER_TOTAL_BYTES;\n const usedBytes = storage?.usedBytes ?? 0;\n const totalBytes =\n storage?.totalBytes && storage.totalBytes > 0 ? storage.totalBytes : fallbackTotalBytes;\n const percentage = totalBytes > 0 ? Math.min((usedBytes / totalBytes) * 100, 100) : 0;\n const barColor =\n percentage > 80\n ? 'bg-status-error-bg'\n : percentage > 60\n ? 'bg-status-warning-bg'\n : 'bg-status-success-bg';\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.storage.title', 'Storage')}\n icon={<HardDrive className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n >\n <div className=\"space-y-3\">\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between text-xs\">\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'dashboard.storage.used', 'Used')}\n </span>\n <span className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'dashboard.storage.usedOfTotal', '{used} / {total}', {\n used: formatBytesAs(t, usedBytes, totalBytes),\n total: formatBytes(t, totalBytes),\n })}\n </span>\n </div>\n <div className=\"h-2 w-full bg-bg-canvas rounded-full overflow-hidden\">\n <div\n className={`h-full rounded-full ${barColor} transition-all duration-500`}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"text-[11px] text-text-secondary text-right\">\n {tWithFallback(t, 'dashboard.storage.percentUsed', '{percent}% used', {\n percent: percentage.toFixed(1),\n })}\n </p>\n </div>\n {storage?.fileCount != null && (\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-text-secondary\">\n {tWithFallback(t, 'dashboard.storage.totalFiles', 'Total files')}\n </span>\n <span className=\"font-medium text-text-primary\">\n {storage.fileCount.toLocaleString()}\n </span>\n </div>\n )}\n </div>\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAQA,IAAM,IAAwB,KAAK,OAAO,MACpC,IAAwB,IAAI,OAAO,OAAO;AAShD,SAAS,EAAU,GAAoC;AACrD,QAAO;EACL,EAAc,GAAG,+BAA+B,IAAI;EACpD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EACzD,EAAc,GAAG,mCAAmC,KAAK;EAC1D;;AAGH,SAAS,EAAY,GAA0B,GAAuB;AACpE,KAAI,MAAU,EAGZ,QAAO,KAAK,EAAc,GAAG,+BAA+B,IAAI;CAElE,IAAM,IAAI,MACJ,IAAQ,EAAU,EAAE,EACpB,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,QAAO,GAAG,YAAY,IAAiB,MAAG,GAAI,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM;;AAOrE,SAAS,EAAc,GAA0B,GAAe,GAA4B;CAC1F,IAAM,IAAI,MACJ,IAAQ,EAAU,EAAE;AAC1B,KAAI,KAAc,EAAG,QAAO,EAAY,GAAG,EAAM;CACjD,IAAM,IAAW,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAW,GAAG,KAAK,IAAI,EAAE,CAAC,EAAE,EAAM,SAAS,EAAE,EACrF,IAAQ,IAAiB,MAAG;AAGlC,QAAO,GADU,OAAO,UAAU,EAAM,GAAG,EAAM,UAAU,GAAG,EAAM,QAAQ,EAAE,CAC3D,GAAG,EAAM;;AAG9B,SAAgB,EAAc,EAAE,kBAAe,gBAA0B;CACvE,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,cAAW,EAAiB,EAAE,UAAU,WAAW,CAAC,EACtD,IAAU,GAAe,cAIzB,IAAqB,IAAS,IAAwB,GACtD,IAAY,GAAS,aAAa,GAClC,IACJ,GAAS,cAAc,EAAQ,aAAa,IAAI,EAAQ,aAAa,GACjE,IAAa,IAAa,IAAI,KAAK,IAAK,IAAY,IAAc,KAAK,IAAI,GAAG,GAC9E,IACJ,IAAa,KACT,uBACA,IAAa,KACX,yBACA;AAER,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,2BAA2B,UAAU;EAC7D,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;EACtC,MAAK;EACM;YAEX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAc,GAAG,0BAA0B,OAAO;OAC9C,CAAA,EACP,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAc,GAAG,iCAAiC,oBAAoB;QACrE,MAAM,EAAc,GAAG,GAAW,EAAW;QAC7C,OAAO,EAAY,GAAG,EAAW;QAClC,CAAC;OACG,CAAA,CACH;;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAW,uBAAuB,EAAS;OAC3C,OAAO,EAAE,OAAO,GAAG,EAAW,IAAI;OAClC,CAAA;MACE,CAAA;KACN,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAc,GAAG,iCAAiC,mBAAmB,EACpE,SAAS,EAAW,QAAQ,EAAE,EAC/B,CAAC;MACA,CAAA;KACA;OACL,GAAS,aAAa,QACrB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,EAAc,GAAG,gCAAgC,cAAc;KAC3D,CAAA,EACP,kBAAC,QAAD;KAAM,WAAU;eACb,EAAQ,UAAU,gBAAgB;KAC9B,CAAA,CACH;MAEJ;;EACQ,CAAA"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { tWithFallback as e } from "../../../utils/i18n.js";
|
|
2
|
+
import { WidgetWrapper as t } from "../WidgetWrapper.js";
|
|
3
|
+
import { ArrowRight as n, Tags as r } from "lucide-react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Badge as o, Button as s, IllustratedEmptyState as c } from "@burdenoff/fe-libs/ui";
|
|
6
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
7
|
//#region src/pages/dashboard/widgets/TagCloudWidget.tsx
|
|
6
|
-
var
|
|
8
|
+
var u = [
|
|
7
9
|
"bg-status-info-bg-subtle text-status-info-text border-border-default",
|
|
8
10
|
"bg-status-success-bg-subtle text-status-success-text border-border-default",
|
|
9
11
|
"bg-accent-purple-subtle text-accent-purple border-border-default",
|
|
@@ -13,48 +15,44 @@ var c = [
|
|
|
13
15
|
"bg-accent-orange-subtle text-accent-orange border-border-default",
|
|
14
16
|
"bg-accent-rose-subtle text-accent-rose border-border-default"
|
|
15
17
|
];
|
|
16
|
-
function
|
|
17
|
-
let
|
|
18
|
-
return /* @__PURE__ */
|
|
19
|
-
title: "Tags",
|
|
20
|
-
icon: /* @__PURE__ */ r
|
|
18
|
+
function d({ workspaceData: d, isLoading: f, navigate: p }) {
|
|
19
|
+
let { t: m } = l(), h = d?.tags.items ?? [], g = d?.tags.total ?? 0;
|
|
20
|
+
return /* @__PURE__ */ i(t, {
|
|
21
|
+
title: e(m, "dashboard.tagCloud.title", "Tags"),
|
|
22
|
+
icon: /* @__PURE__ */ i(r, { className: "size-4" }),
|
|
21
23
|
size: "small",
|
|
22
|
-
isLoading:
|
|
23
|
-
unavailable: !
|
|
24
|
-
headerAction: /* @__PURE__ */
|
|
24
|
+
isLoading: f,
|
|
25
|
+
unavailable: !d,
|
|
26
|
+
headerAction: /* @__PURE__ */ i(s, {
|
|
25
27
|
variant: "ghost",
|
|
26
28
|
size: "sm",
|
|
27
29
|
className: "text-xs",
|
|
28
|
-
onClick: () =>
|
|
29
|
-
children: /* @__PURE__ */
|
|
30
|
+
onClick: () => p("/tags"),
|
|
31
|
+
children: /* @__PURE__ */ i(n, { className: "size-3" })
|
|
30
32
|
}),
|
|
31
|
-
children:
|
|
33
|
+
children: h.length === 0 ? /* @__PURE__ */ i(c, {
|
|
32
34
|
illustration: "empty-generic",
|
|
33
|
-
title: "No tags created yet",
|
|
34
|
-
description: "Tags help you label and filter projects and content across the workspace.",
|
|
35
|
+
title: e(m, "dashboard.tagCloud.emptyTitle", "No tags created yet"),
|
|
36
|
+
description: e(m, "dashboard.tagCloud.emptyDescription", "Tags help you label and filter projects and content across the workspace."),
|
|
35
37
|
className: "py-4"
|
|
36
|
-
}) : /* @__PURE__ */
|
|
38
|
+
}) : /* @__PURE__ */ a("div", {
|
|
37
39
|
className: "space-y-2",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ i("div", {
|
|
39
41
|
className: "flex flex-wrap gap-1.5",
|
|
40
|
-
children:
|
|
42
|
+
children: h.slice(0, 12).map((e, t) => /* @__PURE__ */ i(o, {
|
|
41
43
|
variant: "outline",
|
|
42
|
-
className: `text-[11px] cursor-pointer hover:opacity-80 transition-opacity ${
|
|
43
|
-
onClick: () =>
|
|
44
|
+
className: `text-[11px] cursor-pointer hover:opacity-80 transition-opacity ${u[t % u.length]}`,
|
|
45
|
+
onClick: () => p("/tags"),
|
|
44
46
|
children: e.label
|
|
45
47
|
}, e.id))
|
|
46
|
-
}),
|
|
48
|
+
}), g > 12 && /* @__PURE__ */ i("p", {
|
|
47
49
|
className: "text-[11px] text-text-secondary text-center",
|
|
48
|
-
children:
|
|
49
|
-
"+",
|
|
50
|
-
p - 12,
|
|
51
|
-
" more tags"
|
|
52
|
-
]
|
|
50
|
+
children: e(m, "dashboard.tagCloud.more", "+{count} more tags", { count: g - 12 })
|
|
53
51
|
})]
|
|
54
52
|
})
|
|
55
53
|
});
|
|
56
54
|
}
|
|
57
55
|
//#endregion
|
|
58
|
-
export {
|
|
56
|
+
export { d as TagCloudWidget };
|
|
59
57
|
|
|
60
58
|
//# sourceMappingURL=TagCloudWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TagCloudWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/TagCloudWidget.tsx"],"sourcesContent":["import { Tags, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nconst tagColors = [\n 'bg-status-info-bg-subtle text-status-info-text border-border-default',\n 'bg-status-success-bg-subtle text-status-success-text border-border-default',\n 'bg-accent-purple-subtle text-accent-purple border-border-default',\n 'bg-status-warning-bg-subtle text-status-warning-text border-border-default',\n 'bg-accent-pink-subtle text-accent-pink border-border-default',\n 'bg-accent-cyan-subtle text-accent-cyan border-border-default',\n 'bg-accent-orange-subtle text-accent-orange border-border-default',\n 'bg-accent-rose-subtle text-accent-rose border-border-default',\n];\n\nexport function TagCloudWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const tags = workspaceData?.tags.items ?? [];\n const total = workspaceData?.tags.total ?? 0;\n\n return (\n <WidgetWrapper\n title
|
|
1
|
+
{"version":3,"file":"TagCloudWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/TagCloudWidget.tsx"],"sourcesContent":["import { Tags, ArrowRight } from 'lucide-react';\nimport { Badge, Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\nimport { tWithFallback } from '../../../utils/i18n';\n\nconst tagColors = [\n 'bg-status-info-bg-subtle text-status-info-text border-border-default',\n 'bg-status-success-bg-subtle text-status-success-text border-border-default',\n 'bg-accent-purple-subtle text-accent-purple border-border-default',\n 'bg-status-warning-bg-subtle text-status-warning-text border-border-default',\n 'bg-accent-pink-subtle text-accent-pink border-border-default',\n 'bg-accent-cyan-subtle text-accent-cyan border-border-default',\n 'bg-accent-orange-subtle text-accent-orange border-border-default',\n 'bg-accent-rose-subtle text-accent-rose border-border-default',\n];\n\nexport function TagCloudWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const { t } = useI18n();\n const tags = workspaceData?.tags.items ?? [];\n const total = workspaceData?.tags.total ?? 0;\n\n return (\n <WidgetWrapper\n title={tWithFallback(t, 'dashboard.tagCloud.title', 'Tags')}\n icon={<Tags className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => navigate('/tags')}>\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n {tags.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title={tWithFallback(t, 'dashboard.tagCloud.emptyTitle', 'No tags created yet')}\n description={tWithFallback(\n t,\n 'dashboard.tagCloud.emptyDescription',\n 'Tags help you label and filter projects and content across the workspace.'\n )}\n className=\"py-4\"\n />\n ) : (\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap gap-1.5\">\n {tags.slice(0, 12).map((tag, idx) => (\n <Badge\n key={tag.id}\n variant=\"outline\"\n className={`text-[11px] cursor-pointer hover:opacity-80 transition-opacity ${tagColors[idx % tagColors.length]}`}\n onClick={() => navigate('/tags')}\n >\n {tag.label}\n </Badge>\n ))}\n </div>\n {total > 12 && (\n <p className=\"text-[11px] text-text-secondary text-center\">\n {tWithFallback(t, 'dashboard.tagCloud.more', '+{count} more tags', {\n count: total - 12,\n })}\n </p>\n )}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;;;AAOA,IAAM,IAAY;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAe,EAAE,kBAAe,cAAW,eAAyB;CAClF,IAAM,EAAE,SAAM,GAAS,EACjB,IAAO,GAAe,KAAK,SAAS,EAAE,EACtC,IAAQ,GAAe,KAAK,SAAS;AAE3C,QACE,kBAAC,GAAD;EACE,OAAO,EAAc,GAAG,4BAA4B,OAAO;EAC3D,MAAM,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;EACjC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,WAAU;GAAU,eAAe,EAAS,QAAQ;aACpF,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGV,EAAK,WAAW,IACf,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAc,GAAG,iCAAiC,sBAAsB;GAC/E,aAAa,EACX,GACA,uCACA,4EACD;GACD,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAK,MAAM,GAAG,GAAG,CAAC,KAAK,GAAK,MAC3B,kBAAC,GAAD;KAEE,SAAQ;KACR,WAAW,kEAAkE,EAAU,IAAM,EAAU;KACvG,eAAe,EAAS,QAAQ;eAE/B,EAAI;KACC,EAND,EAAI,GAMH,CACR;IACE,CAAA,EACL,IAAQ,MACP,kBAAC,KAAD;IAAG,WAAU;cACV,EAAc,GAAG,2BAA2B,sBAAsB,EACjE,OAAO,IAAQ,IAChB,CAAC;IACA,CAAA,CAEF;;EAEM,CAAA"}
|