@theoremjs/react 0.3.4-next.871.gea9bbcc → 0.3.4

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/README.md CHANGED
@@ -153,6 +153,8 @@ The profile, the keys, and the system prompt stay on the server. The browser rec
153
153
  | `@theoremjs/react/ui/icons` | The icon set of the theme |
154
154
  | `@theoremjs/react/client` | `@theoremjs/web/client`: the client logic, without the hooks |
155
155
 
156
+ To draw your own figures like the trace panel does, `@theoremjs/react/ui` also exports its chart parts. `ChartCard` holds a chart under its name and one figure. `ChartStat` is one figure in a card. `RankedBars` draws one bar per `RankedBar`, in the order given, with a `TraceFill` for each. `RadialBars` draws the same rows as rings. `Sunburst` draws `SunburstNode` parts as rings, each child outside its parent.
157
+
156
158
  ## Chat
157
159
 
158
160
  `<TheoremChat />` accepts these props. All props are optional.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/react",
3
- "version": "0.3.4-next.871.gea9bbcc",
3
+ "version": "0.3.4",
4
4
  "description": "React bindings for Theorem: headless hooks + transport, an Astryx chat UI, and a Web-standard server handler.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -60,8 +60,8 @@
60
60
  "@stylexjs/stylex": "0.19.0",
61
61
  "react": "^19.0.0",
62
62
  "react-dom": "^19.0.0",
