@tangle-network/agent-app 0.60.184 → 0.60.185

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 (81) hide show
  1. package/dist/agent-profiles/d1.js +3 -3
  2. package/dist/agent-profiles/index.js +3 -3
  3. package/dist/assistant/index.js +9 -9
  4. package/dist/channels/index.js +1 -1
  5. package/dist/chat-react/index.js +4 -4
  6. package/dist/chat-routes/index.js +4 -4
  7. package/dist/{chunk-YXLN4PXK.js → chunk-2RCPPOQ4.js} +2 -2
  8. package/dist/{chunk-YESFGM56.js → chunk-5IS3GCMR.js} +3 -3
  9. package/dist/{chunk-3RHAF2P5.js → chunk-A3CUVYRZ.js} +3 -3
  10. package/dist/{chunk-LN5RBVGP.js → chunk-AVP5SNIG.js} +4 -4
  11. package/dist/{chunk-LN5RBVGP.js.map → chunk-AVP5SNIG.js.map} +1 -1
  12. package/dist/{chunk-7BYY7QKC.js → chunk-E65YV7VV.js} +5 -5
  13. package/dist/{chunk-7BYY7QKC.js.map → chunk-E65YV7VV.js.map} +1 -1
  14. package/dist/chunk-EZFBGW23.js +179 -0
  15. package/dist/chunk-EZFBGW23.js.map +1 -0
  16. package/dist/{chunk-UDLA3NGY.js → chunk-GNBBY3KN.js} +3 -3
  17. package/dist/{chunk-KOTNMTHV.js → chunk-HCFVEJQL.js} +3 -3
  18. package/dist/{chunk-YGBEGBJY.js → chunk-HEPEGY6Q.js} +2 -2
  19. package/dist/{chunk-AJZ3EJB5.js → chunk-IJAXOQJU.js} +2 -2
  20. package/dist/{chunk-EQRVOMMS.js → chunk-KMIDRUUH.js} +2 -2
  21. package/dist/{chunk-BBHVV37T.js → chunk-MSAGTFN4.js} +4 -4
  22. package/dist/chunk-OBKVNZPH.js +70 -0
  23. package/dist/chunk-OBKVNZPH.js.map +1 -0
  24. package/dist/{chunk-75ZIWV4U.js → chunk-QAF7ZTO3.js} +4 -4
  25. package/dist/{chunk-TJ4DYKF2.js → chunk-XG6FODYO.js} +3 -2
  26. package/dist/chunk-XG6FODYO.js.map +1 -0
  27. package/dist/{chunk-6O2NQ7WD.js → chunk-Y2SOQOJE.js} +3 -3
  28. package/dist/integrations-react/index.js +1 -1
  29. package/dist/{mention-editor-ZJAOUDEE.js → mention-editor-NOS5R3AU.js} +2 -2
  30. package/dist/preset-cloudflare/index.js +2 -2
  31. package/dist/sandbox/index.js +4 -4
  32. package/dist/settings-react/confirm-action.d.ts +51 -0
  33. package/dist/settings-react/delete-workspace-section.d.ts +31 -0
  34. package/dist/settings-react/export-section.d.ts +26 -0
  35. package/dist/settings-react/index.d.ts +20 -0
  36. package/dist/settings-react/index.js +1438 -0
  37. package/dist/settings-react/index.js.map +1 -0
  38. package/dist/settings-react/members-section.d.ts +66 -0
  39. package/dist/settings-react/save-status.d.ts +24 -0
  40. package/dist/settings-react/setting-outcome.d.ts +33 -0
  41. package/dist/settings-react/settings-layout.d.ts +71 -0
  42. package/dist/settings-react/settings-save-bar.d.ts +26 -0
  43. package/dist/settings-react/timezone-picker.d.ts +17 -0
  44. package/dist/settings-react/use-setting-field.d.ts +70 -0
  45. package/dist/stories/settings/settings-backend.d.ts +66 -0
  46. package/dist/stories/settings/settings-demo.d.ts +16 -0
  47. package/dist/stories/settings/team-panels-demo.d.ts +21 -0
  48. package/dist/studio-react/index.js +2 -2
  49. package/dist/teams-react/components/InviteForm.d.ts +2 -2
  50. package/dist/teams-react/components/MembersPanel.d.ts +9 -5
  51. package/dist/teams-react/components/RoleSelect.d.ts +10 -8
  52. package/dist/teams-react/components/invitation-detail.d.ts +8 -0
  53. package/dist/teams-react/components/member-access.d.ts +17 -0
  54. package/dist/teams-react/contracts.d.ts +2 -0
  55. package/dist/teams-react/index.js +119 -242
  56. package/dist/teams-react/index.js.map +1 -1
  57. package/dist/theme/tokens.css +43 -2
  58. package/dist/tools/index.js +4 -4
  59. package/dist/web-react/agent-profile-editor.js +4 -4
  60. package/dist/web-react/async/index.js +1 -1
  61. package/dist/web-react/controls.d.ts +4 -5
  62. package/dist/web-react/index.js +12 -12
  63. package/dist/work-product/index.js +2 -2
  64. package/dist/workspace-export/react.d.ts +26 -0
  65. package/dist/workspace-export/react.js +6 -169
  66. package/dist/workspace-export/react.js.map +1 -1
  67. package/dist/workspace-react/index.js +3 -3
  68. package/package.json +9 -4
  69. package/dist/chunk-TJ4DYKF2.js.map +0 -1
  70. /package/dist/{chunk-YXLN4PXK.js.map → chunk-2RCPPOQ4.js.map} +0 -0
  71. /package/dist/{chunk-YESFGM56.js.map → chunk-5IS3GCMR.js.map} +0 -0
  72. /package/dist/{chunk-3RHAF2P5.js.map → chunk-A3CUVYRZ.js.map} +0 -0
  73. /package/dist/{chunk-UDLA3NGY.js.map → chunk-GNBBY3KN.js.map} +0 -0
  74. /package/dist/{chunk-KOTNMTHV.js.map → chunk-HCFVEJQL.js.map} +0 -0
  75. /package/dist/{chunk-YGBEGBJY.js.map → chunk-HEPEGY6Q.js.map} +0 -0
  76. /package/dist/{chunk-AJZ3EJB5.js.map → chunk-IJAXOQJU.js.map} +0 -0
  77. /package/dist/{chunk-EQRVOMMS.js.map → chunk-KMIDRUUH.js.map} +0 -0
  78. /package/dist/{chunk-BBHVV37T.js.map → chunk-MSAGTFN4.js.map} +0 -0
  79. /package/dist/{chunk-75ZIWV4U.js.map → chunk-QAF7ZTO3.js.map} +0 -0
  80. /package/dist/{chunk-6O2NQ7WD.js.map → chunk-Y2SOQOJE.js.map} +0 -0
  81. /package/dist/{mention-editor-ZJAOUDEE.js.map → mention-editor-NOS5R3AU.js.map} +0 -0
