@adonis-agora/agent-dashboard 0.6.1 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @adonis-agora/agent-dashboard
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#71](https://github.com/DavideCarvalho/adonis-agora-agent/pull/71) [`24c8d6f`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/24c8d6f81da6fc335ec96e4154f36a22ad762a8a) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Delegation is authorizable, the actor directory is consumed, and `dashboard` is part of the typed config.
8
+
9
+ - **`delegatesTo` accepts an object edge.** A synthesized `ask_<target>` tool goes through the same
10
+ `RolesPolicy` gate as any other tool, and a bare-string edge carries neither `roles` nor `ability` —
11
+ which is ADMIN-only under `DefaultToolAuthorizer` and an outright deny under `authzToolAuthorizer`,
12
+ so delegation was unreachable under authz with no way to open it. `delegatesTo` now also takes
13
+ `{ agent, roles?, ability? }` (`DelegateEdge`), whose annotation lands on the synthesized spec. Bare
14
+ strings keep their existing fail-closed behaviour.
15
+ - **`ActorDirectory.resolveDisplay` is now called.** `actorDirectory` resolved into a provider field
16
+ that was never read, so governance surfaces always rendered raw refs. Every governance route
17
+ returning an `actorRef` now carries an optional `actorLabel`, filled from one batched directory
18
+ lookup per page and omitted for unknown refs. Fail-soft: an unbound or throwing directory leaves the
19
+ rows exactly as the read-model produced them.
20
+ - **`AgentConfig.dashboard` is typed.** The console's config block was read through an untyped
21
+ `config.get('agent.dashboard')` string path, so writing it inside `defineConfig({ ... })` was an
22
+ excess-property error. It is now a declared `AgentConfig` field and the provider reads it off the
23
+ typed config.
24
+ - **`engines.node` is a range again.** Both packages published an exact version (`v22.23.2` /
25
+ `v26.7.0`), which warns on install for every consumer on any other Node and hard-fails under
26
+ `engine-strict`. Renovate's global `rangeStrategy: "pin"` had rewritten the ranges; `engines` is now
27
+ excluded from pinning so it cannot happen again.
28
+ - JSDoc: the published source carried Portuguese doc comments (`sse.ts`, `spi/tool.ts`, `base-tool.ts`,
29
+ `stores/factory.ts`, `rag/qdrant-store.ts`, `ai-tool-ref.ts`) — translated to English. Corrected the
30
+ stale cost formula on `AgentGovernanceQueries` (it omitted the cache-token split), the attachment
31
+ allow-list default (an exact-match list of 7 types, not `text/*`), and two NestJS-era references.
32
+
33
+ ## 0.7.0
34
+
35
+ ### Minor Changes
36
+
37
+ - [`ca0d9bb`](https://github.com/DavideCarvalho/adonis-agora-agent/commit/ca0d9bb41c077d9ed68703d61d2300744f0c4b02) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Ported the console's visual identity to match `nestjs-agent/packages/dashboard`'s Aviary console 1:1, overruling the earlier "Agora keeps its own distinct brand identity" decision (see `thin-buttons-relax` changeset) now that side-by-side screenshots show it was never actually close.
38
+
39
+ Verified by building both consoles against realistic seeded data (a thin API shim serving every governance endpoint on this side; the Nest sibling's own `preview.html` mock-data harness on the other) and screenshotting every section, light and dark, via an isolated headless Chrome.
40
+
41
+ - Dark is now the unconditional default canvas (was light-by-default with dark as an opt-out) — the single biggest reason the two consoles read as unrelated at a glance. Light stays available as an Agora-only extra behind the explicit toggle; Nest's agent console has no light mode at all.
42
+ - Token values now match Aviary's spec byte-for-byte: the shared dark neutrals (`--bg #09090b`, `--panel #0c0c0f`, `--panel-2 [#101017](https://github.com/DavideCarvalho/adonis-agent/issues/101017)`, `--line #1c1c22`, `--text #e7e7ea`, `--muted #76767f`) and the agent console's own accent (`#a78bfa` violet, replacing Agora's `#5a45ff`). The "in flight" status colour moved from cyan to Aviary's blue (`#60a5fa`).
43
+ - Body font is now Space Grotesk (was the system sans stack); `.mono` now matches the reference's stack order. Both are loaded the same way the Nest sibling loads them, via Google Fonts links in `index.html`.
44
+ - The active nav tab, and every semantic button (approve/reject), moved from a solid colour fill to a subtle tinted outline (`color-mix` border + background tint, text stays the console's normal foreground) — the loud filled-pill look was the second biggest source of "doesn't look anything alike".
45
+ - Panel radius (14px → 12px) and button/tab radius (999px pill / 8px → 6-8px) now match the reference's card and button primitives.
46
+ - Table bodies are fully monospace with tabular numerals now (previously only the numeric/id columns were), matching the reference's `TableBody` primitive.
47
+ - The brand mark and header subtitle now use the same bordered/tinted-tile and mono-uppercase-tracked treatments as the reference instead of a solid gradient badge and a plain grey caption.
48
+
49
+ Left alone, on purpose: the Nest sibling has no server-sent-events "Live" tab (no backend streaming plumbing here to back one — a real, pre-existing gap, not something this pass could close honestly); the Approvals/Runs sections keep this console's own table-based layout rather than the reference's stacked-card layout (same data, same button treatment, different list chrome — a lower-priority structural difference left for a follow-up); and no icon set was added to nav items/stat cards (the reference's SVG icon set was not ported in this pass).
50
+
3
51
  ## 0.6.1
4
52
 
5
53
  ### Patch Changes
@@ -0,0 +1 @@
1
+ :root{--bg: #09090b;--panel: #0c0c0f;--panel-2: #101017;--line: #1c1c22;--line-soft: #16161a;--text: #e7e7ea;--muted: #76767f;--agora-primary: #a78bfa;--agora-primary-soft: color-mix(in srgb, #a78bfa 14%, transparent);--agora-accent: #60a5fa;--primary: var(--agora-primary);--primary-fg: #09090b;--good: #34d399;--warn: #fbbf24;--bad: #f87171;--info: var(--agora-accent)}:root[data-theme=light],:root:not([data-theme=dark]){color-scheme:dark}:root[data-theme=dark]{color-scheme:dark}:root[data-theme=light]{color-scheme:light;--bg: hsl(40 30% 96%);--panel: hsl(40 28% 98%);--panel-2: hsl(40 24% 95%);--line: hsl(240 10% 14% / .12);--line-soft: hsl(240 10% 14% / .06);--text: hsl(240 14% 12%);--muted: hsl(240 6% 42%);--primary-fg: hsl(40 40% 97%)}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--text);font-family:Space Grotesk,ui-sans-serif,system-ui,sans-serif;-webkit-font-smoothing:antialiased}.mono{font-family:JetBrains Mono,ui-monospace,SF Mono,Menlo,monospace}.tnum{font-variant-numeric:tabular-nums}.app-bg{position:fixed;inset:0;z-index:0;pointer-events:none;background-image:radial-gradient(900px 420px at 82% -10%,hsl(250 74% 51% / .12),transparent 70%),radial-gradient(700px 360px at 8% -12%,hsl(190 90% 50% / .06),transparent 70%);opacity:.9}.shell{position:relative;z-index:1;max-width:1180px;margin:0 auto;padding:28px 24px 64px}.masthead{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:22px}.brand{display:flex;align-items:center;gap:12px}.brand-mark{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;color:var(--primary);background:color-mix(in srgb,var(--primary) 10%,transparent);border:1px solid color-mix(in srgb,var(--primary) 40%,transparent);font-weight:700}.brand h1{font-size:14px;font-weight:600;margin:0;letter-spacing:-.01em}.brand p{margin:1px 0 0;font-family:JetBrains Mono,ui-monospace,SF Mono,Menlo,monospace;font-size:10px;text-transform:uppercase;letter-spacing:.2em;color:var(--muted)}.tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px}.tab{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--muted);border-radius:8px;padding:6px 12px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:7px;transition:color .15s,border-color .15s,background .15s}.tab:hover{color:var(--text)}.tab[aria-selected=true]{color:var(--text);background:color-mix(in srgb,var(--primary) 10%,transparent);border-color:color-mix(in srgb,var(--primary) 50%,var(--line))}.controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.range-input{background:var(--panel);border:1px solid var(--line);color:var(--text);border-radius:8px;padding:6px 9px;font-size:12px;font-family:inherit}.price-input{min-width:225px}.icon-btn{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--muted);border-radius:8px;width:34px;height:32px;cursor:pointer;display:grid;place-items:center}.icon-btn:hover{color:var(--text)}.panel{background:linear-gradient(180deg,var(--panel-2),var(--panel));border:1px solid var(--line);border-radius:12px;padding:18px}.grid{display:grid;gap:16px}.grid.cols-3{grid-template-columns:repeat(3,1fr)}.grid.cols-2{grid-template-columns:repeat(2,1fr)}.grid.stat-4{grid-template-columns:repeat(4,1fr)}@media(max-width:900px){.grid.cols-3,.grid.cols-2,.grid.stat-4{grid-template-columns:repeat(2,1fr)}}@media(max-width:560px){.grid.cols-3,.grid.cols-2,.grid.stat-4{grid-template-columns:1fr}}.stat .label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:8px}.stat .value{font-size:24px;font-weight:600;letter-spacing:-.02em}.stat .sub{font-size:12px;color:var(--muted);margin-top:4px}.section-title{display:flex;align-items:center;justify-content:space-between;margin:0 0 14px;gap:12px}.section-title h2{font-size:14px;margin:0;letter-spacing:.01em}.section-title .hint{font-size:12px;color:var(--muted)}.table{width:100%;border-collapse:collapse;font-size:12px}.table tbody{font-family:JetBrains Mono,ui-monospace,SF Mono,Menlo,monospace;font-variant-numeric:tabular-nums}.table th{text-align:left;font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);padding:0 10px 10px;border-bottom:1px solid var(--line)}.table td{padding:10px;border-bottom:1px solid var(--line-soft);vertical-align:middle}.table tr:last-child td{border-bottom:none}.table .num{text-align:right}.table-wrap{overflow-x:auto}.bar{height:6px;border-radius:999px;background:var(--line);overflow:hidden;min-width:60px}.bar>span{display:block;height:100%;border-radius:999px;background:var(--primary)}.legend{display:flex;flex-direction:column;gap:8px;font-size:12px}.legend .row{display:flex;align-items:center;gap:8px}.swatch{width:10px;height:10px;border-radius:3px;flex:none}.legend .name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pill{display:inline-flex;align-items:center;gap:6px;padding:2px 9px;border-radius:999px;font-size:11px;border:1px solid var(--line);color:var(--muted)}.dot{width:7px;height:7px;border-radius:999px;background:currentColor;display:inline-block}.s-completed,.s-ok,.s-success,.s-approved{color:var(--good)}.s-running,.s-pending,.s-pending_approval,.s-awaiting_approval{color:var(--info)}.s-failed,.s-error,.s-rejected,.s-forbidden{color:var(--bad)}.s-cancelled,.s-canceled{color:var(--warn)}.btn{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--text);border-radius:6px;padding:6px 12px;font-size:12px;font-family:inherit;cursor:pointer;transition:color .15s,border-color .15s,background .15s}.btn:hover:not(:disabled){border-color:var(--muted)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn.approve{color:var(--good);background:color-mix(in srgb,var(--good) 10%,var(--panel));border-color:color-mix(in srgb,var(--good) 50%,var(--line))}.btn.reject{color:var(--bad);background:color-mix(in srgb,var(--bad) 10%,var(--panel));border-color:color-mix(in srgb,var(--bad) 50%,var(--line))}.rowbtns{display:inline-flex;gap:6px;justify-content:flex-end}.table tr.clickable{cursor:pointer}.table tr.clickable:hover td{background:var(--panel-2)}.th-sort{appearance:none;background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;cursor:pointer}.th-sort:hover{color:var(--text)}.warnnum{color:var(--warn)}.msg{border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:var(--panel-2)}.msg-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.msg-body{font-size:13px;white-space:pre-wrap;word-break:break-word}.empty{color:var(--muted);font-size:13px;text-align:center;padding:26px 10px}.err{color:var(--bad);font-size:13px;padding:20px 10px;text-align:center}.skeleton{border-radius:8px;background:linear-gradient(90deg,var(--line-soft),var(--line),var(--line-soft));background-size:200% 100%;animation:shimmer 1.3s ease-in-out infinite;height:14px}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.stack{display:flex;flex-direction:column;gap:16px}.muted{color:var(--muted)}.right{text-align:right}.chart-svg{width:100%;height:auto;display:block}.foot{margin-top:26px;font-size:11px;color:var(--muted);text-align:center}
@@ -3,9 +3,15 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
7
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8
+ <link
9
+ href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Space+Grotesk:wght@400;500;600;700&display=swap"
10
+ rel="stylesheet"
11
+ />
6
12
  <title>agent · governance console</title>
7
- <script type="module" crossorigin src="./assets/index-DoUZ23yb.js"></script>
8
- <link rel="stylesheet" crossorigin href="./assets/index-D8pLS_sd.css">
13
+ <script type="module" crossorigin src="./assets/index-HVy8whBx.js"></script>
14
+ <link rel="stylesheet" crossorigin href="./assets/index-DshcMnMD.css">
9
15
  </head>
10
16
  <body>
11
17
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/agent-dashboard",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Read-only governance console (spend by model/actor, usage trend, recent threads & tool calls, quota) for @adonis-agora/agent — a dependency-light React SPA served by a thin AdonisJS provider. Part of the Agora ecosystem.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -47,7 +47,7 @@
47
47
  "devDependencies": {
48
48
  "@adonisjs/core": "7.4.0",
49
49
  "@testing-library/react": "16.3.2",
50
- "@types/node": "22.20.0",
50
+ "@types/node": "22.20.1",
51
51
  "@types/react": "19.2.18",
52
52
  "@types/react-dom": "19.2.4",
53
53
  "@vitejs/plugin-react": "4.7.0",
@@ -57,10 +57,10 @@
57
57
  "typescript": "5.9.3",
58
58
  "vite": "7.3.6",
59
59
  "vitest": "3.2.7",
60
- "@adonis-agora/agent": "0.21.0"
60
+ "@adonis-agora/agent": "0.24.0"
61
61
  },
62
62
  "engines": {
63
- "node": "v26.7.0"
63
+ "node": ">=20.6.0"
64
64
  },
65
65
  "keywords": [
66
66
  "adonisjs",
@@ -1 +0,0 @@
1
- :root{--agora-primary: #5a45ff;--agora-primary-soft: hsl(250 74% 51% / .14);--agora-accent: #06b6d4;--bg: hsl(240 10% 6.5%);--panel: hsl(240 9% 9%);--panel-2: hsl(240 9% 11%);--line: hsl(40 30% 82% / .1);--line-soft: hsl(40 30% 82% / .05);--text: hsl(40 28% 92%);--muted: hsl(40 12% 66%);--primary: var(--agora-primary);--primary-fg: hsl(40 40% 96%);--good: #34d399;--warn: #fbbf24;--bad: #f87171;--info: var(--agora-accent)}:root[data-theme=light],:root:not([data-theme=dark]){color-scheme:light}:root[data-theme=dark]{color-scheme:dark}@media(prefers-color-scheme:light){:root:not([data-theme=dark]){--bg: hsl(40 30% 96%);--panel: hsl(40 28% 98%);--panel-2: hsl(40 24% 95%);--line: hsl(240 10% 14% / .12);--line-soft: hsl(240 10% 14% / .06);--text: hsl(240 14% 12%);--muted: hsl(240 6% 42%);--primary-fg: hsl(40 40% 97%)}}:root[data-theme=light]{--bg: hsl(40 30% 96%);--panel: hsl(40 28% 98%);--panel-2: hsl(40 24% 95%);--line: hsl(240 10% 14% / .12);--line-soft: hsl(240 10% 14% / .06);--text: hsl(240 14% 12%);--muted: hsl(240 6% 42%);--primary-fg: hsl(40 40% 97%)}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{background:var(--bg);color:var(--text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}.mono{font-family:ui-monospace,JetBrains Mono,SF Mono,Menlo,monospace}.tnum{font-variant-numeric:tabular-nums}.app-bg{position:fixed;inset:0;z-index:0;pointer-events:none;background-image:radial-gradient(900px 420px at 82% -10%,hsl(250 74% 51% / .12),transparent 70%),radial-gradient(700px 360px at 8% -12%,hsl(190 90% 50% / .06),transparent 70%);opacity:.9}.shell{position:relative;z-index:1;max-width:1180px;margin:0 auto;padding:28px 24px 64px}.masthead{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:22px}.brand{display:flex;align-items:center;gap:12px}.brand-mark{width:34px;height:34px;border-radius:9px;display:grid;place-items:center;color:var(--primary-fg);background:linear-gradient(140deg,var(--agora-primary),#8b78ff);box-shadow:0 6px 20px #4426df59;font-weight:700}.brand h1{font-size:17px;margin:0;letter-spacing:-.01em}.brand p{margin:1px 0 0;font-size:12px;color:var(--muted)}.tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:20px}.tab{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--muted);border-radius:999px;padding:7px 14px;font-size:13px;cursor:pointer;display:inline-flex;align-items:center;gap:7px;transition:color .15s,border-color .15s,background .15s}.tab:hover{color:var(--text)}.tab[aria-selected=true]{color:var(--primary-fg);background:var(--primary);border-color:var(--primary)}.controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.range-input{background:var(--panel);border:1px solid var(--line);color:var(--text);border-radius:8px;padding:6px 9px;font-size:12px;font-family:inherit}.price-input{min-width:225px}.icon-btn{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--muted);border-radius:8px;width:34px;height:32px;cursor:pointer;display:grid;place-items:center}.icon-btn:hover{color:var(--text)}.panel{background:linear-gradient(180deg,var(--panel-2),var(--panel));border:1px solid var(--line);border-radius:14px;padding:18px}.grid{display:grid;gap:16px}.grid.cols-3{grid-template-columns:repeat(3,1fr)}.grid.cols-2{grid-template-columns:repeat(2,1fr)}.grid.stat-4{grid-template-columns:repeat(4,1fr)}@media(max-width:900px){.grid.cols-3,.grid.cols-2,.grid.stat-4{grid-template-columns:repeat(2,1fr)}}@media(max-width:560px){.grid.cols-3,.grid.cols-2,.grid.stat-4{grid-template-columns:1fr}}.stat .label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:8px}.stat .value{font-size:24px;font-weight:600;letter-spacing:-.02em}.stat .sub{font-size:12px;color:var(--muted);margin-top:4px}.section-title{display:flex;align-items:center;justify-content:space-between;margin:0 0 14px;gap:12px}.section-title h2{font-size:14px;margin:0;letter-spacing:.01em}.section-title .hint{font-size:12px;color:var(--muted)}.table{width:100%;border-collapse:collapse;font-size:13px}.table th{text-align:left;font-weight:500;font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);padding:0 10px 10px;border-bottom:1px solid var(--line)}.table td{padding:10px;border-bottom:1px solid var(--line-soft);vertical-align:middle}.table tr:last-child td{border-bottom:none}.table .num{text-align:right}.table-wrap{overflow-x:auto}.bar{height:6px;border-radius:999px;background:var(--line);overflow:hidden;min-width:60px}.bar>span{display:block;height:100%;border-radius:999px;background:var(--primary)}.legend{display:flex;flex-direction:column;gap:8px;font-size:12px}.legend .row{display:flex;align-items:center;gap:8px}.swatch{width:10px;height:10px;border-radius:3px;flex:none}.legend .name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pill{display:inline-flex;align-items:center;gap:6px;padding:2px 9px;border-radius:999px;font-size:11px;border:1px solid var(--line);color:var(--muted)}.dot{width:7px;height:7px;border-radius:999px;background:currentColor;display:inline-block}.s-completed,.s-ok,.s-success,.s-approved{color:var(--good)}.s-running,.s-pending,.s-pending_approval,.s-awaiting_approval{color:var(--info)}.s-failed,.s-error,.s-rejected,.s-forbidden{color:var(--bad)}.s-cancelled,.s-canceled{color:var(--warn)}.btn{appearance:none;border:1px solid var(--line);background:var(--panel);color:var(--text);border-radius:8px;padding:6px 12px;font-size:12px;font-family:inherit;cursor:pointer;transition:color .15s,border-color .15s,background .15s}.btn:hover:not(:disabled){border-color:var(--muted)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn.approve{color:var(--good);border-color:color-mix(in srgb,var(--good) 40%,var(--line))}.btn.reject{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 40%,var(--line))}.rowbtns{display:inline-flex;gap:6px;justify-content:flex-end}.table tr.clickable{cursor:pointer}.table tr.clickable:hover td{background:var(--panel-2)}.th-sort{appearance:none;background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;cursor:pointer}.th-sort:hover{color:var(--text)}.warnnum{color:var(--warn)}.msg{border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:var(--panel-2)}.msg-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}.msg-body{font-size:13px;white-space:pre-wrap;word-break:break-word}.empty{color:var(--muted);font-size:13px;text-align:center;padding:26px 10px}.err{color:var(--bad);font-size:13px;padding:20px 10px;text-align:center}.skeleton{border-radius:8px;background:linear-gradient(90deg,var(--line-soft),var(--line),var(--line-soft));background-size:200% 100%;animation:shimmer 1.3s ease-in-out infinite;height:14px}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.stack{display:flex;flex-direction:column;gap:16px}.muted{color:var(--muted)}.right{text-align:right}.chart-svg{width:100%;height:auto;display:block}.foot{margin-top:26px;font-size:11px;color:var(--muted);text-align:center}