63
- "@theoremjs/agents": "^0.3.4-next.871.gea9bbcc",
64
- "@theoremjs/web": "^0.3.4-next.871.gea9bbcc"
63
+ "@theoremjs/agents": "^0.3.4",
64
+ "@theoremjs/web": "^0.3.4"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@astryxdesign/core": {
@@ -0,0 +1,30 @@
1
+ import { type RankedBar } from './RankedBars.tsx';
2
+ import { type TraceFill } from './trace-patterns.tsx';
3
+ /**
4
+ * One ring per row, the first outermost, each drawn round its track as far as its value goes, with
5
+ * a key beside it in the same order. `max` fixes the scale, for figures such as rates that share one.
6
+ */
7
+ export declare function RadialBars({ rows, format, max, }: {
8
+ rows: readonly RankedBar[];
9
+ format: (value: number) => string;
10
+ max?: number;
11
+ }): import("react").JSX.Element;
12
+ /** One part of a {@link Sunburst}: a leaf has a value, a branch the sum of its children's. */
13
+ export interface SunburstNode {
14
+ id: string;
15
+ name: string;
16
+ value?: number;
17
+ /** Default: the trace's solid fills in turn, a child taking its parent's. */
18
+ fill?: TraceFill;
19
+ /** A line under the value in the part's hover card. */
20
+ detail?: string;
21
+ children?: SunburstNode[];
22
+ }
23
+ /**
24
+ * Rings of parts, the inner ring the top parts and each ring further out their children, each part
25
+ * as wide as its share, with the top parts keyed beside it. Every part is named on hover.
26
+ */
27
+ export declare function Sunburst({ parts, format, }: {
28
+ parts: readonly SunburstNode[];
29
+ format: (value: number) => string;
30
+ }): import("react").JSX.Element;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { HStack } from '@astryxdesign/core/HStack';
3
+ import { Text } from '@astryxdesign/core/Text';
4
+ import { VStack } from '@astryxdesign/core/VStack';
5
+ import { Cell, PolarAngleAxis, RadialBar, RadialBarChart, SunburstChart, Tooltip, } from 'recharts';
6
+ import { paintOf, RankedTip } from "./RankedBars.js";
7
+ import { ChartFrame } from "./TraceOverview.js";
8
+ import { TRACE_GAP, traceFill } from "./trace-patterns.js";
9
+ /** Width and height of a radial chart, in pixels. */
10
+ const RADIAL_PX = 180;
11
+ /** The solid fills in order, for parts that name none. */
12
+ const RAMP = ['clear', 'shade1', 'shade2', 'shade3'];
13
+ function fillAt(fill, index) {
14
+ return fill ?? RAMP[index % RAMP.length] ?? 'clear';
15
+ }
16
+ /** A key's mark: a small rounded square in any paint. */
17
+ function Swatch({ paint }) {
18
+ return (_jsx("svg", { "aria-hidden": true, width: 10, height: 10, style: { display: 'block', flexShrink: 0 }, children: _jsx("rect", { width: 10, height: 10, rx: 2, fill: paint }) }));
19
+ }
20
+ /** Each row's mark, name and value, in the order given. */
21
+ function RadialKey({ rows, format, }) {
22
+ return (_jsx(VStack, { gap: 1, children: rows.map((row) => (_jsxs(HStack, { gap: 2, align: "center", justify: "between", children: [_jsxs(HStack, { gap: 2, align: "center", children: [_jsx(Swatch, { paint: row.paint }), _jsx(Text, { type: "supporting", color: "secondary", children: row.name })] }), _jsx(Text, { type: "supporting", hasTabularNumbers: true, children: format(row.value) })] }, row.id))) }));
23
+ }
24
+ /** A radial chart, centred, beside its key. */
25
+ function RadialFrame({ chart, keyed }) {
26
+ return (_jsxs(HStack, { gap: 4, align: "center", wrap: "wrap", children: [_jsx(ChartFrame, { children: chart }), _jsx("div", { style: { flex: '1 1 140px', minWidth: 0 }, children: keyed })] }));
27
+ }
28
+ /**
29
+ * One ring per row, the first outermost, each drawn round its track as far as its value goes, with
30
+ * a key beside it in the same order. `max` fixes the scale, for figures such as rates that share one.
31
+ */
32
+ export function RadialBars({ rows, format, max, }) {
33
+ const painted = rows.map((row, index) => ({
34
+ ...row,
35
+ paint: paintOf(row, fillAt(row.fill, index)),
36
+ }));
37
+ const rings = [...painted].reverse();
38
+ return (_jsx(RadialFrame, { chart: _jsxs(RadialBarChart, { width: RADIAL_PX, height: RADIAL_PX, data: rings, innerRadius: "22%", outerRadius: "100%", startAngle: 90, endAngle: -270, barCategoryGap: 1, accessibilityLayer: false, children: [_jsx(PolarAngleAxis, { type: "number", domain: [0, max ?? 'auto'], tick: false, axisLine: false }), _jsx(RadialBar, { dataKey: "value", background: { fill: 'var(--color-background-gray)' }, cornerRadius: 4, stroke: TRACE_GAP, isAnimationActive: false, children: rings.map((row) => (_jsx(Cell, { fill: row.paint }, row.id))) }), _jsx(Tooltip, { content: (props) => _jsx(RankedTip, { ...props, format: format }), cursor: false, isAnimationActive: false })] }), keyed: _jsx(RadialKey, { rows: painted, format: format }) }));
39
+ }
40
+ function total(nodes) {
41
+ return nodes.reduce((sum, node) => sum + (node.value ?? 0), 0);
42
+ }
43
+ function sunburstPart(node, fill) {
44
+ const children = node.children ? sunburstData(node.children, fill) : undefined;
45
+ return {
46
+ id: node.id,
47
+ name: node.name,
48
+ value: node.value ?? total(children ?? []),
49
+ fill: traceFill(fill),
50
+ detail: node.detail,
51
+ children,
52
+ };
53
+ }
54
+ function sunburstData(nodes, inherited) {
55
+ return nodes.map((node, index) => sunburstPart(node, node.fill ?? inherited ?? fillAt(undefined, index)));
56
+ }
57
+ /**
58
+ * Rings of parts, the inner ring the top parts and each ring further out their children, each part
59
+ * as wide as its share, with the top parts keyed beside it. Every part is named on hover.
60
+ */
61
+ export function Sunburst({ parts, format, }) {
62
+ const top = sunburstData(parts);
63
+ return (_jsx(RadialFrame, { chart: _jsx(SunburstChart, { width: RADIAL_PX, height: RADIAL_PX, data: { name: '', value: total(top), children: top }, innerRadius: RADIAL_PX * 0.11, ringPadding: 2, stroke: TRACE_GAP, textOptions: { fill: 'transparent' }, children: _jsx(Tooltip, { content: (props) => _jsx(RankedTip, { ...props, format: format }), isAnimationActive: false }) }), keyed: _jsx(RadialKey, { rows: top.map((part) => ({
64
+ id: String(part.id),
65
+ name: part.name,
66
+ value: part.value ?? 0,
67
+ paint: String(part.fill),
68
+ })), format: format }) }));
69
+ }
@@ -0,0 +1,31 @@
1
+ import { type TooltipContentProps } from 'recharts';
2
+ import { type TraceFill } from './trace-patterns.tsx';
3
+ /** One bar of {@link RankedBars}. */
4
+ export interface RankedBar {
5
+ id: string;
6
+ name: string;
7
+ value: number;
8
+ /** Default `clear`. */
9
+ fill?: TraceFill;
10
+ /** Any CSS colour, in place of `fill`: for a bar shaded by its own value. */
11
+ color?: string;
12
+ /** A line under the value in the bar's hover card. */
13
+ detail?: string;
14
+ }
15
+ /** A bar's paint: its colour, else its fill. */
16
+ export declare function paintOf(row: RankedBar, fill?: TraceFill): string;
17
+ /** A bar's hover card: its name, its value, and its detail. */
18
+ export declare function RankedTip({ active, payload, format, }: TooltipContentProps & {
19
+ format: (value: number) => string;
20
+ }): import("react").JSX.Element | null;
21
+ /**
22
+ * Bars in the order given, one per row, named on the left and valued on the right, drawn like the
23
+ * trace's charts. `max` fixes the scale, for figures such as rates that share one; a bar opens
24
+ * `onSelect` when given.
25
+ */
26
+ export declare function RankedBars({ rows, format, max, onSelect, }: {
27
+ rows: readonly RankedBar[];
28
+ format: (value: number) => string;
29
+ max?: number;
30
+ onSelect?: (row: RankedBar) => void;
31
+ }): import("react").JSX.Element;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text } from '@astryxdesign/core/Text';
3
+ import { VStack } from '@astryxdesign/core/VStack';
4
+ import { Bar, BarChart, Cell, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts';
5
+ import { ChartFrame, ChartTip, FIRST_WIDTH_PX } from "./TraceOverview.js";
6
+ import { TRACE_GAP, TRACE_RADIUS, traceFill } from "./trace-patterns.js";
7
+ /** A bar's paint: its colour, else its fill. */
8
+ export function paintOf(row, fill = 'clear') {
9
+ return row.color ?? traceFill(row.fill ?? fill);
10
+ }
11
+ /** Height of one bar's row, in pixels. */
12
+ const ROW_PX = 22;
13
+ /** Longest name the axis shows whole; the hover card shows the rest. */
14
+ const NAME_CHARS = 15;
15
+ const TICK = { fill: 'var(--color-text-secondary)', fontSize: 11 };
16
+ /** A bar's hover card: its name, its value, and its detail. */
17
+ export function RankedTip({ active, payload, format, }) {
18
+ const row = payload?.[0]?.payload;
19
+ if (!active || !row)
20
+ return null;
21
+ return (_jsx(ChartTip, { children: _jsxs(VStack, { gap: 0, children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { type: "supporting", hasTabularNumbers: true, children: format(row.value) }), row.detail ? (_jsx(Text, { type: "supporting", color: "secondary", hasTabularNumbers: true, children: row.detail })) : null] }) }));
22
+ }
23
+ /**
24
+ * Bars in the order given, one per row, named on the left and valued on the right, drawn like the
25
+ * trace's charts. `max` fixes the scale, for figures such as rates that share one; a bar opens
26
+ * `onSelect` when given.
27
+ */
28
+ export function RankedBars({ rows, format, max, onSelect, }) {
29
+ const height = rows.length * ROW_PX;
30
+ return (_jsx(ChartFrame, { children: _jsx(ResponsiveContainer, { width: "100%", height: height, initialDimension: { width: FIRST_WIDTH_PX, height }, children: _jsxs(BarChart, { data: [...rows], layout: "vertical", margin: { top: 0, right: 48, bottom: 0, left: 0 }, accessibilityLayer: false, barCategoryGap: 5, onClick: (state) => {
31
+ const row = rows[Number(state?.activeIndex)];
32
+ if (row && onSelect)
33
+ onSelect(row);
34
+ }, style: onSelect ? { cursor: 'pointer' } : undefined, children: [_jsx(XAxis, { type: "number", hide: true, domain: [0, max ?? 'auto'] }), _jsx(YAxis, { type: "category", dataKey: "id", width: 104, tick: TICK, tickLine: false, axisLine: false, interval: 0, tickFormatter: (id) => {
35
+ const name = rows.find((row) => row.id === id)?.name ?? '';
36
+ return name.length > NAME_CHARS ? `${name.slice(0, NAME_CHARS - 1)}…` : name;
37
+ } }), _jsx(Tooltip, { content: (props) => _jsx(RankedTip, { ...props, format: format }), cursor: false, isAnimationActive: false }), _jsx(Bar, { dataKey: "value", radius: TRACE_RADIUS, stroke: TRACE_GAP, strokeWidth: 2, isAnimationActive: false, minPointSize: 2, label: {
38
+ position: 'right',
39
+ fill: 'var(--color-text-secondary)',
40
+ fontSize: 11,
41
+ formatter: (value) => format(Number(value)),
42
+ }, children: rows.map((row) => (_jsx(Cell, { fill: paintOf(row) }, row.id))) })] }) }) }));
43
+ }
package/ui/TraceCharts.js CHANGED
@@ -4,9 +4,10 @@ import { HStack } from '@astryxdesign/core/HStack';
4
4
  import { Text } from '@astryxdesign/core/Text';
