@schlessera/brain-ui-react 0.18.0 → 0.20.0

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.
Files changed (86) hide show
  1. package/dist/components/activity/activity-page.d.ts +11 -0
  2. package/dist/components/activity/activity-page.d.ts.map +1 -0
  3. package/dist/components/activity/activity-page.js +243 -0
  4. package/dist/components/activity/activity-page.js.map +1 -0
  5. package/dist/components/activity/digest-card.d.ts +10 -0
  6. package/dist/components/activity/digest-card.d.ts.map +1 -0
  7. package/dist/components/activity/digest-card.js +55 -0
  8. package/dist/components/activity/digest-card.js.map +1 -0
  9. package/dist/components/activity/push-toggle.d.ts +2 -0
  10. package/dist/components/activity/push-toggle.d.ts.map +1 -0
  11. package/dist/components/activity/push-toggle.js +121 -0
  12. package/dist/components/activity/push-toggle.js.map +1 -0
  13. package/dist/components/activity/span-bits.d.ts +76 -0
  14. package/dist/components/activity/span-bits.d.ts.map +1 -0
  15. package/dist/components/activity/span-bits.js +127 -0
  16. package/dist/components/activity/span-bits.js.map +1 -0
  17. package/dist/components/chat/chat-page.d.ts.map +1 -1
  18. package/dist/components/chat/chat-page.js +4 -1
  19. package/dist/components/chat/chat-page.js.map +1 -1
  20. package/dist/components/chat/session-drawer.d.ts.map +1 -1
  21. package/dist/components/chat/session-drawer.js +1 -15
  22. package/dist/components/chat/session-drawer.js.map +1 -1
  23. package/dist/components/chat/subagent-view.d.ts +18 -0
  24. package/dist/components/chat/subagent-view.d.ts.map +1 -0
  25. package/dist/components/chat/subagent-view.js +83 -0
  26. package/dist/components/chat/subagent-view.js.map +1 -0
  27. package/dist/components/chat/tool-call-timeline.d.ts.map +1 -1
  28. package/dist/components/chat/tool-call-timeline.js +40 -4
  29. package/dist/components/chat/tool-call-timeline.js.map +1 -1
  30. package/dist/components/chat/tool-views.d.ts +4 -0
  31. package/dist/components/chat/tool-views.d.ts.map +1 -1
  32. package/dist/components/chat/tool-views.js +26 -0
  33. package/dist/components/chat/tool-views.js.map +1 -1
  34. package/dist/components/layout/mobile-tab-bar.d.ts.map +1 -1
  35. package/dist/components/layout/mobile-tab-bar.js +10 -4
  36. package/dist/components/layout/mobile-tab-bar.js.map +1 -1
  37. package/dist/components/layout/side-rail.d.ts.map +1 -1
  38. package/dist/components/layout/side-rail.js +7 -4
  39. package/dist/components/layout/side-rail.js.map +1 -1
  40. package/dist/components/settings/models-tab.d.ts +15 -0
  41. package/dist/components/settings/models-tab.d.ts.map +1 -1
  42. package/dist/components/settings/models-tab.js +100 -21
  43. package/dist/components/settings/models-tab.js.map +1 -1
  44. package/dist/hooks/use-now.d.ts +8 -0
  45. package/dist/hooks/use-now.d.ts.map +1 -0
  46. package/dist/hooks/use-now.js +32 -0
  47. package/dist/hooks/use-now.js.map +1 -0
  48. package/dist/hooks/use-websocket.d.ts.map +1 -1
  49. package/dist/hooks/use-websocket.js +46 -0
  50. package/dist/hooks/use-websocket.js.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +1 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/lib/api-client.d.ts +67 -1
  56. package/dist/lib/api-client.d.ts.map +1 -1
  57. package/dist/lib/api-client.js +41 -0
  58. package/dist/lib/api-client.js.map +1 -1
  59. package/dist/stores/activity-store.d.ts +108 -0
  60. package/dist/stores/activity-store.d.ts.map +1 -0
  61. package/dist/stores/activity-store.js +359 -0
  62. package/dist/stores/activity-store.js.map +1 -0
  63. package/dist/stores/ui-store.d.ts +9 -1
  64. package/dist/stores/ui-store.d.ts.map +1 -1
  65. package/dist/stores/ui-store.js +3 -0
  66. package/dist/stores/ui-store.js.map +1 -1
  67. package/dist/styles.css +1 -1
  68. package/package.json +2 -2
  69. package/src/components/activity/activity-page.tsx +623 -0
  70. package/src/components/activity/digest-card.tsx +105 -0
  71. package/src/components/activity/push-toggle.tsx +157 -0
  72. package/src/components/activity/span-bits.tsx +194 -0
  73. package/src/components/chat/chat-page.tsx +17 -0
  74. package/src/components/chat/session-drawer.tsx +1 -12
  75. package/src/components/chat/subagent-view.tsx +239 -0
  76. package/src/components/chat/tool-call-timeline.tsx +61 -3
  77. package/src/components/chat/tool-views.tsx +22 -0
  78. package/src/components/layout/mobile-tab-bar.tsx +25 -5
  79. package/src/components/layout/side-rail.tsx +17 -0
  80. package/src/components/settings/models-tab.tsx +135 -21
  81. package/src/hooks/use-now.ts +32 -0
  82. package/src/hooks/use-websocket.ts +44 -0
  83. package/src/index.ts +1 -0
  84. package/src/lib/api-client.ts +111 -0
  85. package/src/stores/activity-store.ts +451 -0
  86. package/src/stores/ui-store.ts +13 -1
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The Activity surface: an INDEX of all agent activity — live runs first,
3
+ * then history — with cost/token rollups. Deliberately not a residence:
4
+ * a session run deep-links back into its chat, a subagent into the drill-in
5
+ * stack; only a cron run (which has no chat around it) details here.
6
+ *
7
+ * Live rows ride the index-view activity subscription while this surface is
8
+ * open; history and rollups come from the REST activity API.
9
+ */
10
+ export declare function ActivityPage(): import("react").JSX.Element;
11
+ //# sourceMappingURL=activity-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-page.d.ts","sourceRoot":"","sources":["../../../src/components/activity/activity-page.tsx"],"names":[],"mappings":"AA4CA;;;;;;;;GAQG;AACH,wBAAgB,YAAY,gCAsQ3B"}
@@ -0,0 +1,243 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Activity as ActivityIcon, AlertTriangle, ArrowLeft, Bot, ChevronRight, Clock, RefreshCw, Timer, } from "lucide-react";
4
+ import { useShallow } from "zustand/react/shallow";
5
+ import { isFailureOutcome } from "@schlessera/brain-ui-sdk/protocol";
6
+ import { api, } from "../../lib/api-client.js";
7
+ import { sendClientMessage } from "../../hooks/use-websocket.js";
8
+ import { useNow } from "../../hooks/use-now.js";
9
+ import { useActivityStore, runSpans } from "../../stores/activity-store.js";
10
+ import { useChatStore } from "../../stores/chat-store.js";
11
+ import { useUIStore } from "../../stores/ui-store.js";
12
+ import { cn } from "../../lib/utils.js";
13
+ import { formatDuration, formatRelativeTime, formatTokenCount, } from "../chat/tool-views.js";
14
+ import { SpanPayload, SpanStatusDot, formatAggregateCost, formatEffectiveCost, runCostText, spanToolLabel, } from "./span-bits.js";
15
+ import { PushToggle } from "./push-toggle.js";
16
+ import { SettingsPanel } from "../settings/settings-panel.js";
17
+ /**
18
+ * The Activity surface: an INDEX of all agent activity — live runs first,
19
+ * then history — with cost/token rollups. Deliberately not a residence:
20
+ * a session run deep-links back into its chat, a subagent into the drill-in
21
+ * stack; only a cron run (which has no chat around it) details here.
22
+ *
23
+ * Live rows ride the index-view activity subscription while this surface is
24
+ * open; history and rollups come from the REST activity API.
25
+ */
26
+ export function ActivityPage() {
27
+ const supported = useActivityStore((s) => s.supported);
28
+ const connectionEpoch = useActivityStore((s) => s.connectionEpoch);
29
+ const liveSpans = useActivityStore((s) => s.spans);
30
+ const setActiveView = useUIStore((s) => s.setActiveView);
31
+ const openSettings = useUIStore((s) => s.openSettings);
32
+ const settingsPanelOpen = useUIStore((s) => s.settingsPanelOpen);
33
+ const setSettingsPanelOpen = useUIStore((s) => s.setSettingsPanelOpen);
34
+ const setActiveSession = useChatStore((s) => s.setActiveSession);
35
+ const [runs, setRuns] = useState(null);
36
+ const [rollups, setRollups] = useState(null);
37
+ const [error, setError] = useState(null);
38
+ const [detailRunId, setDetailRunId] = useState(null);
39
+ const [pricingStale, setPricingStale] = useState(false);
40
+ const inbox = useActivityStore((s) => s.inbox);
41
+ const loadInbox = useActivityStore((s) => s.loadInbox);
42
+ const acknowledgeIntent = useActivityStore((s) => s.acknowledgeIntent);
43
+ const acknowledgeAllIntents = useActivityStore((s) => s.acknowledgeAllIntents);
44
+ const refresh = () => {
45
+ api
46
+ .activityRuns({ limit: 100 })
47
+ .then(setRuns)
48
+ .catch((err) => setError(err instanceof Error ? err.message : String(err)));
49
+ api.activityRollups(7).then(setRollups).catch(() => { });
50
+ // Surface the pricing warning only when the table is stale AND refreshes
51
+ // are failing — a merely-aging table heals itself. A rejection (including
52
+ // 404 from a server that predates the route) means no signal: stay quiet.
53
+ api
54
+ .pricingState()
55
+ .then((s) => setPricingStale(Boolean(s.stale && s.error)))
56
+ .catch(() => setPricingStale(false));
57
+ void loadInbox();
58
+ };
59
+ // Re-runs on every reconnect (connectionEpoch): the new socket has no
60
+ // server-side subscriptions, and the REST refresh heals whatever finished
61
+ // while disconnected.
62
+ useEffect(() => {
63
+ refresh();
64
+ if (supported) {
65
+ sendClientMessage({ type: "activity_subscribe", view: "index" });
66
+ return () => {
67
+ sendClientMessage({ type: "activity_unsubscribe", view: "index" });
68
+ };
69
+ }
70
+ return undefined;
71
+ }, [supported, connectionEpoch]);
72
+ // Deep-link consumer: `#/activity/<runId>` (the push notification landing
73
+ // spot — the shell routes it here but leaves the hash intact) opens that
74
+ // run's detail. Opening/closing detail writes the hash back via
75
+ // history.replaceState so the link stays shareable without history spam.
76
+ useEffect(() => {
77
+ const applyHash = () => {
78
+ const match = /^#\/activity\/(.+)$/.exec(window.location.hash);
79
+ if (match)
80
+ setDetailRunId(decodeURIComponent(match[1]));
81
+ };
82
+ applyHash();
83
+ window.addEventListener("hashchange", applyHash);
84
+ return () => window.removeEventListener("hashchange", applyHash);
85
+ }, []);
86
+ function showDetail(runId) {
87
+ setDetailRunId(runId);
88
+ const target = runId ? `#/activity/${encodeURIComponent(runId)}` : "#/activity";
89
+ if (window.location.hash !== target) {
90
+ // window-qualified: `history` is the run list in this scope.
91
+ window.history.replaceState(null, "", target);
92
+ }
93
+ }
94
+ // Live open roots from the stream override/extend the REST snapshot.
95
+ const liveRoots = useMemo(() => {
96
+ const roots = [];
97
+ for (const byId of Object.values(liveSpans)) {
98
+ for (const span of Object.values(byId)) {
99
+ if (!span.parentSpanId && span.outcome === undefined)
100
+ roots.push(span);
101
+ }
102
+ }
103
+ return roots.sort((a, b) => b.startedAt - a.startedAt);
104
+ }, [liveSpans]);
105
+ const liveRunIds = new Set(liveRoots.map((r) => r.runId));
106
+ const restLive = (runs?.live ?? []).filter((r) => !liveRunIds.has(r.runId));
107
+ const history = (runs?.history ?? []).filter((r) => !liveRunIds.has(r.runId));
108
+ function openRun(row) {
109
+ if (row.origin === "session" && row.sessionId) {
110
+ // Chat is the residence — a session run opens its session.
111
+ setActiveSession(row.sessionId);
112
+ setActiveView("chat");
113
+ return;
114
+ }
115
+ showDetail(row.runId);
116
+ }
117
+ /** An inbox tap routes like any run row: chat for session runs, detail otherwise. */
118
+ function openIntent(intent) {
119
+ const liveRoot = liveRoots.find((r) => r.runId === intent.runId);
120
+ if (liveRoot) {
121
+ openRun({ runId: liveRoot.runId, origin: liveRoot.origin, sessionId: liveRoot.sessionId });
122
+ return;
123
+ }
124
+ const known = [...(runs?.live ?? []), ...(runs?.history ?? [])].find((r) => r.runId === intent.runId);
125
+ if (known) {
126
+ openRun(known);
127
+ }
128
+ else {
129
+ showDetail(intent.runId);
130
+ }
131
+ }
132
+ if (detailRunId) {
133
+ return _jsx(RunDetail, { runId: detailRunId, onBack: () => showDetail(null) });
134
+ }
135
+ return (_jsxs("div", { className: "flex h-full flex-col overflow-y-auto", children: [_jsx(SettingsPanel, { open: settingsPanelOpen, onClose: () => setSettingsPanelOpen(false) }), _jsxs("div", { className: "flex items-center gap-2 border-b border-border-subtle px-4 py-3", children: [_jsx(ActivityIcon, { className: "h-4 w-4 text-muted-foreground" }), _jsx("h1", { className: "text-sm font-medium", children: "Activity" }), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [pricingStale && (_jsxs("button", { type: "button", onClick: () => openSettings("models"), className: "flex items-center gap-1 rounded-md p-1.5 text-amber-500 transition-colors hover:bg-surface-raised hover:text-amber-400", "aria-label": "Pricing refresh is failing \u2014 costs may use stale rates. Open Settings", title: "Pricing refresh is failing \u2014 costs may use stale rates. Open Settings", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), _jsx("span", { className: "hidden text-[10px] sm:inline", children: "Pricing stale" })] })), _jsx(PushToggle, {})] }), _jsx("button", { type: "button", onClick: refresh, className: "rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-surface-raised hover:text-foreground", "aria-label": "Refresh", children: _jsx(RefreshCw, { className: "h-3.5 w-3.5" }) })] }), _jsxs("div", { className: "mx-auto w-full max-w-3xl space-y-6 p-4", children: [error && (_jsxs("p", { className: "text-xs text-destructive", children: ["Could not load activity: ", error] })), inbox.length > 0 && (_jsxs("section", { children: [_jsxs("div", { className: "mb-2 flex items-center", children: [_jsx("h2", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Needs attention" }), _jsx("button", { type: "button", onClick: () => void acknowledgeAllIntents(), className: "ml-auto text-[11px] text-muted-foreground transition-colors hover:text-foreground", children: "Dismiss all" })] }), _jsx("div", { className: "space-y-1", children: inbox.map((intent) => (_jsxs("div", { className: cn("flex items-center gap-2 rounded-lg border px-3 py-2 text-xs", intent.kind === "failure"
136
+ ? "border-destructive/30 bg-destructive/5"
137
+ : "border-border-subtle bg-surface"), children: [intent.kind === "failure" ? (_jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0 text-destructive" })) : intent.kind === "stuck" ? (_jsx(Timer, { className: "h-3.5 w-3.5 shrink-0 text-amber-500" })) : (_jsx(ActivityIcon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })), _jsx("button", { type: "button", className: "min-w-0 flex-1 truncate text-left hover:underline", onClick: () => {
138
+ void acknowledgeIntent(intent.id);
139
+ openIntent(intent);
140
+ }, children: intent.title }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: formatRelativeTime(intent.createdAt) }), _jsx("button", { type: "button", onClick: () => void acknowledgeIntent(intent.id), className: "shrink-0 rounded p-1 text-muted-foreground transition-colors hover:text-foreground", "aria-label": "Dismiss", children: "\u00D7" })] }, intent.id))) })] })), rollups && _jsx(RollupCards, { rollups: rollups }), _jsxs("section", { children: [_jsx("h2", { className: "mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Running now" }), liveRoots.length === 0 && restLive.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground/70", children: "Nothing is running." })) : (_jsxs("div", { className: "space-y-1", children: [liveRoots.map((span) => (_jsx(LiveRow, { span: span, onOpen: openRun }, span.runId))), restLive.map((run) => (_jsx(RunRow, { run: run, onOpen: openRun }, run.runId)))] }))] }), _jsxs("section", { children: [_jsx("h2", { className: "mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "History" }), history.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground/70", children: "No recorded runs yet." })) : (_jsx("div", { className: "space-y-1", children: history.map((run) => (_jsx(RunRow, { run: run, onOpen: openRun }, run.runId))) }))] })] })] }));
141
+ }
142
+ /** Today's key in the server's configured zone (mirrors its day formatter). */
143
+ function todayKey(timeZone) {
144
+ let fmt;
145
+ try {
146
+ fmt = new Intl.DateTimeFormat("en-CA", { timeZone, dateStyle: "short" });
147
+ }
148
+ catch {
149
+ fmt = new Intl.DateTimeFormat("en-CA", { timeZone: "UTC", dateStyle: "short" });
150
+ }
151
+ return fmt.format(new Date());
152
+ }
153
+ function RollupCards({ rollups }) {
154
+ // days[0] is merely the newest day WITH runs — on a quiet day that is
155
+ // yesterday, so look today up by its actual key (0 when absent).
156
+ const today = rollups.days.find((d) => d.day === todayKey(rollups.timeZone));
157
+ const week = rollups.days.reduce((acc, d) => ({
158
+ runs: acc.runs + d.runs,
159
+ failures: acc.failures + d.failures,
160
+ costUsd: acc.costUsd + d.costUsd,
161
+ effectiveCostUsd: acc.effectiveCostUsd + (d.effectiveCostUsd ?? 0),
162
+ unpricedRuns: acc.unpricedRuns + (d.unpricedRuns ?? 0),
163
+ tokens: acc.tokens + d.inputTokens + d.outputTokens,
164
+ }), { runs: 0, failures: 0, costUsd: 0, effectiveCostUsd: 0, unpricedRuns: 0, tokens: 0 });
165
+ // A server that predates pricing omits the effective fields entirely; the
166
+ // card then keeps its single list-price number instead of claiming an
167
+ // effective $0.00 it never computed.
168
+ const hasEffective = rollups.days.some((d) => d.effectiveCostUsd !== undefined);
169
+ return (_jsxs("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: [_jsx(StatCard, { label: "Runs today", value: String(today?.runs ?? 0) }), _jsx(StatCard, { label: "Failures (7d)", value: String(week.failures), alert: week.failures > 0 }), _jsx(StatCard, { label: "Spend (7d)", value: hasEffective
170
+ ? formatAggregateCost(week.effectiveCostUsd, week.unpricedRuns)
171
+ : `$${week.costUsd.toFixed(2)}`, secondary: hasEffective && (_jsxs(_Fragment, { children: ["list $", week.costUsd.toFixed(2), week.unpricedRuns > 0 && (_jsxs(_Fragment, { children: [_jsxs("span", { className: "hidden sm:inline", children: [" \u00B7 ", week.unpricedRuns, " unpriced"] }), _jsxs("span", { className: "sm:hidden", children: [" \u00B7 ", week.unpricedRuns, "?"] })] }))] })) }), _jsx(StatCard, { label: "Tokens (7d)", value: formatTokenCount(week.tokens) })] }));
172
+ }
173
+ function StatCard({ label, value, secondary, alert, }) {
174
+ return (_jsxs("div", { className: "rounded-lg border border-border-subtle bg-surface p-3", children: [_jsx("div", { className: cn("text-lg font-semibold", alert && "text-destructive"), children: value }), secondary && (_jsx("div", { className: "truncate text-[10px] text-muted-foreground/70", children: secondary })), _jsx("div", { className: "text-[11px] text-muted-foreground", children: label })] }));
175
+ }
176
+ function LiveRow({ span, onOpen, }) {
177
+ const children = useActivityStore(useShallow((s) => Object.values(s.spans[span.runId] ?? {}).filter((x) => x.parentSpanId)));
178
+ const now = useNow();
179
+ const current = children.filter((c) => c.outcome === undefined).at(-1);
180
+ const currentLabel = current ? spanToolLabel(current) : null;
181
+ return (_jsxs("button", { type: "button", onClick: () => onOpen({ runId: span.runId, origin: span.origin, sessionId: span.sessionId }), className: "flex w-full items-center gap-2 rounded-lg border border-border-subtle bg-surface px-3 py-2 text-left text-xs transition-colors hover:bg-surface-raised", children: [_jsx("span", { className: "h-2 w-2 shrink-0 animate-pulse rounded-full bg-primary" }), span.origin === "cron" ? (_jsx(Clock, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })) : (_jsx(Bot, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })), _jsx("span", { className: "truncate font-medium", children: span.jobName ?? span.name }), currentLabel && (_jsxs("span", { className: "truncate text-muted-foreground", children: ["\u00B7 ", currentLabel] })), _jsx("span", { className: "ml-auto shrink-0 font-[family-name:var(--font-mono)] text-[10px] text-muted-foreground/60", children: formatDuration(now - span.startedAt) })] }));
182
+ }
183
+ function RunRow({ run, onOpen, }) {
184
+ const failed = isFailureOutcome(run.outcome);
185
+ // Effective cost only — list price lives on the Spend card and the detail
186
+ // view. "—" is unknown, never $0.00 (AE3); a pre-pricing server that never
187
+ // sent the field keeps the original list-cost span instead (see runCostText).
188
+ const cost = runCostText(run);
189
+ return (_jsxs("button", { type: "button", onClick: () => onOpen(run), className: cn("flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-xs transition-colors hover:bg-surface-raised", failed && "border border-destructive/30 bg-destructive/5"), children: [run.origin === "cron" ? (_jsx(Clock, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })) : (_jsx(Bot, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })), _jsx("span", { className: "truncate", children: run.jobName ?? run.name }), failed && _jsx(AlertTriangle, { className: "h-3 w-3 shrink-0 text-destructive" }), run.outcome && run.outcome !== "success" && (_jsx("span", { className: cn("text-[10px] uppercase", failed ? "text-destructive" : "text-muted-foreground"), children: run.outcome })), _jsxs("span", { className: "ml-auto flex shrink-0 items-center gap-2 font-[family-name:var(--font-mono)] text-[10px] text-muted-foreground/60", children: [cost !== null && _jsx("span", { children: cost }), run.durationMs !== null && (_jsxs("span", { className: "flex items-center gap-0.5", children: [_jsx(Timer, { className: "h-3 w-3" }), formatDuration(run.durationMs)] })), _jsx("span", { children: formatRelativeTime(run.startedAt) })] })] }));
190
+ }
191
+ /** Chat-less run detail (cron runs; pruned runs show their rollup). */
192
+ function RunDetail({ runId, onBack }) {
193
+ const streamed = useActivityStore(useShallow((s) => runSpans(s, runId)));
194
+ const applySnapshot = useActivityStore((s) => s.applySnapshot);
195
+ const [pruned, setPruned] = useState(null);
196
+ const [rollup, setRollup] = useState(null);
197
+ const [missing, setMissing] = useState(false);
198
+ useEffect(() => {
199
+ api
200
+ // Payload bodies are excluded from run detail by default; this view's
201
+ // rows expand to them, so opt in.
202
+ .activityRun(runId, { includePayloads: true })
203
+ .then((detail) => {
204
+ if (detail.rollup)
205
+ setRollup(detail.rollup);
206
+ if (detail.detailPruned) {
207
+ setPruned(detail.rollup ?? {});
208
+ }
209
+ else if (detail.spans) {
210
+ applySnapshot({
211
+ type: "activity_snapshot",
212
+ view: "run",
213
+ runId,
214
+ spans: detail.spans,
215
+ events: detail.events ?? [],
216
+ highWaterSeq: { [runId]: detail.highWaterSeq ?? 0 },
217
+ });
218
+ }
219
+ })
220
+ .catch(() => setMissing(true));
221
+ }, [runId, applySnapshot]);
222
+ return (_jsxs("div", { className: "flex h-full flex-col overflow-y-auto", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border-subtle px-4 py-3", children: [_jsx("button", { type: "button", onClick: onBack, className: "rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-surface-raised hover:text-foreground", "aria-label": "Back", children: _jsx(ArrowLeft, { className: "h-4 w-4" }) }), _jsx("h1", { className: "truncate text-sm font-medium", children: runId })] }), _jsxs("div", { className: "mx-auto w-full max-w-3xl space-y-1 p-4", children: [missing && _jsx("p", { className: "text-xs text-muted-foreground", children: "Unknown run." }), rollup && (_jsxs("div", { className: "mb-2 flex flex-wrap items-center gap-x-3 gap-y-0.5 rounded-lg border border-border-subtle bg-surface px-3 py-2 text-[11px] text-muted-foreground", children: [_jsxs("span", { children: ["list", " ", _jsx("span", { className: "font-[family-name:var(--font-mono)] text-foreground/80", children: formatEffectiveCost(rollup.costUsd) })] }), _jsxs("span", { children: ["effective", " ", _jsx("span", { className: "font-[family-name:var(--font-mono)] text-foreground/80", children: formatEffectiveCost(rollup.effectiveCostUsd, rollup.pricingEstimate) })] }), rollup.billingMode && (_jsx("span", { children: rollup.billingMode === "api" ? "API billed" : "subscription billed" })), rollup.pricingEstimate && _jsx("span", { children: "~ estimated rates" })] })), pruned && (_jsxs("div", { className: "rounded-lg border border-border-subtle bg-surface p-3 text-xs text-muted-foreground", children: ["Detail pruned \u2014 only the rollup remains.", _jsx("pre", { className: "mt-2 overflow-x-auto text-[11px]", children: JSON.stringify(pruned, null, 2) })] })), streamed.map((span) => (_jsx(DetailSpanRow, { span: span, depth: depthOf(span, streamed) }, span.spanId)))] })] }));
223
+ }
224
+ /** One span tree row; tool spans expand to their recorded payload (AE7). */
225
+ function DetailSpanRow({ span, depth }) {
226
+ const [expanded, setExpanded] = useState(false);
227
+ const expandable = span.kind === "tool";
228
+ return (_jsxs("div", { style: { paddingLeft: `${depth * 16}px` }, children: [_jsxs("div", { className: cn("flex items-center gap-2 text-xs text-muted-foreground", expandable && "cursor-pointer hover:text-foreground"), onClick: expandable ? () => setExpanded((v) => !v) : undefined, children: [_jsx(SpanStatusDot, { span: span }), _jsx("span", { className: "truncate font-[family-name:var(--font-mono)]", children: spanToolLabel(span) }), span.outcome && span.outcome !== "success" && (_jsx("span", { className: "text-[10px] uppercase", children: span.outcome })), span.outcomeReason && (_jsx("span", { className: "truncate text-[10px] text-muted-foreground/60", children: span.outcomeReason })), _jsx("span", { className: "ml-auto shrink-0 font-[family-name:var(--font-mono)] text-[10px] text-muted-foreground/50", children: span.endedAt !== undefined
229
+ ? formatDuration(span.endedAt - span.startedAt)
230
+ : "…" }), expandable && (_jsx(ChevronRight, { className: cn("h-3 w-3 shrink-0 transition-transform", expanded && "rotate-90") }))] }), expanded && _jsx(SpanPayload, { spanId: span.spanId })] }));
231
+ }
232
+ function depthOf(span, all) {
233
+ let depth = 0;
234
+ let current = span;
235
+ while (current?.parentSpanId) {
236
+ depth += 1;
237
+ current = all.find((s) => s.spanId === current.parentSpanId);
238
+ if (depth > 6)
239
+ break;
240
+ }
241
+ return depth;
242
+ }
243
+ //# sourceMappingURL=activity-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activity-page.js","sourceRoot":"","sources":["../../../src/components/activity/activity-page.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The while-you-were-away card: presented PROACTIVELY on app open when a
3
+ * fresh digest exists (planning decision — built passively, the flow
4
+ * collapses back into asking the agent what happened). Once per digest:
5
+ * dismissing persists server-side, so a phone and a laptop don't each nag.
6
+ * An empty window stays quiet — no card for "nothing happened". The window
7
+ * is labeled: the card says what period it covers, never implies "now".
8
+ */
9
+ export declare function DigestCard(): import("react").JSX.Element | null;
10
+ //# sourceMappingURL=digest-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest-card.d.ts","sourceRoot":"","sources":["../../../src/components/activity/digest-card.tsx"],"names":[],"mappings":"AAQA;;;;;;;GAOG;AACH,wBAAgB,UAAU,uCAoFzB"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { AlertTriangle, Newspaper, X } from "lucide-react";
4
+ import { api } from "../../lib/api-client.js";
5
+ import { useUIStore } from "../../stores/ui-store.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { digestCostClause } from "./span-bits.js";
8
+ /**
9
+ * The while-you-were-away card: presented PROACTIVELY on app open when a
10
+ * fresh digest exists (planning decision — built passively, the flow
11
+ * collapses back into asking the agent what happened). Once per digest:
12
+ * dismissing persists server-side, so a phone and a laptop don't each nag.
13
+ * An empty window stays quiet — no card for "nothing happened". The window
14
+ * is labeled: the card says what period it covers, never implies "now".
15
+ */
16
+ export function DigestCard() {
17
+ const [digest, setDigest] = useState(null);
18
+ const [visible, setVisible] = useState(false);
19
+ const setActiveView = useUIStore((s) => s.setActiveView);
20
+ useEffect(() => {
21
+ void api
22
+ .activityDigest()
23
+ .then(({ digest, dismissedAt }) => {
24
+ if (!digest || digest.runs === 0)
25
+ return;
26
+ if (digest.generatedAt <= dismissedAt)
27
+ return;
28
+ setDigest(digest);
29
+ setVisible(true);
30
+ })
31
+ .catch(() => {
32
+ // No digest is a quiet state, never an error surface.
33
+ });
34
+ }, []);
35
+ if (!visible || !digest)
36
+ return null;
37
+ function dismiss() {
38
+ setVisible(false);
39
+ void api.activityDigestDismiss().catch(() => { });
40
+ }
41
+ const windowLabel = `${formatDay(digest.windowStart)} – ${formatDay(digest.windowEnd)}`;
42
+ // Effective-only spend clause; a digest persisted before pricing shipped
43
+ // falls back to its old list-cost clause inside the helper.
44
+ const costClause = digestCostClause(digest);
45
+ return (_jsxs("div", { className: cn("mx-auto mb-3 w-full max-w-2xl rounded-xl border p-3 text-xs", digest.failures > 0
46
+ ? "border-destructive/30 bg-destructive/5"
47
+ : "border-border-subtle bg-surface"), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Newspaper, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "font-medium", children: "While you were away" }), _jsx("span", { className: "text-[10px] text-muted-foreground/60", children: windowLabel }), _jsx("button", { type: "button", onClick: dismiss, className: "ml-auto rounded p-1 text-muted-foreground transition-colors hover:text-foreground", "aria-label": "Dismiss", children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }), _jsxs("div", { className: "mt-1.5 text-muted-foreground", children: [digest.runs, " run", digest.runs === 1 ? "" : "s", digest.failures > 0 && (_jsxs("span", { className: "text-destructive", children: [" \u00B7 ", digest.failures, " failed"] })), costClause && ` · ${costClause}`] }), digest.notable.length > 0 && (_jsx("div", { className: "mt-1.5 space-y-0.5", children: digest.notable.slice(0, 3).map((n) => (_jsxs("div", { className: "flex items-center gap-1.5 text-[11px]", children: [_jsx(AlertTriangle, { className: "h-3 w-3 shrink-0 text-destructive" }), _jsxs("span", { className: "truncate", children: [n.jobName ?? n.name, " \u2014 ", n.outcome] })] }, n.runId))) })), _jsx("button", { type: "button", onClick: () => {
48
+ dismiss();
49
+ setActiveView("activity");
50
+ }, className: "mt-2 text-[11px] font-medium text-primary hover:underline", children: "Open Activity" })] }));
51
+ }
52
+ function formatDay(ms) {
53
+ return new Date(ms).toLocaleDateString(undefined, { month: "short", day: "numeric" });
54
+ }
55
+ //# sourceMappingURL=digest-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest-card.js","sourceRoot":"","sources":["../../../src/components/activity/digest-card.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function PushToggle(): import("react").JSX.Element;
2
+ //# sourceMappingURL=push-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/activity/push-toggle.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,UAAU,gCA8HzB"}
@@ -0,0 +1,121 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Bell, BellOff } from "lucide-react";
4
+ import { api } from "../../lib/api-client.js";
5
+ import { cn } from "../../lib/utils.js";
6
+ /**
7
+ * The push enable/disable control, living on the Activity surface (where the
8
+ * alerts it governs land). The permission model is THREE-state, not a bare
9
+ * toggle: once the browser prompt is denied, Chrome silently ignores every
10
+ * further in-page request — so the blocked state must say where the real
11
+ * switch lives (site settings) instead of offering a button that does
12
+ * nothing. Where the platform has no push at all (iOS Safari in-browser,
13
+ * pre-16.4), the control renders disabled-with-explanation, not hidden.
14
+ */
15
+ /** The subscription's bound applicationServerKey, in the base64url form the
16
+ * server hands out — comparable against `pushPublicKey()` directly. */
17
+ function keyToBase64Url(key) {
18
+ let bin = "";
19
+ for (const b of new Uint8Array(key))
20
+ bin += String.fromCharCode(b);
21
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
22
+ }
23
+ export function PushToggle() {
24
+ const [state, setState] = useState("unsupported");
25
+ const [busy, setBusy] = useState(false);
26
+ useEffect(() => {
27
+ void (async () => {
28
+ if (!("Notification" in window) || !("serviceWorker" in navigator) || !("PushManager" in window)) {
29
+ setState("unsupported");
30
+ return;
31
+ }
32
+ if (Notification.permission === "denied") {
33
+ setState("blocked");
34
+ return;
35
+ }
36
+ if (Notification.permission === "default") {
37
+ setState("not-asked");
38
+ return;
39
+ }
40
+ const registration = await navigator.serviceWorker.ready;
41
+ const subscription = await registration.pushManager.getSubscription();
42
+ if (subscription) {
43
+ // The browser having a subscription doesn't mean the SERVER can
44
+ // still use it. Two disagreement cases: the server pruned/lost the
45
+ // row (a dead-endpoint send, a DB restore) — healed by re-asserting,
46
+ // an idempotent upsert; or the server's VAPID keypair changed, which
47
+ // makes this subscription permanently unsendable — its
48
+ // applicationServerKey no longer matches, so drop it and surface the
49
+ // re-enable button. Offline, trust the browser's own state.
50
+ try {
51
+ const { publicKey } = await api.pushPublicKey();
52
+ const boundKey = subscription.options.applicationServerKey;
53
+ if (boundKey && keyToBase64Url(boundKey) !== publicKey) {
54
+ await subscription.unsubscribe().catch(() => { });
55
+ setState("unsubscribed");
56
+ return;
57
+ }
58
+ await api.pushSubscribe(subscription.toJSON(), navigator.userAgent.slice(0, 100));
59
+ }
60
+ catch {
61
+ // Server unreachable — keep the browser's answer.
62
+ }
63
+ }
64
+ setState(subscription ? "subscribed" : "unsubscribed");
65
+ })();
66
+ }, []);
67
+ async function enable() {
68
+ setBusy(true);
69
+ try {
70
+ const permission = await Notification.requestPermission();
71
+ if (permission === "denied") {
72
+ setState("blocked");
73
+ return;
74
+ }
75
+ if (permission !== "granted") {
76
+ setState("not-asked");
77
+ return;
78
+ }
79
+ const { publicKey } = await api.pushPublicKey();
80
+ const registration = await navigator.serviceWorker.ready;
81
+ const subscription = await registration.pushManager.subscribe({
82
+ userVisibleOnly: true,
83
+ applicationServerKey: publicKey,
84
+ });
85
+ await api.pushSubscribe(subscription.toJSON(), navigator.userAgent.slice(0, 100));
86
+ setState("subscribed");
87
+ }
88
+ catch {
89
+ setState("unsubscribed");
90
+ }
91
+ finally {
92
+ setBusy(false);
93
+ }
94
+ }
95
+ async function disable() {
96
+ setBusy(true);
97
+ try {
98
+ const registration = await navigator.serviceWorker.ready;
99
+ const subscription = await registration.pushManager.getSubscription();
100
+ if (subscription) {
101
+ await api.pushUnsubscribe(subscription.endpoint).catch(() => { });
102
+ await subscription.unsubscribe();
103
+ }
104
+ setState("unsubscribed");
105
+ }
106
+ finally {
107
+ setBusy(false);
108
+ }
109
+ }
110
+ if (state === "unsupported") {
111
+ return (_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground/60", title: "This browser has no web push. On iOS, install the app to the home screen (iOS 16.4+).", children: [_jsx(BellOff, { className: "h-3.5 w-3.5" }), "No push here"] }));
112
+ }
113
+ if (state === "blocked") {
114
+ return (_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] text-muted-foreground", title: "Notifications are blocked at the browser level. Allow them in this site's browser settings, then reload.", children: [_jsx(BellOff, { className: "h-3.5 w-3.5 text-destructive" }), "Blocked in browser settings"] }));
115
+ }
116
+ const on = state === "subscribed";
117
+ return (_jsxs("button", { type: "button", disabled: busy, onClick: () => void (on ? disable() : enable()), className: cn("flex items-center gap-1.5 rounded-md px-2 py-1 text-[11px] transition-colors disabled:opacity-50", on
118
+ ? "text-primary hover:bg-surface-raised"
119
+ : "text-muted-foreground hover:bg-surface-raised hover:text-foreground"), title: on ? "Push notifications are on for this device" : "Get a push notification when background work fails", children: [on ? _jsx(Bell, { className: "h-3.5 w-3.5" }) : _jsx(BellOff, { className: "h-3.5 w-3.5" }), on ? "Push on" : "Enable push"] }));
120
+ }
121
+ //# sourceMappingURL=push-toggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push-toggle.js","sourceRoot":"","sources":["../../../src/components/activity/push-toggle.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { ActivitySpan, ActivitySpanOutcome } from "@schlessera/brain-ui-sdk/protocol";
2
+ /**
3
+ * THE status dot for activity spans — one outcome→color mapping so every
4
+ * surface (timeline rows, drill-in, activity index) agrees on what a color
5
+ * means: running pulses primary, success is accent, failures (per
6
+ * `isFailureOutcome`) and denied are destructive, cancelled is muted.
7
+ */
8
+ export declare function SpanStatusDot({ span, outcome, running, className, }: {
9
+ span?: ActivitySpan;
10
+ /** Alternative to `span` when only the outcome is at hand. */
11
+ outcome?: ActivitySpanOutcome | string | null;
12
+ running?: boolean;
13
+ /** Size/position overrides; defaults to h-1.5 w-1.5. */
14
+ className?: string;
15
+ }): import("react").JSX.Element;
16
+ /**
17
+ * Display label for a span. Prefers the server-lifted `toolName`; root spans
18
+ * label by kind (a turn or cron root is not a tool call); the last resort
19
+ * un-parses the `execute_tool` naming convention for spans recorded before
20
+ * the field existed.
21
+ */
22
+ export declare function spanToolLabel(span: ActivitySpan): string;
23
+ /**
24
+ * The recorded input/output of one tool span, expanded under its row — the
25
+ * ONE payload renderer shared by the subagent drill-in and the run detail.
26
+ * Mounted only while expanded (collapsed rows never subscribe). A span with
27
+ * no payload events (recorded before capture shipped) states so instead of
28
+ * offering an empty block (AE7); a payload clipped at persist time carries
29
+ * the wire `truncated` flag, rendered as a hint line under the block.
30
+ */
31
+ export declare function SpanPayload({ spanId }: {
32
+ spanId: string;
33
+ }): import("react").JSX.Element;
34
+ /**
35
+ * THE effective-cost glyph — one three-state rule so no surface ever renders
36
+ * an unknown cost as $0.00 (AE3): absent/NULL is "—" (we don't know), 0 is
37
+ * "free" (we know — subscription-billed or genuinely zero), positive is
38
+ * dollars, "~"-prefixed when computed from estimated rates. Sub-cent costs
39
+ * floor at "<$0.01" rather than rounding down to a zero look-alike.
40
+ */
41
+ export declare function formatEffectiveCost(costUsd: number | null | undefined, estimate?: boolean): string;
42
+ /**
43
+ * An aggregate's effective-cost sum. Sums exclude unknown-cost runs, so a
44
+ * nonzero `unpricedRuns` makes the number a floor ("≥ $X"), never a total.
45
+ * A known sub-cent sum floors at "<$0.01" like the per-run glyph — only an
46
+ * exact 0 (genuinely nothing to add up) renders "$0.00" (AE3).
47
+ */
48
+ export declare function formatAggregateCost(effectiveUsd: number, unpricedRuns: number): string;
49
+ /**
50
+ * A run row's cost text, three-way on `effectiveCostUsd`: ABSENT means a
51
+ * pre-pricing server never sent the field — fall back to the original
52
+ * list-cost rendering (a positive `costUsd`, else nothing) instead of
53
+ * claiming unknown; explicit null means THIS server computed "unknown" and
54
+ * renders the em dash; a number goes through `formatEffectiveCost` (AE3).
55
+ */
56
+ export declare function runCostText(run: {
57
+ costUsd: number | null;
58
+ effectiveCostUsd?: number | null;
59
+ pricingEstimate?: boolean;
60
+ }): string | null;
61
+ /**
62
+ * The digest card's cost clause (no leading separator), effective-only, or
63
+ * null for "say nothing". A digest persisted before pricing shipped carries
64
+ * neither new field and keeps its old list-cost clause.
65
+ */
66
+ export declare function digestCostClause(digest: {
67
+ costUsd: number;
68
+ effectiveCostUsd?: number;
69
+ unpricedRuns?: number;
70
+ }): string | null;
71
+ /** The "9+" unread-count bubble shared by the rail and the tab bar. Hidden at 0. */
72
+ export declare function CountBadge({ count, className }: {
73
+ count: number;
74
+ className?: string;
75
+ }): import("react").JSX.Element | null;
76
+ //# sourceMappingURL=span-bits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span-bits.d.ts","sourceRoot":"","sources":["../../../src/components/activity/span-bits.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAU3F;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,OAAO,EACP,OAAO,EACP,SAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAkBA;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CASxD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,+BAgCzD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,QAAQ,CAAC,EAAE,OAAO,GACjB,MAAM,CAKR;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAItF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,GAAG,IAAI,CAYhB;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,sCAYrF"}