@@ -1,172 +1,9 @@
1
- // src/workspace-export/react.tsx
2
- import { Button, Card, StatusPill } from "@tangle-network/ui/primitives";
3
- import { useCallback, useEffect, useRef, useState } from "react";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
- var DEFAULT_DESCRIPTION = "Everything in one zip. Credentials are never included.";
6
- var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
7
- function formatBytes(bytes) {
8
- let value = bytes;
9
- let unit = 0;
10
- while (value >= 1024 && unit < BYTE_UNITS.length - 1) {
11
- value /= 1024;
12
- unit += 1;
13
- }
14
- return `${value < 10 && unit ? value.toFixed(1) : Math.round(value)} ${BYTE_UNITS[unit]}`;
15
- }
16
- var TONE = { running: "info", complete: "success", failed: "danger" };
17
- var LABEL = { running: "Preparing", complete: "Ready", failed: "Failed" };
18
- async function readJson(response) {
19
- const body = await response.json().catch(() => null);
20
- if (!response.ok) throw new Error(body?.error?.message ?? `Request failed (${response.status})`);
21
- return body;
22
- }
23
- function WorkspaceExportPanel({ endpoint, canExport, title = "Export workspace data", description = DEFAULT_DESCRIPTION, className, fetcher, continuesInBackground = false }) {
24
- const doFetch = fetcher ?? fetch;
25
- const [exports, setExports] = useState([]);
26
- const [error, setError] = useState(null);
27
- const [loading, setLoading] = useState(true);
28
- const [starting, setStarting] = useState(false);
29
- const driving = useRef(null);
30
- const mounted = useRef(true);
31
- useEffect(() => () => {
32
- mounted.current = false;
33
- }, []);
34
- const upsert = useCallback((next) => {
35
- setExports((current) => [next, ...current.filter((e) => e.id !== next.id)]);
36
- }, []);
37
- const drive = useCallback(async (id) => {
38
- if (driving.current === id) return;
39
- driving.current = id;
40
- const retired = () => !mounted.current || driving.current !== id;
41
- let failures = 0;
42
- try {
43
- for (; ; ) {
44
- if (retired()) return;
45
- try {
46
- const next = await readJson(await doFetch(`${endpoint}/${id}/advance`, { method: "POST", credentials: "same-origin" }));
47
- if (retired()) return;
48
- failures = 0;
49
- setError(null);
50
- upsert(next);
51
- if (next.status !== "running") return;
52
- } catch (err) {
53
- failures += 1;
54
- if (failures >= 6) throw err;
55
- if (mounted.current) setError(`Still working; retrying (${failures})`);
56
- }
57
- await new Promise((resolve) => setTimeout(resolve, failures ? 3e3 * failures : 1500));
58
- }
59
- } catch (err) {
60
- if (mounted.current) setError(err instanceof Error ? err.message : String(err));
61
- } finally {
62
- if (driving.current === id) driving.current = null;
63
- }
64
- }, [doFetch, endpoint, upsert]);
65
- const refresh = useCallback(async () => {
66
- setLoading(true);
67
- setError(null);
68
- try {
69
- const body = await readJson(await doFetch(endpoint, { credentials: "same-origin" }));
70
- if (!mounted.current) return;
71
- setExports(body.exports);
72
- const running2 = body.exports.find((e) => e.status === "running");
73
- if (running2) void drive(running2.id);
74
- } catch (err) {
75
- if (mounted.current) setError(err instanceof Error ? err.message : String(err));
76
- } finally {
77
- if (mounted.current) setLoading(false);
78
- }
79
- }, [doFetch, drive, endpoint]);
80
- useEffect(() => {
81
- if (canExport) void refresh();
82
- }, [canExport, refresh]);
83
- const start = async () => {
84
- setStarting(true);
85
- setError(null);
86
- try {
87
- const job = await readJson(await doFetch(endpoint, { method: "POST", credentials: "same-origin" }));
88
- upsert(job);
89
- void drive(job.id);
90
- } catch (err) {
91
- setError(err instanceof Error ? err.message : String(err));
92
- } finally {
93
- setStarting(false);
94
- }
95
- };
96
- if (!canExport) return null;
97
- const latest = exports[0];
98
- const running = latest?.status === "running";
99
- return /* @__PURE__ */ jsxs(Card, { className: ["min-w-0 space-y-4 p-5 shadow-none", className].filter(Boolean).join(" "), "aria-busy": running || loading || void 0, children: [
100
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [
101
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
102
- /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
103
- description != null && description !== false ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
104
- ] }),
105
- /* @__PURE__ */ jsx(Button, { type: "button", onClick: start, disabled: starting || running, children: running ? "Exporting\u2026" : starting ? "Starting\u2026" : "Export" })
106
- ] }),
107
- error ? /* @__PURE__ */ jsxs("div", { role: "alert", className: "flex flex-wrap items-center gap-3 rounded-xl border border-destructive/40 p-3 text-sm text-destructive", children: [
108
- /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 break-words", children: error }),
109
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => latest?.status === "running" ? void drive(latest.id) : void refresh(), children: "Retry" })
110
- ] }) : null,
111
- latest ? /* @__PURE__ */ jsxs("section", { "aria-label": "Latest export", className: "space-y-3 rounded-xl border border-border p-4", children: [
112
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
113
- /* @__PURE__ */ jsx(StatusPill, { tone: TONE[latest.status], size: "sm", children: LABEL[latest.status] }),
114
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
115
- latest.unitsDone,
116
- " of ",
117
- latest.unitsTotal,
118
- " sources",
119
- latest.unitsFailed ? ` \xB7 ${latest.unitsFailed} failed` : "",
120
- " \xB7 ",
121
- formatBytes(latest.bytes)
122
- ] }),
123
- latest.current ? /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-sm text-muted-foreground", children: [
124
- "Now: ",
125
- latest.current
126
- ] }) : null
127
- ] }),
128
- running && continuesInBackground ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "You can close this page. The export keeps going, and the download is here when you return." }) : null,
129
- /* @__PURE__ */ jsx(
130
- "div",
131
- {
132
- role: "progressbar",
133
- "aria-label": "Export progress",
134
- "aria-valuemin": 0,
135
- "aria-valuemax": latest.unitsTotal,
136
- "aria-valuenow": latest.unitsDone,
137
- className: "h-2 overflow-hidden rounded-full bg-muted",
138
- children: /* @__PURE__ */ jsx(
139
- "div",
140
- {
141
- className: `h-full rounded-full ${latest.status === "failed" ? "bg-destructive" : "bg-primary"} transition-[width] duration-300 motion-reduce:transition-none`,
142
- style: { width: `${latest.unitsTotal ? Math.round(latest.unitsDone / latest.unitsTotal * 100) : 0}%` }
143
- }
144
- )
145
- }
146
- ),
147
- latest.status === "failed" ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive [overflow-wrap:anywhere]", children: [
148
- latest.error ?? "The export failed.",
149
- " Start a new export to try again."
150
- ] }) : null,
151
- latest.downloadUrl ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
152
- /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: latest.downloadUrl, download: true, children: "Download zip" }) }),
153
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
154
- "Link expires ",
155
- latest.downloadExpiresAt ? new Date(latest.downloadExpiresAt).toLocaleTimeString() : "soon",
156
- "; refresh the page for a new one."
157
- ] })
158
- ] }) : null,
159
- /* @__PURE__ */ jsxs("details", { className: "text-sm", children: [
160
- /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-muted-foreground", children: "Sources" }),
161
- /* @__PURE__ */ jsx("ul", { className: "mt-2 divide-y divide-border", children: latest.units.map((unit) => /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2 py-1.5", children: [
162
- /* @__PURE__ */ jsx("span", { className: "min-w-0 [overflow-wrap:anywhere]", children: unit.name }),
163
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: unit.status === "pending" ? "waiting" : unit.status === "empty" ? "nothing to export" : unit.status === "failed" ? "failed" : unit.rows !== void 0 ? `${unit.rows} rows` : unit.files !== void 0 ? `${unit.files} files \xB7 ${formatBytes(unit.bytes)}` : formatBytes(unit.bytes) })
164
- ] }, unit.name)) })
165
- ] })
166
- ] }) : loading ? /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm text-muted-foreground", children: "Loading exports\u2026" }) : null
167
- ] });
168
- }
1
+ import {
2
+ WorkspaceExportPanel,
3
+ useWorkspaceExport
4
+ } from "../chunk-EZFBGW23.js";
169
5
  export {
170
- WorkspaceExportPanel
6
+ WorkspaceExportPanel,
7
+ useWorkspaceExport
171
8
  };