5
5
  import { VStack } from '@astryxdesign/core/VStack';
6
6
  import { useMemo } from 'react';
7
- import { Bar, BarChart, Cell, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts';
7
+ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts';
8
8
  import { isCallTrace, traceActor, } from "../client/trace-story.js";
9
9
  import { traceSpans } from "../client/trace-view.js";
10
+ import { RankedBars } from "./RankedBars.js";
10
11
  import { ChartFrame, ChartTip, FIRST_WIDTH_PX } from "./TraceOverview.js";
11
12
  import { useTraceFormat } from "./TraceValues.js";
12
13
  import { ACTOR_FILL, ChartCard, TRACE_GAP, TRACE_RADIUS, TraceSwatch, traceFill, } from "./trace-patterns.js";
@@ -14,8 +15,6 @@ import { ACTOR_FILL, ChartCard, TRACE_GAP, TRACE_RADIUS, TraceSwatch, traceFill,
14
15
  const CHART_PX = 120;
15
16
  /** Height of a share bar, in pixels. */
16
17
  const SHARE_PX = 12;
17
- /** Height of one row of the slowest-steps chart, in pixels. */
18
- const SLOW_ROW_PX = 22;
19
18
  /** Most steps the slowest-steps chart ranks. */
20
19
  const SLOWEST = 5;
21
20
  const TICK = { fill: 'var(--color-text-secondary)', fontSize: 11 };
@@ -162,49 +161,36 @@ function useCallRows(root, onSelect) {
162
161
  open: () => onSelect(node),
163
162
  })), [root, onSelect, t]);
164
163
  }
165
- function SlowTip({ active, payload }) {
166
- const format = useTraceFormat();
167
- const row = payload?.[0]?.payload;
168
- if (!active || !row)
169
- return null;
170
- return (_jsx(ChartTip, { children: _jsxs(VStack, { gap: 0, children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { type: "supporting", hasTabularNumbers: true, children: format.duration(row.ms) })] }) }));
171
- }
172
164
  /** The steps that took longest, longest first, filled by who acted. A bar opens its step. */
173
165
  function SlowestCard({ root, onSelect }) {
174
166
  const format = useTraceFormat();
175
167
  const { t } = format;
176
- const rows = useMemo(() => {
168
+ const slowest = useMemo(() => {
177
169
  const calls = modelCalls(root);
178
170
  return traceSpans(root.children)
179
171
  .filter((node) => node.meta.type === 'tool' || MODEL_CALLS.has(node.meta.type))
180
172
  .sort((a, b) => b.durationMs - a.durationMs)
181
173
  .slice(0, SLOWEST)
182
174
  .map((node) => ({
183
- id: node.id,
184
175
  node,
185
- name: node.meta.type === 'tool'
186
- ? (node.meta.subject ?? node.meta.label)
187
- : t('@theorem.panel.trace.charts.call', { index: calls.indexOf(node) + 1 }),
188
- ms: node.durationMs,
176
+ bar: {
177
+ id: node.id,
178
+ name: node.meta.type === 'tool'
179
+ ? (node.meta.subject ?? node.meta.label)
180
+ : t('@theorem.panel.trace.charts.call', { index: calls.indexOf(node) + 1 }),
181
+ value: node.durationMs,
182
+ fill: ACTOR_FILL[traceActor(node)],
183
+ },
189
184
  }));
190
185
  }, [root, t]);
191
- const top = rows[0];
186
+ const top = slowest[0];
192
187
  if (!top)
193
188
  return null;
194
- const height = rows.length * SLOW_ROW_PX;
195
- return (_jsx(ChartCard, { title: t('@theorem.panel.trace.charts.slowest'), value: format.duration(top.ms), note: top.name, children: _jsx(ChartFrame, { children: _jsx(ResponsiveContainer, { width: "100%", height: height, initialDimension: { width: FIRST_WIDTH_PX, height }, children: _jsxs(BarChart, { data: rows, layout: "vertical", margin: { top: 0, right: 48, bottom: 0, left: 0 }, accessibilityLayer: false, barCategoryGap: 5, onClick: (state) => {
196
- const row = rows[Number(state?.activeIndex)];
197
- if (row)
198
- onSelect(row.node);
199
- }, style: { cursor: 'pointer' }, children: [_jsx(XAxis, { type: "number", hide: true }), _jsx(YAxis, { type: "category", dataKey: "id", width: 104, tick: TICK, tickLine: false, axisLine: false, interval: 0, tickFormatter: (id) => {
200
- const name = rows.find((row) => row.id === id)?.name ?? '';
201
- return name.length > 15 ? `${name.slice(0, 14)}…` : name;
202
- } }), _jsx(Tooltip, { content: (props) => _jsx(SlowTip, { ...props }), cursor: false, isAnimationActive: false }), _jsx(Bar, { dataKey: "ms", ...BAR, label: {
203
- position: 'right',
204
- fill: 'var(--color-text-secondary)',
205
- fontSize: 11,
206
- formatter: (value) => format.duration(Number(value)),
207
- }, children: rows.map((row) => (_jsx(Cell, { fill: traceFill(ACTOR_FILL[traceActor(row.node)]) }, row.id))) })] }) }) }) }));
189
+ return (_jsx(ChartCard, { title: t('@theorem.panel.trace.charts.slowest'), value: format.duration(top.bar.value), note: top.bar.name, children: _jsx(RankedBars, { rows: slowest.map(({ bar }) => bar), format: format.duration, onSelect: (bar) => {
190
+ const step = slowest.find((each) => each.bar.id === bar.id);
191
+ if (step)
192
+ onSelect(step.node);
193
+ } }) }));
208
194
  }
209
195
  /** Where a turn's time, tokens, and slowest steps went. A bar opens its span. */
210
196
  export function TraceCharts({ root, split, onSelect, }) {
@@ -24,6 +24,12 @@ export declare function TurnPicker({ turns, index, onChange, onBack, end, }: {
24
24
  onBack?: (() => void) | undefined;
25
25
  end?: ReactNode;
26
26
  }): import("react").JSX.Element | null;
27
+ /** One figure in a card: its name, the figure, and lines that say more. */
28
+ export declare function ChartStat({ label, value, detail, }: {
29
+ label: string;
30
+ value: ReactNode;
31
+ detail?: ReactNode | readonly ReactNode[];
32
+ }): import("react").JSX.Element;
27
33
  /** A turn's guardrail figures alone, as {@link TurnStats} lays them out. */
28
34
  export declare function GuardrailStats({ latency }: {
29
35
  latency: TraceLatency;
@@ -55,7 +55,8 @@ export function TurnPicker({ turns, index, onChange, onBack, end, }) {
55
55
  count: turns.length,
56
56
  }) }), turn.node.meta.subject ? (_jsx(Text, { color: "secondary", maxLines: 1, children: turn.node.meta.subject })) : null] }), _jsxs(HStack, { gap: 0, children: [_jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.previous' : '@theorem.panel.trace.previous'), icon: _jsx(Icon, { icon: IconChevronLeft }), variant: "ghost", size: "sm", isDisabled: index === 0, onClick: () => onChange(index - 1) }), _jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.next' : '@theorem.panel.trace.next'), icon: _jsx(Icon, { icon: IconChevronRight }), variant: "ghost", size: "sm", isDisabled: index === turns.length - 1, onClick: () => onChange(index + 1) }), end] })] })] }));