172
9
  //# sourceMappingURL=react.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/workspace-export/react.tsx"],"sourcesContent":["/**\n * Export panel for a workspace settings page. Starts an export, drives it to\n * completion by calling `advance` while the page is open, shows each source as\n * it finishes, and offers the signed download link. Reopening the page resumes\n * a running export.\n */\n\nimport { Button, Card, StatusPill, type StatusTone } from '@tangle-network/ui/primitives'\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { ExportProgress } from './types'\n\nexport interface WorkspaceExportPanelProps {\n /** The mount path of the product's export routes, e.g. `/api/workspaces/ws-1/exports`. */\n endpoint: string\n /** Hidden for non-owners; the server refuses them regardless. */\n canExport: boolean\n title?: string\n /** One short muted line under the title. Pass `null` to render none. */\n description?: ReactNode\n className?: string\n fetcher?: typeof fetch\n /** True when the product runs `continueRunning` on a cron, so the export finishes after the page closes. */\n continuesInBackground?: boolean\n}\n\nconst DEFAULT_DESCRIPTION = 'Everything in one zip. Credentials are never included.'\n\nconst BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB']\n\nfunction formatBytes(bytes: number): string {\n let value = bytes\n let unit = 0\n while (value >= 1024 && unit < BYTE_UNITS.length - 1) {\n value /= 1024\n unit += 1\n }\n return `${value < 10 && unit ? value.toFixed(1) : Math.round(value)} ${BYTE_UNITS[unit]}`\n}\n\nconst TONE: Record<ExportProgress['status'], StatusTone> = { running: 'info', complete: 'success', failed: 'danger' }\nconst LABEL: Record<ExportProgress['status'], string> = { running: 'Preparing', complete: 'Ready', failed: 'Failed' }\n\nasync function readJson<T>(response: Response): Promise<T> {\n const body = (await response.json().catch(() => null)) as { error?: { message?: string } } | null\n if (!response.ok) throw new Error(body?.error?.message ?? `Request failed (${response.status})`)\n return body as T\n}\n\nexport function WorkspaceExportPanel({ endpoint, canExport, title = 'Export workspace data', description = DEFAULT_DESCRIPTION, className, fetcher, continuesInBackground = false }: WorkspaceExportPanelProps) {\n const doFetch = fetcher ?? fetch\n const [exports, setExports] = useState<ExportProgress[]>([])\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n const [starting, setStarting] = useState(false)\n const driving = useRef<string | null>(null)\n const mounted = useRef(true)\n\n useEffect(() => () => {\n mounted.current = false\n }, [])\n\n const upsert = useCallback((next: ExportProgress) => {\n setExports((current) => [next, ...current.filter((e) => e.id !== next.id)])\n }, [])\n\n const drive = useCallback(async (id: string) => {\n // One driver at a time: starting another export retires the old loop, so a\n // stale running job and a new one are never advanced together.\n if (driving.current === id) return\n driving.current = id\n const retired = () => !mounted.current || driving.current !== id\n // A failed request is retried: the server records each attempt and fails\n // the export itself once a source cannot finish.\n let failures = 0\n try {\n for (;;) {\n if (retired()) return\n try {\n const next = await readJson<ExportProgress>(await doFetch(`${endpoint}/${id}/advance`, { method: 'POST', credentials: 'same-origin' }))\n if (retired()) return\n failures = 0\n setError(null)\n upsert(next)\n if (next.status !== 'running') return\n } catch (err) {\n failures += 1\n if (failures >= 6) throw err\n if (mounted.current) setError(`Still working; retrying (${failures})`)\n }\n // Another tab may hold the lease, or the last request failed; wait before asking again.\n await new Promise((resolve) => setTimeout(resolve, failures ? 3000 * failures : 1500))\n }\n } catch (err) {\n if (mounted.current) setError(err instanceof Error ? err.message : String(err))\n } finally {\n if (driving.current === id) driving.current = null\n }\n }, [doFetch, endpoint, upsert])\n\n const refresh = useCallback(async () => {\n setLoading(true)\n setError(null)\n try {\n const body = await readJson<{ exports: ExportProgress[] }>(await doFetch(endpoint, { credentials: 'same-origin' }))\n if (!mounted.current) return\n setExports(body.exports)\n const running = body.exports.find((e) => e.status === 'running')\n if (running) void drive(running.id)\n } catch (err) {\n if (mounted.current) setError(err instanceof Error ? err.message : String(err))\n } finally {\n if (mounted.current) setLoading(false)\n }\n }, [doFetch, drive, endpoint])\n\n useEffect(() => {\n if (canExport) void refresh()\n }, [canExport, refresh])\n\n const start = async () => {\n setStarting(true)\n setError(null)\n try {\n const job = await readJson<ExportProgress>(await doFetch(endpoint, { method: 'POST', credentials: 'same-origin' }))\n upsert(job)\n void drive(job.id)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n } finally {\n setStarting(false)\n }\n }\n\n if (!canExport) return null\n const latest = exports[0]\n const running = latest?.status === 'running'\n\n return (\n <Card className={['min-w-0 space-y-4 p-5 shadow-none', className].filter(Boolean).join(' ')} aria-busy={running || loading || undefined}>\n <header className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h2 className=\"text-base font-semibold\">{title}</h2>\n {description != null && description !== false ? (\n <p className=\"mt-1 text-sm text-muted-foreground\">{description}</p>\n ) : null}\n </div>\n <Button type=\"button\" onClick={start} disabled={starting || running}>\n {running ? 'Exporting…' : starting ? 'Starting…' : 'Export'}\n </Button>\n </header>\n\n {error ? (\n <div role=\"alert\" className=\"flex flex-wrap items-center gap-3 rounded-xl border border-destructive/40 p-3 text-sm text-destructive\">\n <p className=\"min-w-0 flex-1 break-words\">{error}</p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => (latest?.status === 'running' ? void drive(latest.id) : void refresh())}>\n Retry\n </Button>\n </div>\n ) : null}\n\n {latest ? (\n <section aria-label=\"Latest export\" className=\"space-y-3 rounded-xl border border-border p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusPill tone={TONE[latest.status]} size=\"sm\">{LABEL[latest.status]}</StatusPill>\n <span className=\"text-sm text-muted-foreground\">\n {latest.unitsDone} of {latest.unitsTotal} sources{latest.unitsFailed ? ` · ${latest.unitsFailed} failed` : ''} · {formatBytes(latest.bytes)}\n </span>\n {latest.current ? <span className=\"min-w-0 truncate text-sm text-muted-foreground\">Now: {latest.current}</span> : null}\n </div>\n {running && continuesInBackground ? (\n <p className=\"text-xs text-muted-foreground\">You can close this page. The export keeps going, and the download is here when you return.</p>\n ) : null}\n <div\n role=\"progressbar\"\n aria-label=\"Export progress\"\n aria-valuemin={0}\n aria-valuemax={latest.unitsTotal}\n aria-valuenow={latest.unitsDone}\n className=\"h-2 overflow-hidden rounded-full bg-muted\"\n >\n <div\n className={`h-full rounded-full ${latest.status === 'failed' ? 'bg-destructive' : 'bg-primary'} transition-[width] duration-300 motion-reduce:transition-none`}\n style={{ width: `${latest.unitsTotal ? Math.round((latest.unitsDone / latest.unitsTotal) * 100) : 0}%` }}\n />\n </div>\n {latest.status === 'failed' ? (\n <p className=\"text-sm text-destructive [overflow-wrap:anywhere]\">{latest.error ?? 'The export failed.'} Start a new export to try again.</p>\n ) : null}\n {latest.downloadUrl ? (\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button asChild>\n <a href={latest.downloadUrl} download>Download zip</a>\n </Button>\n <span className=\"text-xs text-muted-foreground\">\n Link expires {latest.downloadExpiresAt ? new Date(latest.downloadExpiresAt).toLocaleTimeString() : 'soon'}; refresh the page for a new one.\n </span>\n </div>\n ) : null}\n <details className=\"text-sm\">\n <summary className=\"cursor-pointer text-muted-foreground\">Sources</summary>\n <ul className=\"mt-2 divide-y divide-border\">\n {latest.units.map((unit) => (\n <li key={unit.name} className=\"flex flex-wrap items-center justify-between gap-2 py-1.5\">\n <span className=\"min-w-0 [overflow-wrap:anywhere]\">{unit.name}</span>\n <span className=\"text-xs text-muted-foreground\">\n {unit.status === 'pending' ? 'waiting' : unit.status === 'empty' ? 'nothing to export' : unit.status === 'failed' ? 'failed' : unit.rows !== undefined ? `${unit.rows} rows` : unit.files !== undefined ? `${unit.files} files · ${formatBytes(unit.bytes)}` : formatBytes(unit.bytes)}\n </span>\n </li>\n ))}\n </ul>\n </details>\n </section>\n ) : loading ? (\n <p role=\"status\" className=\"text-sm text-muted-foreground\">Loading exports…</p>\n ) : null}\n </Card>\n )\n}\n"],"mappings":"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","names":["running"]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  WorkspaceInitial,
3
3
  WorkspaceSwitcher
4
- } from "../chunk-UDLA3NGY.js";
4
+ } from "../chunk-GNBBY3KN.js";
5
5
  import {
6
6
  ActionDialog,
7
7
  ActionDialogButton
@@ -13,13 +13,13 @@ import {
13
13
  } from "../chunk-SJWIZT7B.js";
14
14
  import {
15
15
  usePopoverDialog
16
- } from "../chunk-YGBEGBJY.js";
16
+ } from "../chunk-HEPEGY6Q.js";
17
17
  import {
18
18
  CheckGlyph,
19
19
  OVERLAY_SHADOW,
20
20
  PopoverSurface,
21
21
  usePopover
22
- } from "../chunk-TJ4DYKF2.js";
22
+ } from "../chunk-XG6FODYO.js";
23
23
  import "../chunk-RASGKEAL.js";
24
24
  import "../chunk-AMUXRAUO.js";
25
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.60.184",
3
+ "version": "0.60.185",
4
4
  "packageManager": "pnpm@11.24.0",
5
5
  "description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
6
6
  "keywords": [
@@ -557,6 +557,11 @@
557
557
  "import": "./dist/teams/invitations-api.js",
558
558
  "default": "./dist/teams/invitations-api.js"
559
559
  },
560
+ "./settings-react": {
561
+ "types": "./dist/settings-react/index.d.ts",
562
+ "import": "./dist/settings-react/index.js",
563
+ "default": "./dist/settings-react/index.js"
564
+ },
560
565
  "./teams-react": {
561
566
  "types": "./dist/teams-react/index.d.ts",
562
567
  "import": "./dist/teams-react/index.js",
@@ -707,11 +712,11 @@
707
712
  "@tangle-network/agent-knowledge": "22.0.1",
708
713
  "@tangle-network/agent-profile-materialize": "0.25.2",
709
714
  "@tangle-network/agent-runtime": "0.319.3",
710
- "@tangle-network/brand": "1.25.1",
715
+ "@tangle-network/brand": "1.26.0",
711
716
  "@tangle-network/hub-sdk": "0.30.2",
712
717
  "@tangle-network/sandbox": "0.60.49",
713
718
  "@tangle-network/sandbox-ui": "0.134.10",
714
- "@tangle-network/ui": "11.38.1",
719
+ "@tangle-network/ui": "11.40.0",
715
720
  "@testing-library/dom": "^10.4.1",
716
721
  "@testing-library/react": "^16.3.3",
717
722
  "@testing-library/user-event": "^14.6.6",
@@ -783,7 +788,7 @@
783
788
  "@tangle-network/hub-sdk": ">=0.19.3 <0.21.0 || >=0.21.0 <0.22.0 || >=0.22.1 <0.25.0 || >=0.27.0 <0.28.0 || >=0.28.0 <0.29.0 || >=0.29.5 <0.30.0 || >=0.30.2 <0.31.0",
784
789
  "@tangle-network/sandbox": ">=0.45.0 <0.48.0 || ^0.49.0 || ^0.50.0 || ^0.51.0 || ^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || >=0.58.1 <0.60.0 || >=0.60.3 <0.61.0",
785
790
  "@tangle-network/sandbox-ui": ">=0.134.10 <0.135.0",
786
- "@tangle-network/ui": ">=11.32.0 <12.0.0",
791
+ "@tangle-network/ui": ">=11.40.0 <12.0.0",
787
792
  "@tiptap/core": ">=3.28.0 <4.0.0",
788
793
  "@tiptap/extension-mention": ">=3.28.0 <4.0.0",
789
794
  "@tiptap/pm": ">=3.28.0 <4.0.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/web-react/controls.tsx"],"sourcesContent":["/**\n * Shared chat-shell control primitives — the LEAF that both the web-react barrel\n * (`./index`) and the composer children (`./agent-session-controls`,\n * `./seat-paywall`) import directly, so neither child has to reach back through\n * the barrel (which would re-create an import cycle). The barrel re-exports the\n * public names (`usePopover`, `usePending`, `ModelPicker`, `EffortPicker`, …)\n * unchanged, so the published export surface is identical.\n *\n * Styling contract matches the rest of `web-react`: Tailwind classes against the\n * shared design tokens; the glyphs are inline SVGs, no icon-library dependency.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { ProviderLogo } from './provider-logo'\nimport { StatusPill } from '@tangle-network/ui/primitives'\nimport {\n MAX_RECOMMENDED_MODELS,\n sortModelsByFreshness,\n type CatalogModel,\n} from '../runtime/model-catalog'\n\n// ── shared glyphs (no icon-library dependency) ────────────────────────────\n\nexport function ChevronDown({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nfunction SearchGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n )\n}\n\nfunction SparkleGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6 2.1 2.1m0-12.8-2.1 2.1M7.7 16.3l-2.1 2.1\" />\n </svg>\n )\n}\n\n/** lucide `brain` (v1.27) inlined — `/web-react` ships no icon-library\n * dependency, so the thinking glyph follows the same pattern as the rest of\n * this set. */\nexport function BrainGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 18V5\" />\n <path d=\"M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4\" />\n <path d=\"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5\" />\n <path d=\"M17.997 5.125a4 4 0 0 1 2.526 5.77\" />\n <path d=\"M18 18a4 4 0 0 0 2-7.464\" />\n <path d=\"M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517\" />\n <path d=\"M6 18a4 4 0 0 1-2-7.464\" />\n <path d=\"M6.003 5.125a4 4 0 0 0-2.526 5.77\" />\n </svg>\n )\n}\n\n/** lucide `check` — the selected-row mark in the picker menus. */\nexport function CheckGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )\n}\n\n/**\n * Keyboard + pointer model for a trigger-and-popover pair, dependency-free.\n * Outside-mousedown and Escape both close; Escape also returns focus to the\n * trigger so keyboard users aren't dropped at the top of the document. The\n * returned `triggerProps` carry the ARIA contract (`aria-haspopup`/\n * `aria-expanded`); spread them onto the trigger button.\n *\n * `panelRef` belongs to the popover panel and MUST be wired when the panel is\n * rendered through {@link PopoverSurface}: a portaled panel is not inside\n * `containerRef`, so a container-only outside test reads every click on the\n * menu's own rows as an outside click and closes before the row's handler runs.\n */\nexport function usePopover(open: boolean, setOpen: (open: boolean) => void) {\n const containerRef = useRef<HTMLDivElement>(null)\n const triggerRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!open) return\n function onMouseDown(e: MouseEvent) {\n const target = e.target as Node\n if (containerRef.current?.contains(target)) return\n const panel = panelRef.current\n if (panel?.contains(target)) return\n // A popover opened FROM this one portals as a SIBLING at body level, so\n // `contains` cannot see it and a click on its rows would read as outside.\n // Surfaces stamp their ancestor chain, so descendancy survives the portal.\n const ownPath = panel?.getAttribute(POPOVER_SURFACE_ATTR)\n const hitPath =\n target instanceof Element\n ? target.closest(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR)\n : null\n if (ownPath && hitPath && (hitPath === ownPath || hitPath.startsWith(`${ownPath}${POPOVER_PATH_SEPARATOR}`))) return\n setOpen(false)\n }\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape' && !e.defaultPrevented) {\n const ownPath = panelRef.current?.getAttribute(POPOVER_SURFACE_ATTR)\n const focusedPath = document.activeElement?.closest(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR)\n if (ownPath && focusedPath?.startsWith(`${ownPath}${POPOVER_PATH_SEPARATOR}`)) return\n e.preventDefault()\n setOpen(false)\n triggerRef.current?.focus()\n }\n }\n document.addEventListener('mousedown', onMouseDown)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n document.removeEventListener('mousedown', onMouseDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open, setOpen])\n\n return {\n containerRef,\n triggerRef,\n panelRef,\n triggerProps: {\n ref: triggerRef,\n 'aria-haspopup': true as const,\n 'aria-expanded': open,\n },\n }\n}\n\n// ── PopoverSurface ────────────────────────────────────────────────────────\n\n/** Distance between the trigger and the panel it opens. */\nconst POPOVER_GAP = 8\n/** Minimum distance the panel keeps from every viewport edge. */\nconst POPOVER_VIEWPORT_MARGIN = 16\n/** Floor for the computed max-height, so a cramped side still shows rows\n * rather than collapsing to a sliver the user cannot read. */\nconst POPOVER_MIN_HEIGHT = 120\n\n/**\n * Marks the portaled panel in the DOM. Products and audits (see\n * `playground/scripts/popover-hit-test.mjs`) select on this rather than on a\n * Tailwind class, which is presentation and free to change.\n *\n * Its VALUE is the surface's ancestor path (`outer/inner`), which is what\n * restores \"is this click inside my popover\" after the portal flattens two\n * nested panels into two siblings of `<body>`.\n */\nexport const POPOVER_SURFACE_ATTR = 'data-agent-app-popover'\nconst POPOVER_PATH_SEPARATOR = '/'\n\n/**\n * `PopoverSurface` is now mounted (closed) inside every server-rendered\n * composer, so its hooks run on the server once per composer per request.\n * React 18 logs \"useLayoutEffect does nothing on the server\" for that; React 19\n * does not (measured silent on react-dom 19.2.8). The peer floor is `react >=18`,\n * so bind the synchronous hook only where there is a DOM and the effect hook\n * elsewhere — placement never runs on the server either way.\n */\nexport const useBrowserLayoutEffect = typeof document !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface PopoverSurfaceProps {\n open: boolean\n /** The trigger the panel anchors to — `usePopover`'s `triggerRef`. */\n triggerRef: RefObject<HTMLElement | null>\n /** `usePopover`'s `panelRef`; also what the outside-click test consults. */\n panelRef: RefObject<HTMLDivElement | null>\n /** Presentation classes. Placement and elevation are owned here — a caller\n * must not pass `absolute`/`fixed`/`top-*`/`bottom-*`/`z-*`. */\n className?: string\n role?: string\n id?: string\n 'aria-label'?: string\n /** Make the panel at least as wide as its trigger. A portaled panel has no\n * `w-full` to inherit — the trigger is no longer its offset parent — so a\n * menu that used to stretch to a full-width trigger declares it here. */\n matchTriggerWidth?: boolean\n /** Preferred side. `above` suits controls docked at the bottom of a composer;\n * `below` suits headers, rows, and rails. Either flips when the other side has more room. */\n side?: 'above' | 'below'\n /** Which trigger edge the panel lines up with. `end` keeps a row's trailing menu over the row. */\n align?: 'start' | 'end'\n /** Opt in to remeasurement when asynchronous or filtered content changes. */\n contentKey?: unknown\n children: ReactNode\n}\n\n/**\n * The floating panel every canonical picker opens.\n *\n * It renders through a PORTAL to `document.body` and anchors itself to the\n * trigger in viewport coordinates, because an in-place `absolute` panel's\n * visibility is decided by markup this package does not own. Measured in\n * production: the shipped chat composer docks these controls inside a\n * horizontally scrolling rail (`overflow-x-auto`), and a scroll container\n * clips every positioned descendant whose containing block sits inside it —\n * so a correct 420x457 menu with correct coordinates painted zero pixels and\n * could not be clicked. An ancestor `transform`/`filter`/`contain` would trap\n * it the same way through the stacking context instead of the clip. Leaving\n * the DOM subtree is the only placement a host cannot re-break.\n *\n * Placement prefers ABOVE the trigger (these controls dock at the bottom of a\n * composer), flips below when there is more room there, clamps horizontally\n * into the viewport, and caps its own height to the space on the chosen side.\n * The panel is `visibility: hidden` for the measure pass so it never paints at\n * the pre-placement origin.\n */\nexport function PopoverSurface({\n open,\n triggerRef,\n panelRef,\n className,\n role,\n id,\n 'aria-label': ariaLabel,\n matchTriggerWidth,\n side = 'above',\n align = 'start',\n contentKey,\n children,\n}: PopoverSurfaceProps) {\n const surfaceId = useId()\n const [style, setStyle] = useState<CSSProperties>(() => ({\n position: 'fixed',\n top: 0,\n left: 0,\n visibility: 'hidden',\n }))\n\n const place = useCallback(() => {\n const trigger = triggerRef.current\n const panel = panelRef.current\n if (!trigger || !panel) return\n const anchor = trigger.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n // `scrollHeight` is the panel's CONTENT height, so it does not feed back\n // on the `maxHeight` this function just applied — reading `offsetHeight`\n // here would measure the previous pass's clamp and ratchet the panel\n // smaller on every scroll event.\n const contentHeight = panel.scrollHeight\n const panelWidth = panel.offsetWidth\n\n const roomAbove = anchor.top - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN\n const roomBelow = viewportHeight - anchor.bottom - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN\n const above = side === 'above'\n ? contentHeight <= roomAbove || roomAbove >= roomBelow\n : !(contentHeight <= roomBelow || roomBelow >= roomAbove)\n const maxHeight = Math.max(POPOVER_MIN_HEIGHT, above ? roomAbove : roomBelow)\n const height = Math.min(contentHeight, maxHeight)\n\n const top = above ? Math.max(POPOVER_VIEWPORT_MARGIN, anchor.top - POPOVER_GAP - height) : anchor.bottom + POPOVER_GAP\n const rightBound = Math.max(POPOVER_VIEWPORT_MARGIN, viewportWidth - panelWidth - POPOVER_VIEWPORT_MARGIN)\n const preferredLeft = align === 'end' ? anchor.right - panelWidth : anchor.left\n const left = Math.min(Math.max(POPOVER_VIEWPORT_MARGIN, preferredLeft), rightBound)\n\n setStyle({\n position: 'fixed',\n top,\n left,\n maxHeight,\n visibility: 'visible',\n ...(matchTriggerWidth ? { minWidth: anchor.width } : {}),\n })\n }, [align, matchTriggerWidth, panelRef, side, triggerRef])\n\n // Layout effect: placement is resolved before the browser paints, so the\n // panel is never seen at the origin it mounts at.\n useBrowserLayoutEffect(() => {\n if (!open) {\n setStyle({ position: 'fixed', top: 0, left: 0, visibility: 'hidden' })\n return\n }\n place()\n }, [open, place, contentKey])\n\n useEffect(() => {\n if (!open) return\n const onViewportChange = () => place()\n // `capture` so the rail's OWN scroll re-anchors the panel — a scroll inside\n // an ancestor does not bubble to window.\n window.addEventListener('scroll', onViewportChange, true)\n window.addEventListener('resize', onViewportChange)\n return () => {\n window.removeEventListener('scroll', onViewportChange, true)\n window.removeEventListener('resize', onViewportChange)\n }\n }, [open, place])\n\n if (!open || typeof document === 'undefined') return null\n\n const ownerPath = triggerRef.current?.closest?.(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR)\n const path = ownerPath ? `${ownerPath}${POPOVER_PATH_SEPARATOR}${surfaceId}` : surfaceId\n\n return createPortal(\n <div\n ref={panelRef}\n id={id}\n role={role}\n aria-label={ariaLabel}\n style={style}\n {...{ [POPOVER_SURFACE_ATTR]: path }}\n className={`z-[1000] ${className ?? ''}`}\n >\n {children}\n </div>,\n document.body,\n )\n}\n\n/**\n * Focus treatment for a row inside a popover panel.\n *\n * The ring itself now comes from the `:focus-visible` floor in tokens.css, so\n * this no longer restates a width or a colour. What it still has to say is\n * WHERE the ring is drawn: a popover option is a full-width row inside a panel\n * that clips its own corners (`overflow-hidden rounded-xl`), and an outward\n * ring on the first or last row is clipped away by that panel. Pulling the\n * offset negative draws the same ring just inside the row instead.\n */\nexport const POPOVER_OPTION_FOCUS = 'focus-visible:[outline-offset:-2px]'\n\n/**\n * The one overlay elevation for floating surfaces — picker menus, popovers,\n * drawers, modals. Reads the theme's `--shadow-overlay` token, so every overlay\n * lifts with the same shadow and re-themes from one source. The floating\n * composer uses the quieter `shadow-raised` rung instead.\n *\n * Written as an arbitrary value rather than the preset's `shadow-overlay`\n * utility, because that utility only exists where the preset is part of the\n * Tailwind build. A host that gets its tokens through a precompiled bundle —\n * `@tangle-network/sandbox-ui` ships brand's tokens inlined, with no `@theme`\n * block surviving the compile — receives `--shadow-overlay` as a plain custom\n * property, from which no `shadow-overlay` utility can be generated, and these\n * surfaces render flat. The arbitrary form emits from the class alone and works\n * either way.\n */\nexport const OVERLAY_SHADOW = 'shadow-[var(--shadow-overlay)]'\n\n/**\n * Root geometry for a picker — the box that holds the trigger — in one place\n * because every picker in this family has to agree on it.\n *\n * Shrink-wrapping (`inline-flex`) is the default: these controls dock on a\n * composer row where an expanding pill shoves its neighbours around. A STACKED\n * panel wants the opposite — the compact `AgentSessionControls` gear popover\n * lays its controls out in a column, and a shrink-wrapped root there makes a\n * trigger's own `w-full` a no-op, since it fills a box the trigger itself\n * sized. That is what left Agent backend short of the panel edge and Thinking\n * narrower still.\n *\n * The panel is portaled ({@link PopoverSurface}), so widening the root widens\n * the TRIGGER only. A menu that should follow it declares `matchTriggerWidth`.\n */\nexport function pickerRootClass(fullWidth: boolean): string {\n return `relative ${fullWidth ? 'flex w-full' : 'inline-flex'}`\n}\n\n/**\n * How a picker's TRIGGER is drawn. The menu it opens is the same either way.\n *\n * - `chip` (default): the 32px bordered pill — `rounded-full border\n * border-border bg-card font-medium`, on the composer row's height.\n * - `quiet`: a 32px borderless text button (the control scale's sm, the\n * composer row's one height) — muted label, small glyph, a\n * surface fill on hover and while the menu is open, nothing else. For a\n * composer whose card already draws the border: three pills docked under\n * the input there read as three more cards, not as the input's controls.\n * - `field`: a full-width form field — the same height, radius, border,\n * well, and focus treatment as a text input at `--control-height`. For a\n * settings form, where a pill among inputs reads as a different control.\n */\nexport type PickerVariant = 'chip' | 'quiet' | 'field'\n\n/**\n * Trigger classes for a `field` picker. It matches `@tangle-network/ui`'s\n * compact `Input` (height, `rounded-lg`, `--bg-input` well, border hover, and\n * `:focus` halo) so a model or harness selector sits in a form as one more\n * field. Written out here because `/web-react` controls stay free of the\n * sandbox-ui peer.\n */\nexport function fieldPickerTriggerClass({ interactive = true }: { interactive?: boolean } = {}): string {\n return `inline-flex h-[var(--control-height)] w-full min-w-0 items-center justify-between gap-2 whitespace-nowrap rounded-lg border border-border bg-[var(--bg-input)] px-3 text-left text-sm font-normal text-foreground transition-[border-color,box-shadow] duration-150 ease-out focus:outline-hidden focus:border-[var(--focus-border)] focus:ring-3 focus:ring-[var(--focus-halo)] disabled:cursor-not-allowed disabled:opacity-50 ${\n interactive ? 'hover:border-[var(--border-strong)] data-[state=open]:border-[var(--focus-border)]' : 'cursor-default'}`\n}\n\n/** The chip trigger: a fixed 32px height (no vertical padding, so the 20px\n * line and the border cannot push it to 34px). A full-width EffortPicker in a\n * settings panel keeps the 36px field row instead. */\nconst CHIP_PICKER_TRIGGER =\n 'inline-flex h-[var(--control-height-sm,2rem)] items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-3 text-sm font-medium text-foreground transition'\n\n/** The quiet trigger at rest: geometry, type, and keyboard focus. */\nconst QUIET_PICKER_TRIGGER_BASE =\n 'inline-flex h-[var(--control-height-sm,2rem)] shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md bg-transparent px-2 text-sm font-normal text-muted-foreground transition focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'\n\n/** The fills a quiet trigger shows on hover and while its menu is open — the\n * only two states it draws. Reads `data-state`, which every picker trigger\n * in this family stamps. */\nconst QUIET_PICKER_TRIGGER_INTERACTIVE =\n 'hover:bg-accent hover:text-foreground data-[state=open]:bg-accent data-[state=open]:text-foreground'\n\n/**\n * Trigger classes for a `quiet` picker. Height, padding, radius, and type are\n * fixed here so the triggers on one composer row agree; a caller adds only\n * width (`w-full`) and alignment (`justify-between`).\n *\n * The focus ring is drawn on the element rather than left to the tokens.css\n * `:focus-visible` floor: a quiet trigger sits beside the composer's own action\n * buttons (attach, dictate, send), which draw `focus-visible:ring-2\n * focus-visible:ring-ring`, and a row whose controls answer keyboard focus two\n * different ways reads as two families.\n *\n * `interactive: false` is for a PINNED control (see `HarnessPicker`'s\n * `lockReason`): it keeps the resting treatment and draws no hover or open\n * fill, because a fill promises a menu the control will not open.\n */\nexport function quietPickerTriggerClass({ interactive = true }: { interactive?: boolean } = {}): string {\n return interactive\n ? `${QUIET_PICKER_TRIGGER_BASE} ${QUIET_PICKER_TRIGGER_INTERACTIVE}`\n : `${QUIET_PICKER_TRIGGER_BASE} cursor-default`\n}\n\n/**\n * Guard an async action against double-submit. `run` ignores re-entrant calls\n * while a promise is in flight and flips `pending` so the caller can disable\n * the control — the fix for double-charge / double-approve on a slow network.\n * Settles (success or throw) before clearing, and no-ops state updates after\n * unmount.\n */\nexport function usePending(): { pending: boolean; run: (action: () => void | Promise<void>) => void } {\n const [pending, setPending] = useState(false)\n const inFlight = useRef(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n const run = (action: () => void | Promise<void>) => {\n if (inFlight.current) return\n let result: void | Promise<void>\n try {\n result = action()\n } catch {\n return\n }\n if (!(result instanceof Promise)) return\n inFlight.current = true\n setPending(true)\n void result.finally(() => {\n inFlight.current = false\n if (mounted.current) setPending(false)\n })\n }\n return { pending, run }\n}\n\n// ── ModelPicker ───────────────────────────────────────────────────────────\n\nexport interface ModelPickerProps {\n /** Controlled, opaque saved id. Never replaced merely because the catalogue omits it. */\n value: string\n onChange: (id: string) => void\n /** Catalogue models — from `GET`ing the app's catalogue route (see\n * `runtime/model-catalog`), plus any product-specific entries appended. */\n models: CatalogModel[]\n /** Host-owned request state. Loading takes precedence over error and results. */\n loading?: boolean\n /** Host-safe failure message. null/undefined means no failure; even '' is an error. */\n error?: string | null\n /** Ask the host to retry. The host owns fetching, loading, errors and cancellation. */\n onRetry?: () => void\n /** Disable the trigger and close an open picker; does not change the saved value. */\n disabled?: boolean\n /** Associations belong to the trigger button, not its surrounding div. */\n id?: string\n 'aria-label'?: string\n 'aria-labelledby'?: string\n 'aria-describedby'?: string\n /** Render a provider logo/badge; default is ProviderLogo. */\n renderProviderBadge?: (provider: string) => ReactNode\n /** Section label for `featured` models. */\n recommendedLabel?: string\n /** Pin a labeled section to the TOP of the list (above Recommended) for the\n * models a product wants surfaced first — e.g. a tuner app's own fine-tuned\n * models (`{ label: 'Your Fine-Tuned Models', match: (m) => m.provider === 'tuner' }`).\n * Matching models are shown only in this section, not duplicated below. */\n priorityGroup?: {\n label: string\n match: (model: CatalogModel) => boolean\n }\n /** Trigger treatment — see {@link PickerVariant}. Default `chip`. */\n variant?: PickerVariant\n /** Optional accessible trigger content for a picker docked beside an editable model field. */\n triggerContent?: ReactNode\n /**\n * Offer \"no selection\" as the first row, for a setting that may defer to a\n * default chosen elsewhere. Choosing it calls `onChange('')`, and the trigger\n * shows this label while `value` is empty.\n */\n defaultOption?: { label: string }\n}\n\nfunction formatPrice(p?: string): string | undefined {\n if (!p) return undefined\n const n = Number(p)\n if (isNaN(n) || n === 0) return undefined\n const perM = n * 1_000_000\n const amount = Number.isInteger(perM) ? perM.toFixed(0) : perM.toFixed(2)\n return `$${amount}/M`\n}\n\nconst OUTPUT_UNIT_LABEL: Record<NonNullable<CatalogModel['outputPrice']>['unit'], string> = {\n image: 'image',\n second: 'sec',\n minute: 'min',\n '1k_chars': '1K chars',\n}\n\n/**\n * The price a person pays per output, in the unit that output comes in: per\n * million prompt tokens for text, per image, per second of video, per minute of\n * audio, per 1,000 characters of speech. Undefined when the catalogue has no\n * price, so a row never shows a made-up number.\n */\nexport function formatModelPrice(model: Pick<CatalogModel, 'pricing' | 'outputPrice'>): string | undefined {\n const media = model.outputPrice\n if (media && Number.isFinite(media.usd) && media.usd > 0) {\n const amount = media.usd >= 1 ? media.usd.toFixed(2) : media.usd >= 0.01 ? String(Number(media.usd.toFixed(3))) : media.usd.toPrecision(2)\n return `$${amount}/${OUTPUT_UNIT_LABEL[media.unit]}`\n }\n return formatPrice(model.pricing?.prompt)\n}\n\nfunction formatContext(len?: number): string | undefined {\n if (!len) return undefined\n if (len >= 1_000_000) return `${(len / 1_000_000).toFixed(1)}M ctx`\n if (len >= 1_000) return `${Math.round(len / 1_000)}K ctx`\n return `${len} ctx`\n}\n\n/** The brand whose logo a row shows: its maker when the catalogue names one,\n * else its host. A fal-hosted Veo row shows Google, not fal. */\nfunction logoBrand(model: Pick<CatalogModel, 'provider' | 'maker'>): string {\n return model.maker?.trim() || model.provider\n}\n\nfunction SectionHeader({ children }: { children: ReactNode }) {\n return (\n <div className=\"px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {children}\n </div>\n )\n}\n\nfunction ModelRow({\n model,\n selected,\n onSelect,\n renderProviderBadge,\n}: {\n model: CatalogModel\n selected: boolean\n onSelect: () => void\n renderProviderBadge?: (provider: string) => ReactNode\n}) {\n const price = formatModelPrice(model)\n const ctx = formatContext(model.contextLength)\n const media = model.output && model.output !== 'text'\n const thumb = media && (model.output === 'image' || model.output === 'video') ? model.previewUrl : undefined\n const unavailable = model.status === 'unavailable'\n const flagged = unavailable || model.status === 'limited'\n // A flagged model keeps its row and stays selectable; the warning tone, not a\n // grey sentence, says it cannot run as asked.\n const capability = model.capability ?? (flagged ? model.statusReason : undefined)\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n aria-pressed={selected}\n title={`${model.name} (${model.id})`}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n selected ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n {thumb ? (\n // A media model shows what it makes: a 40px example beside its name.\n <img src={thumb} alt=\"\" loading=\"lazy\" decoding=\"async\" data-model-preview\n className={`size-10 shrink-0 rounded-md border border-border object-cover${unavailable ? ' opacity-50' : ''}`} />\n ) : (\n <span className=\"shrink-0\" aria-hidden>\n {renderProviderBadge ? renderProviderBadge(logoBrand(model)) : <ProviderLogo provider={logoBrand(model)} size={16} />}\n </span>\n )}\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n {thumb && (\n <span className=\"shrink-0\" aria-hidden>\n {renderProviderBadge ? renderProviderBadge(logoBrand(model)) : <ProviderLogo provider={logoBrand(model)} size={14} />}\n </span>\n )}\n <span className={`min-w-0 break-words [overflow-wrap:anywhere]${unavailable ? ' text-muted-foreground' : ''}`}>{model.name}</span>\n {flagged && (\n <StatusPill tone=\"warning\" size=\"sm\" className=\"shrink-0\" title={model.statusReason} data-model-status={model.status}>\n {unavailable ? 'Unavailable' : 'Limited'}\n </StatusPill>\n )}\n </span>\n {capability && (\n <span className=\"mt-0.5 block truncate text-xs font-normal text-muted-foreground\" data-model-capability>{capability}</span>\n )}\n {(ctx || price || (!media && !model.supportsTools)) && (\n <span className=\"mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs font-normal text-muted-foreground\">\n {!media && !model.supportsTools && <span className=\"rounded bg-secondary px-1.5 py-0.5\">no tools</span>}\n {!media && ctx && <span>{ctx}</span>}\n {price && <span data-model-price>{price}</span>}\n </span>\n )}\n </span>\n {selected && <CheckGlyph className=\"h-3.5 w-3.5 shrink-0 text-primary\" />}\n </button>\n )\n}\n\n/**\n * Searchable model picker pill + popover. A featured model is recommended\n * when the catalog marks it as a current choice. The first view stays short;\n * search and the browse action retain every routeable legacy model.\n *\n * This is the CANONICAL ecosystem model picker (see docs/ui-picker-canon.md).\n * Catalogue requests, retry outcomes and persisted selections belong to the host.\n * The non-modal dialog uses native buttons, not a partial combobox/listbox:\n * Tab and Enter/Space work normally; arrow keys also move through its controls.\n */\nexport function ModelPicker({\n value, onChange, models, loading = false, error, onRetry, disabled = false,\n id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy,\n renderProviderBadge, recommendedLabel = 'Recommended', priorityGroup,\n variant = 'chip', triggerContent, defaultOption,\n}: ModelPickerProps) {\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [showAll, setShowAll] = useState(false)\n const changeOpen = useCallback((next: boolean) => {\n setOpen(next && !disabled)\n if (!next) {\n setQuery('')\n setShowAll(false)\n }\n }, [disabled])\n const expanded = open && !disabled\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(expanded, changeOpen)\n const inputRef = useRef<HTMLInputElement>(null)\n const panelId = useId()\n const valueId = useId()\n const unavailable = error != null\n const sortedModels = useMemo(() => sortModelsByFreshness(models), [models])\n const contentKey = useMemo(() => ({ query, showAll, loading, error, models, value, priorityGroup }),\n [query, showAll, loading, error, models, value, priorityGroup])\n\n useEffect(() => {\n if (disabled) changeOpen(false)\n }, [disabled, changeOpen])\n\n useEffect(() => {\n if (!expanded) return\n // The portal's placement may commit a second render. Focus after that too.\n const focus = () => inputRef.current?.focus()\n focus()\n if (typeof requestAnimationFrame !== 'function') return\n const frame = requestAnimationFrame(focus)\n return () => cancelAnimationFrame(frame)\n }, [expanded])\n\n useEffect(() => {\n const panel = panelRef.current\n if (!expanded || !panel) return\n function onKeyDown(event: KeyboardEvent) {\n if (event.defaultPrevented || event.isComposing || event.altKey || event.ctrlKey || event.metaKey) return\n const input = inputRef.current\n if (!input || !panel) return\n const controls: HTMLElement[] = [input, ...panel.querySelectorAll<HTMLButtonElement>('button:not(:disabled)')]\n const index = controls.indexOf(document.activeElement as HTMLElement)\n if (index < 0) return\n if (event.key === 'Tab') {\n // Portals are last in DOM order. Exit beside the trigger, not at the\n // top of the page; Shift+Tab from search returns to the trigger itself.\n if ((event.shiftKey && index === 0) || (!event.shiftKey && index === controls.length - 1)) {\n changeOpen(false)\n triggerRef.current?.focus()\n if (event.shiftKey) event.preventDefault()\n }\n return\n }\n let next: number\n if (event.key === 'ArrowDown') next = (index + 1) % controls.length\n else if (event.key === 'ArrowUp') next = (index - 1 + controls.length) % controls.length\n else if (event.key === 'Home' && index > 0) next = 1\n else if (event.key === 'End' && index > 0) next = controls.length - 1\n else return // Home/End in search remain text-editing keys.\n event.preventDefault()\n controls[next]?.focus()\n }\n panel.addEventListener('keydown', onKeyDown)\n return () => panel.removeEventListener('keydown', onKeyDown)\n }, [expanded, changeOpen, panelRef, triggerRef])\n\n const selected = sortedModels.find((m) => m.id === value)\n const selectedLabel = selected?.name ?? value\n const describeValue = triggerContent != null || Boolean(id || ariaLabel || ariaLabelledBy)\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase()\n if (!q) return null\n return sortedModels.filter(\n (m) =>\n m.id.toLowerCase().includes(q) ||\n m.name.toLowerCase().includes(q) ||\n (m.description?.toLowerCase() ?? '').includes(q) ||\n m.provider.toLowerCase().includes(q) ||\n (m.maker?.toLowerCase() ?? '').includes(q),\n )\n }, [sortedModels, query])\n\n const sections = useMemo(() => {\n const isPriority = priorityGroup ? (m: CatalogModel) => priorityGroup.match(m) : () => false\n const priority = priorityGroup ? sortedModels.filter(isPriority) : []\n const featured = sortedModels.filter((model) => model.featured && !isPriority(model))\n const recommended = featured.slice(0, MAX_RECOMMENDED_MODELS)\n const visibleIds = new Set([...priority, ...recommended].map((model) => model.id))\n const currentSelection = sortedModels.find((model) => model.id === value && !visibleIds.has(model.id))\n const byProvider: Array<{ provider: string; items: CatalogModel[] }> = []\n for (const model of sortedModels) {\n if (visibleIds.has(model.id)) continue\n const last = byProvider[byProvider.length - 1]\n if (last && last.provider === model.provider) last.items.push(model)\n else byProvider.push({ provider: model.provider, items: [model] })\n }\n return { priority, recommended, currentSelection, byProvider }\n }, [sortedModels, priorityGroup, value])\n\n const select = (modelId: string) => {\n if (disabled || (modelId && (loading || unavailable))) return\n changeOpen(false)\n triggerRef.current?.focus()\n onChange(modelId)\n }\n const row = (model: CatalogModel) => (\n <ModelRow key={model.id} model={model} selected={model.id === value} onSelect={() => select(model.id)} renderProviderBadge={renderProviderBadge} />\n )\n\n return (\n <div ref={containerRef} className={variant === 'field' ? 'relative flex w-full min-w-0' : 'relative inline-flex min-w-0 max-w-full'}>\n <button\n type=\"button\"\n {...triggerProps}\n id={id}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={[ariaDescribedBy, describeValue && value ? valueId : undefined].filter(Boolean).join(' ') || undefined}\n aria-haspopup=\"dialog\"\n aria-controls={expanded ? panelId : undefined}\n onClick={() => changeOpen(!expanded)}\n onKeyDown={(event) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n changeOpen(true)\n }\n }}\n title={selectedLabel || defaultOption?.label || 'Select model'}\n data-state={expanded ? 'open' : 'closed'}\n className={`${\n variant === 'field'\n ? fieldPickerTriggerClass({ interactive: !disabled })\n : variant === 'quiet'\n ? quietPickerTriggerClass({ interactive: !disabled })\n : `${CHIP_PICKER_TRIGGER} ${disabled ? '' : 'hover:bg-accent'}`\n } max-w-full disabled:cursor-not-allowed disabled:opacity-50`}\n >\n {triggerContent ?? <span className={variant === 'field' ? 'flex min-w-0 flex-1 items-center gap-2' : 'contents'}>\n <span className=\"shrink-0\" aria-hidden>\n {selected ? (renderProviderBadge ? renderProviderBadge(logoBrand(selected)) : <ProviderLogo provider={logoBrand(selected)} size={16} />) : <SparkleGlyph className=\"h-3.5 w-3.5 text-muted-foreground\" />}\n </span>\n <span className={variant === 'field' ? `min-w-0 flex-1 truncate ${value ? '' : 'text-muted-foreground'}` : 'max-w-[160px] truncate'}>\n {selectedLabel || defaultOption?.label || 'Select model'}</span>\n </span>}\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n </button>\n {describeValue && value && <span id={valueId} className=\"sr-only\">Selected model: {selectedLabel}</span>}\n\n <PopoverSurface\n open={expanded}\n id={panelId}\n role=\"dialog\"\n aria-label=\"Choose a model\"\n contentKey={contentKey}\n triggerRef={triggerRef}\n panelRef={panelRef}\n matchTriggerWidth={variant === 'field'}\n side={variant === 'field' ? 'below' : undefined}\n className={`flex w-[420px] max-w-[calc(100vw-2rem)] flex-col overflow-hidden rounded-xl border border-card-edge bg-popover text-foreground ${OVERLAY_SHADOW}`}\n >\n <div className=\"shrink-0 border-b border-border px-3 py-2\">\n <div className=\"flex items-center gap-2 rounded-lg border border-strong bg-background px-3 py-2 focus-within:ring-2 focus-within:ring-ring\">\n <SearchGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search models...\"\n aria-label=\"Search all models\"\n className=\"min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n </div>\n </div>\n {/* min-h-0 lets results absorb the surface's viewport height limit. */}\n <div className=\"max-h-[520px] min-h-0 overflow-y-auto p-1 pb-2\">\n {defaultOption && !query.trim() && (\n <button\n type=\"button\"\n onClick={() => select('')}\n aria-pressed={!value}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n value ? 'hover:bg-accent' : 'bg-primary/10 font-medium'\n }`}\n >\n <SparkleGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1\">{defaultOption.label}</span>\n {!value && <CheckGlyph className=\"h-3.5 w-3.5 shrink-0 text-primary\" />}\n </button>\n )}\n {value && !selected && (\n <div className=\"mx-2 mb-1 mt-2 rounded-lg bg-secondary px-3 py-2 text-sm\">\n <p className=\"text-xs font-medium text-muted-foreground\">Saved model</p>\n <p className=\"break-words [overflow-wrap:anywhere]\">{value}</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {loading || unavailable ? 'Your saved selection is unchanged.' : 'Not in this catalogue. Your saved selection is unchanged.'}\n </p>\n </div>\n )}\n {loading ? (\n <div role=\"status\" className=\"px-3 py-4 text-center text-sm text-muted-foreground\">Loading models...</div>\n ) : unavailable ? (\n <div className=\"px-3 py-4 text-sm\">\n <div role=\"alert\">\n <p className=\"font-medium\">Model catalogue unavailable</p>\n <p className=\"mt-1 break-words text-muted-foreground [overflow-wrap:anywhere]\">{error || 'The catalogue could not be loaded.'}</p>\n </div>\n {onRetry && <button type=\"button\" className={`mt-3 rounded-md border border-border bg-card px-3 py-2 font-medium hover:bg-accent ${POPOVER_OPTION_FOCUS}`}\n onClick={() => {\n // Retry may remove this button synchronously. Keep focus on\n // the stable search input while the host updates its state.\n inputRef.current?.focus()\n onRetry()\n }}>Retry</button>}\n </div>\n ) : models.length === 0 ? (\n <div role=\"status\" className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No models available</div>\n ) : filtered ? (\n <>\n {filtered.length === 0 && <div role=\"status\" className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No models match your search</div>}\n {filtered.map(row)}\n </>\n ) : (\n <>\n {priorityGroup && sections.priority.length > 0 && <>\n <SectionHeader>{priorityGroup.label}</SectionHeader>\n {sections.priority.map(row)}\n </>}\n {sections.currentSelection && !showAll && <>\n <SectionHeader>Selected model</SectionHeader>\n {row(sections.currentSelection)}\n </>}\n {sections.recommended.length > 0 && <>\n <SectionHeader>{recommendedLabel}</SectionHeader>\n {sections.recommended.map(row)}\n </>}\n {sections.recommended.length === 0 && sections.priority.length === 0 && !sections.currentSelection && (\n <div className=\"px-3 py-3 text-sm text-muted-foreground\">Search or browse all models</div>\n )}\n {sections.byProvider.length > 0 && <button\n type=\"button\"\n aria-expanded={showAll}\n onClick={() => setShowAll(!showAll)}\n className={`mt-2 w-full rounded-md px-3 py-2 text-left text-sm text-muted-foreground hover:bg-accent hover:text-foreground ${POPOVER_OPTION_FOCUS}`}\n >{showAll ? 'Hide older models' : 'Browse all models'}</button>}\n {showAll && sections.byProvider.map((group) => (\n <div key={group.provider}>\n <SectionHeader>{group.provider}</SectionHeader>\n {group.items.map(row)}\n </div>\n ))}\n </>\n )}\n </div>\n </PopoverSurface>\n </div>\n )\n}\n\n// ── EffortPicker ──────────────────────────────────────────────────────────\n\n/** One reasoning-budget level: the engine `id` is unchanged (the value the\n * product sends to the loop); only the user-facing `label` is renamed to the\n * plainer \"how hard should it think\" vocabulary from docs/product-surfaces.md.\n * `low`→Quick, `medium`→Standard, `high`→Extended. The mapping is overridable\n * via `EffortPickerProps.levels`, so a product can relabel without losing the\n * ids the runtime expects. */\nexport interface EffortLevel {\n id: string\n label: string\n}\n\n/**\n * The engine ids this package NAMES, and the word each one reads as.\n *\n * Deliberately WIDER than {@link DEFAULT_EFFORT_LEVELS}: the default list is\n * what a picker OFFERS when a product declares nothing, while this is the\n * vocabulary — the labels a named id keeps wherever it appears. The two are\n * different questions, and folding them together is what made a real engine\n * rung read as its own id: `xhigh` and `ultracode` are rungs claude-code\n * applies above `high`, so a product declaring them got \"Xhigh\" while every\n * neighbouring rung had a plain English word. Naming them here is what stops\n * each product inventing its own — the drift `effortLevelsFromIds` exists to\n * prevent. They stay OUT of the offered default because most backends do not\n * apply them, and offering a rung the backend ignores is the defect\n * `effortLevels` was added to close.\n */\nconst KNOWN_EFFORT_LABELS = {\n off: 'Off',\n low: 'Quick',\n medium: 'Standard',\n high: 'Extended',\n xhigh: 'Extra',\n ultracode: 'Ultra',\n} as const\n\nexport const DEFAULT_EFFORT_LEVELS: readonly EffortLevel[] = [\n { id: 'off', label: KNOWN_EFFORT_LABELS.off },\n { id: 'low', label: KNOWN_EFFORT_LABELS.low },\n { id: 'medium', label: KNOWN_EFFORT_LABELS.medium },\n { id: 'high', label: KNOWN_EFFORT_LABELS.high },\n]\n\n/**\n * The user-facing label for an engine level id: the canonical vocabulary when\n * the id is one this package names, otherwise the id itself made readable\n * (`auto` -> \"Auto\", `ultra-code` -> \"Ultra code\"). Never invents a depth word,\n * so an id nobody declared a label for still reads as ITSELF and never as some\n * other level.\n */\nexport function effortLevelLabel(id: string): string {\n const known = (KNOWN_EFFORT_LABELS as Record<string, string | undefined>)[id]\n if (known) return known\n const words = id.replace(/[-_]+/g, ' ').trim()\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : id\n}\n\n/**\n * Build a levels list from the engine ids a backend applies — the shape the\n * removed `ComposerAgentControls` took as `reasoning.available`, so a product\n * migrating that list has one call to make instead of a hand-written label map\n * per product (which is how \"Quick\" and \"Low\" drift apart across surfaces).\n */\nexport function effortLevelsFromIds(ids: readonly string[]): readonly EffortLevel[] {\n return ids.map((id) => ({ id, label: effortLevelLabel(id) }))\n}\n\n/**\n * The list {@link EffortPicker} RENDERS for `value` — the declared levels, plus\n * `value` itself when the declaration omits it.\n *\n * A picker cannot honestly resolve a selected value it was not given, and the\n * failure it used to take instead — fall back to the middle entry — renders the\n * session's real depth as a DIFFERENT level's name. That is exactly the defect\n * `levels` was added to prevent: the legacy adapter's `available` list excluded\n * the `auto` sentinel because the old picker injected it, so a product mapping\n * that list straight across ships a session running on `auto` labelled\n * \"Extended\". Admitting the value is not offering a new choice — it is\n * reporting the state the session is already in, and it disappears from the\n * list as soon as the user picks a declared level.\n *\n * A blank value is not admitted (there is no honest label for it); the picker\n * renders no selection instead.\n */\nexport function reconcileEffortLevels(\n value: string,\n levels: readonly EffortLevel[] = DEFAULT_EFFORT_LEVELS,\n): readonly EffortLevel[] {\n if (!value || levels.some((l) => l.id === value)) return levels\n return [{ id: value, label: effortLevelLabel(value) }, ...levels]\n}\n\n// ── effort strength meter ─────────────────────────────────────────────────\n\n/** Segments the meter draws — fixed geometry so the ladder stays tabular\n * across levels (and across the trigger and its menu rows). */\nexport const EFFORT_METER_SEGMENTS = 4\n\n/** Filled-segment opacity ladder: translucent on the left, heavy on the\n * right — the ramp carries strength even at a glance, the count carries it\n * exactly. Unfilled segments sit at a fixed ghost opacity. */\nconst EFFORT_METER_FILL_OPACITY = [0.25, 0.5, 0.75, 1] as const\nconst EFFORT_METER_GHOST_OPACITY = 0.15\n\n/** Level ids that mean \"no reasoning\" — the meter renders all-ghost. */\nconst OFF_LEVEL_IDS: ReadonlySet<string> = new Set(['off', 'none'])\n\n/**\n * Ids that name a POLICY rather than a depth: `auto` means \"let the harness and\n * model decide\", so it has no position on a strength ladder.\n *\n * Without this it lands wherever the declaration puts it and draws a filled\n * meter — measured on a product that offers `auto` first: every harness read\n * `Auto` at a FULL four bars, including `cli-base`, which has no agent to think\n * at all. A meter is a claim about how hard the run will think, and a sentinel\n * cannot make that claim.\n */\nconst UNPLACEABLE_LEVEL_IDS: ReadonlySet<string> = new Set(['auto'])\n\n/**\n * Filled-segment count for a level: 0 for off/none (or an id the levels list\n * does not carry); otherwise the level's position among the non-off choices\n * scaled onto the meter, so the ladder reads low < medium < high and the top\n * level fills the whole scale. The canonical four levels land 0 / 1 / 2 / 4.\n */\nexport function effortMeterFill(\n levelId: string,\n levels: readonly EffortLevel[] = DEFAULT_EFFORT_LEVELS,\n): number {\n if (OFF_LEVEL_IDS.has(levelId) || UNPLACEABLE_LEVEL_IDS.has(levelId)) return 0\n const active = levels.filter((l) => !OFF_LEVEL_IDS.has(l.id) && !UNPLACEABLE_LEVEL_IDS.has(l.id))\n const index = active.findIndex((l) => l.id === levelId)\n if (index < 0 || active.length === 0) return 0\n return Math.max(1, Math.floor(((index + 1) * EFFORT_METER_SEGMENTS) / active.length))\n}\n\n/**\n * The thinking-strength meter: four 12px bars, filled count = level, filled\n * opacity ramping 25→100% left to right (unfilled at a faint ghost). Purely\n * decorative — the level name is always rendered as text beside it, so the\n * meter is `aria-hidden` and adds no second accessible name.\n */\nexport function EffortMeter({ fill, className }: { fill: number; className?: string }) {\n return (\n <span aria-hidden className={`inline-flex items-center gap-[2px] ${className ?? ''}`}>\n {Array.from({ length: EFFORT_METER_SEGMENTS }, (_, i) => (\n <span\n key={i}\n className=\"h-3 w-[3px] rounded-full bg-current\"\n style={{ opacity: i < fill ? EFFORT_METER_FILL_OPACITY[i] : EFFORT_METER_GHOST_OPACITY }}\n />\n ))}\n </span>\n )\n}\n\nexport interface EffortPickerProps {\n value: string\n onChange: (id: string) => void\n /** Selectable levels (engine id + user-facing label). Defaults to the plain\n * \"Thinking\" vocabulary; override to relabel without changing the ids the\n * runtime receives.\n *\n * A list that omits the current `value` is not a rendering error the picker\n * papers over: `value` is reconciled INTO the rendered list under its own\n * name (see {@link reconcileEffortLevels}), because a control must report\n * the depth the session is running at and never some other list entry. */\n levels?: readonly EffortLevel[]\n /** Prefix shown before the active level on the pill — the \"what is this\"\n * context the bare value lacked. Default \"Thinking\". Pass '' to hide it. */\n label?: string\n /** Fill the container instead of shrink-wrapping — opt-in, default `false`;\n * see {@link pickerRootClass} for when and why. */\n fullWidth?: boolean\n /** Trigger treatment — see {@link PickerVariant}. Default `chip`. */\n variant?: PickerVariant\n /** Disable the trigger and close an open menu; does not change the value. */\n disabled?: boolean\n /** Associations belong to the trigger button, for a picker labelled by a form field. */\n id?: string\n 'aria-describedby'?: string\n}\n\n/** Thinking-budget selector pill, styled to match {@link ModelPicker}. Show\n * it only when the selected model `supportsReasoning`. \"Thinking\" is the\n * plain-English name for what was internally called \"effort\".\n *\n * The CANONICAL ecosystem effort picker — sandbox-ui's reasoning menu (inside\n * its `chat/AgentSessionControls`) is legacy and frozen. */\nexport function EffortPicker({ value, onChange, levels = DEFAULT_EFFORT_LEVELS, label = 'Thinking', fullWidth = false, variant = 'chip',\n disabled = false, id, 'aria-describedby': ariaDescribedBy }: EffortPickerProps) {\n const [requestedOpen, setOpen] = useState(false)\n const open = requestedOpen && !disabled\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const field = variant === 'field'\n const panelId = useId()\n const rendered = reconcileEffortLevels(value, levels)\n // The strength ladder is computed over the DECLARED levels only, so admitting\n // the selected value cannot shift where the declared ones sit on the meter.\n // A level the declaration does not carry has no position on that ladder, so\n // it renders with NO meter — an all-ghost meter is what `off` looks like, and\n // \"we cannot place this\" is not \"no thinking\".\n const isDeclared = (id: string) => levels.some((l) => l.id === id) && !UNPLACEABLE_LEVEL_IDS.has(id)\n const selected = rendered.find((l) => l.id === value)\n\n return (\n <div ref={containerRef} className={pickerRootClass(fullWidth || field)}>\n <button\n type=\"button\"\n {...triggerProps}\n id={id}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-controls={open ? panelId : undefined}\n onClick={() => setOpen(!open)}\n title={label ? `${label} — how hard the agent reasons before answering` : 'Reasoning effort'}\n data-state={open ? 'open' : 'closed'}\n className={`${\n field\n ? fieldPickerTriggerClass({ interactive: !disabled })\n : variant === 'quiet'\n ? quietPickerTriggerClass()\n : fullWidth\n ? 'inline-flex min-h-[36px] shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent'\n : `${CHIP_PICKER_TRIGGER} shrink-0 hover:bg-accent`\n } ${fullWidth ? 'w-full' : ''}`}\n >\n <BrainGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n {/* Full width gives the label the slack, so the meter and chevron park\n on the trailing edge and the glyph stays against the text. */}\n <span className={fullWidth || field ? 'flex-1 truncate text-left' : undefined}>\n {label ? <span className=\"text-muted-foreground\">{label}: </span> : null}\n {selected ? selected.label : '—'}\n </span>\n {/* Quiet inherits the trigger's tone, so the meter is muted at rest\n and lifts with the label on hover instead of staying at full foreground. */}\n {selected && isDeclared(selected.id) && (\n <EffortMeter\n fill={effortMeterFill(selected.id, levels)}\n className={variant === 'quiet' ? 'shrink-0' : 'shrink-0 text-foreground'}\n />\n )}\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n </button>\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n // A portaled panel has no `w-full` to inherit, so a full-width trigger\n // hands its measured width to the panel instead.\n matchTriggerWidth={fullWidth || field}\n side={field ? 'below' : undefined}\n className={`w-44 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {rendered.map((l) => (\n <button\n key={l.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={l.id === value}\n onClick={() => {\n onChange(l.id)\n setOpen(false)\n }}\n className={`flex min-h-[40px] w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n l.id === value ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n <BrainGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{l.label}</span>\n {isDeclared(l.id) && (\n <EffortMeter fill={effortMeterFill(l.id, levels)} className=\"ml-auto text-foreground\" />\n )}\n {l.id === value && (\n <CheckGlyph className={`${isDeclared(l.id) ? '' : 'ml-auto '}h-3.5 w-3.5 shrink-0 text-primary`} />\n )}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n"],"mappings":"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","names":["id"]}