57
57
  }
58
- function Stat({ label, value, detail, }) {
58
+ /** One figure in a card: its name, the figure, and lines that say more. */
59
+ export function ChartStat({ label, value, detail, }) {
59
60
  return (_jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsxs(VStack, { gap: 1, children: [_jsx(Text, { type: "supporting", color: "secondary", children: label }), _jsx(Text, { type: "large", weight: "semibold", hasTabularNumbers: true, children: value }), _jsx(VStack, { gap: 0, children: [detail]
60
61
  .flat()
61
62
  .filter(Boolean)
@@ -65,7 +66,7 @@ function FirstTextStat({ latency, format }) {
65
66
  if (latency?.firstTextMs === undefined)
66
67
  return null;
67
68
  const { t } = format;
68
- return (_jsx(Stat, { label: t('@theorem.panel.trace.firstText'), value: format.duration(latency.firstTextMs), detail: [
69
+ return (_jsx(ChartStat, { label: t('@theorem.panel.trace.firstText'), value: format.duration(latency.firstTextMs), detail: [
69
70
  latency.tokensPerSecond !== undefined &&
70
71
  t('@theorem.panel.trace.rate', {
71
72
  rate: format.number(Math.round(latency.tokensPerSecond)),
@@ -77,7 +78,7 @@ function FirstTextStat({ latency, format }) {
77
78
  function CostStat({ totals, format }) {
78
79
  if (!totals.cost)
79
80
  return null;
80
- return (_jsx(Stat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) }));
81
+ return (_jsx(ChartStat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) }));
81
82
  }
82
83
  /** A token total as its card reads it: "–" when no call reported it. */
83
84
  function tokenSide(total, format) {
@@ -88,7 +89,7 @@ function TokensStat({ totals, format }) {
88
89
  if (!input && !output)
89
90
  return null;
90
91
  const all = [input, output].reduce((sum, total) => sum + (total?.value ?? 0), 0);
91
- return (_jsx(Stat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', {
92
+ return (_jsx(ChartStat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', {
92
93
  input: tokenSide(input, format),
93
94
  output: tokenSide(output, format),
94
95
  }) }));
@@ -98,13 +99,13 @@ function StepsStat({ totals, tools, format, }) {
98
99
  // why: With no model, every step is a tool call the heading already counts.
99
100
  if (totals.calls === 0 && totals.errors === 0)
100
101
  return null;
101
- return (_jsx(Stat, { label: t('@theorem.panel.trace.steps'), value: format.number(totals.calls + tools), detail: totals.errors > 0 ? (_jsx("span", { style: { color: TRACE_HUE.oxblood }, children: t('@theorem.panel.trace.failed', { count: totals.errors }) })) : (t('@theorem.panel.trace.steps.detail', { calls: totals.calls, tools })) }));
102
+ return (_jsx(ChartStat, { label: t('@theorem.panel.trace.steps'), value: format.number(totals.calls + tools), detail: totals.errors > 0 ? (_jsx("span", { style: { color: TRACE_HUE.oxblood }, children: t('@theorem.panel.trace.failed', { count: totals.errors }) })) : (t('@theorem.panel.trace.steps.detail', { calls: totals.calls, tools })) }));
102
103
  }
103
104
  function GuardrailsStat({ latency, format, }) {
104
105
  const guardrails = latency?.guardrails;
105
106
  if (!guardrails)
106
107
  return null;
107
- return (_jsx(Stat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', {
108
+ return (_jsx(ChartStat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', {
108
109
  checks: guardrails.checks,
109
110
  flagged: guardrails.flagged,
110
111
  }) }));
@@ -122,5 +123,5 @@ export function GuardrailStats({ latency }) {
122
123
  */
123
124
  export function TurnStats({ totals, tools, latency, }) {
124
125
  const format = useTraceFormat();
125
- return (_jsxs(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: [_jsx(Stat, { label: TRACE_FIELDS.duration.label, value: format.duration(totals.durationMs) }), _jsx(FirstTextStat, { latency: latency, format: format }), _jsx(CostStat, { totals: totals, format: format }), _jsx(TokensStat, { totals: totals, format: format }), _jsx(StepsStat, { totals: totals, tools: tools, format: format }), _jsx(GuardrailsStat, { latency: latency, format: format })] }));
126
+ return (_jsxs(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: [_jsx(ChartStat, { label: TRACE_FIELDS.duration.label, value: format.duration(totals.durationMs) }), _jsx(FirstTextStat, { latency: latency, format: format }), _jsx(CostStat, { totals: totals, format: format }), _jsx(TokensStat, { totals: totals, format: format }), _jsx(StepsStat, { totals: totals, tools: tools, format: format }), _jsx(GuardrailsStat, { latency: latency, format: format })] }));
126
127
  }
package/ui/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export { LiveCaptionsPanel, type LiveCaptionsPanelProps } from './LiveCaptionsPa
25
25
  export { composerDrawerLabel, type LabelText, liveStateLabel, THEOREM_UI_CATALOG, type TheoremLabelKey, type TheoremLabelOverrides, type TheoremLabels, type TheoremLabelValues, type TheoremUiCatalog, voiceNoteName, workStatusLabel, } from './labels.ts';
26
26
  export { TheoremLabelsProvider, type TheoremLabelsProviderProps, useLabels, } from './labels-provider.tsx';
27
27
  export { PaneFailure, PaneLoading, PaneState } from './PaneState.tsx';
28
+ export { RadialBars, Sunburst, type SunburstNode } from './RadialCharts.tsx';
29
+ export { type RankedBar, RankedBars } from './RankedBars.tsx';
28
30
  export { InPlace, PaneLayout, PanePanel, Prose, RaisedPane } from './SidePanel.tsx';
29
31
  export { DEFAULT_CHAT_MAX_WIDTH, TheoremChat, type TheoremChatHandle, type TheoremChatProps, } from './TheoremChat.tsx';
30
32
  export { TheoremDecision, type TheoremDecisionProps } from './TheoremDecision.tsx';
@@ -33,4 +35,6 @@ export { TheoremHost, type TheoremHostProps } from './TheoremHost.tsx';
33
35
  export { ApprovalCard, type ApprovalCardProps, AuthChallengeCard, type AuthChallengeCardProps, type ToolDecision, } from './ToolGateCard.tsx';
34
36
  export { ToolResult } from './ToolResult.tsx';
35
37
  export { TraceGuardrailsView, TracePlacement, useTraceInspector } from './TraceInspectorPanel.tsx';
38
+ export { ChartStat } from './TraceOverview.tsx';
36
39
  export { TheoremThemeProvider, type TheoremThemeProviderProps } from './theme.tsx';
40
+ export { ChartCard, type TraceFill } from './trace-patterns.tsx';
package/ui/index.js CHANGED
@@ -28,6 +28,8 @@ export { LiveCaptionsPanel } from "./LiveCaptionsPanel.js";
28
28
  export { composerDrawerLabel, liveStateLabel, THEOREM_UI_CATALOG, voiceNoteName, workStatusLabel, } from "./labels.js";
29
29
  export { TheoremLabelsProvider, useLabels, } from "./labels-provider.js";
30
30
  export { PaneFailure, PaneLoading, PaneState } from "./PaneState.js";
31
+ export { RadialBars, Sunburst } from "./RadialCharts.js";
32
+ export { RankedBars } from "./RankedBars.js";
31
33
  export { InPlace, PaneLayout, PanePanel, Prose, RaisedPane } from "./SidePanel.js";
32
34
  export { DEFAULT_CHAT_MAX_WIDTH, TheoremChat, } from "./TheoremChat.js";
33
35
  export { TheoremDecision } from "./TheoremDecision.js";
@@ -36,4 +38,6 @@ export { TheoremHost } from "./TheoremHost.js";
36
38
  export { ApprovalCard, AuthChallengeCard, } from "./ToolGateCard.js";
37
39
  export { ToolResult } from "./ToolResult.js";
38
40
  export { TraceGuardrailsView, TracePlacement, useTraceInspector } from "./TraceInspectorPanel.js";
41
+ export { ChartStat } from "./TraceOverview.js";
39
42
  export { TheoremThemeProvider } from "./theme.js";
43
+ export { ChartCard } from "./trace-patterns.js";