@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
@@ -0,0 +1,179 @@
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 useWorkspaceExport({ endpoint, enabled, fetcher }) {
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 (enabled) void refresh();
82
+ }, [enabled, 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
+ const latest = exports[0];
97
+ const running = latest?.status === "running";
98
+ const retry = () => latest?.status === "running" ? void drive(latest.id) : void refresh();
99
+ return { latest, error, loading, starting, running, start, retry };
100
+ }
101
+ function WorkspaceExportPanel({ endpoint, canExport, title = "Export workspace data", description = DEFAULT_DESCRIPTION, className, fetcher, continuesInBackground = false }) {
102
+ const { latest, error, loading, starting, running, start, retry } = useWorkspaceExport({ endpoint, enabled: canExport, fetcher });
103
+ if (!canExport) return null;
104
+ 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: [
105
+ /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-start justify-between gap-3", children: [
106
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
107
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
108
+ description != null && description !== false ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
109
+ ] }),
110
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: start, disabled: starting || running, children: running ? "Exporting\u2026" : starting ? "Starting\u2026" : "Export" })
111
+ ] }),
112
+ 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: [
113
+ /* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 break-words", children: error }),
114
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: retry, children: "Retry" })
115
+ ] }) : null,
116
+ latest ? /* @__PURE__ */ jsxs("section", { "aria-label": "Latest export", className: "space-y-3 rounded-xl border border-border p-4", children: [
117
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
118
+ /* @__PURE__ */ jsx(StatusPill, { tone: TONE[latest.status], size: "sm", children: LABEL[latest.status] }),
119
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
120
+ latest.unitsDone,
121
+ " of ",
122
+ latest.unitsTotal,
123
+ " sources",
124
+ latest.unitsFailed ? ` \xB7 ${latest.unitsFailed} failed` : "",
125
+ " \xB7 ",
126
+ formatBytes(latest.bytes)
127
+ ] }),
128
+ latest.current ? /* @__PURE__ */ jsxs("span", { className: "min-w-0 truncate text-sm text-muted-foreground", children: [
129
+ "Now: ",
130
+ latest.current
131
+ ] }) : null
132
+ ] }),
133
+ running && continuesInBackground ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "You can close this page. The export keeps going, and the download is here when you return." }) : null,
134
+ /* @__PURE__ */ jsx(
135
+ "div",
136
+ {
137
+ role: "progressbar",
138
+ "aria-label": "Export progress",
139
+ "aria-valuemin": 0,
140
+ "aria-valuemax": latest.unitsTotal,
141
+ "aria-valuenow": latest.unitsDone,
142
+ className: "h-2 overflow-hidden rounded-full bg-muted",
143
+ children: /* @__PURE__ */ jsx(
144
+ "div",
145
+ {
146
+ className: `h-full rounded-full ${latest.status === "failed" ? "bg-destructive" : "bg-primary"} transition-[width] duration-300 motion-reduce:transition-none`,
147
+ style: { width: `${latest.unitsTotal ? Math.round(latest.unitsDone / latest.unitsTotal * 100) : 0}%` }
148
+ }
149
+ )
150
+ }
151
+ ),
152
+ latest.status === "failed" ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-destructive [overflow-wrap:anywhere]", children: [
153
+ latest.error ?? "The export failed.",
154
+ " Start a new export to try again."
155
+ ] }) : null,
156
+ latest.downloadUrl ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
157
+ /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsx("a", { href: latest.downloadUrl, download: true, children: "Download zip" }) }),
158
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
159
+ "Link expires ",
160
+ latest.downloadExpiresAt ? new Date(latest.downloadExpiresAt).toLocaleTimeString() : "soon",
161
+ "; refresh the page for a new one."
162
+ ] })
163
+ ] }) : null,
164
+ /* @__PURE__ */ jsxs("details", { className: "text-sm", children: [
165
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-muted-foreground", children: "Sources" }),
166
+ /* @__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: [
167
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 [overflow-wrap:anywhere]", children: unit.name }),
168
+ /* @__PURE__ */ jsx("span", { className: "text-sm 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) })
169
+ ] }, unit.name)) })
170
+ ] })
171
+ ] }) : loading ? /* @__PURE__ */ jsx("p", { role: "status", className: "text-sm text-muted-foreground", children: "Loading exports\u2026" }) : null
172
+ ] });
173
+ }
174
+
175
+ export {
176
+ useWorkspaceExport,
177
+ WorkspaceExportPanel
178
+ };
179
+ //# sourceMappingURL=chunk-EZFBGW23.js.map
@@ -0,0 +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 interface UseWorkspaceExportOptions {\n /** The mount path of the product's export routes. */\n endpoint: string\n /** False skips loading; the server refuses non-owners regardless. */\n enabled: boolean\n fetcher?: typeof fetch\n}\n\nexport interface WorkspaceExportState {\n /** The newest export, if any. */\n latest: ExportProgress | undefined\n /** The last request failure, as a sentence. */\n error: string | null\n loading: boolean\n starting: boolean\n running: boolean\n start: () => Promise<void>\n /** Resumes a running export, or reloads the list. */\n retry: () => void\n}\n\n/**\n * Starts an export, drives it to completion by calling `advance` while the\n * page is open, and resumes a running one on mount. `WorkspaceExportPanel`\n * and the settings `ExportSection` both draw from it.\n */\nexport function useWorkspaceExport({ endpoint, enabled, fetcher }: UseWorkspaceExportOptions): WorkspaceExportState {\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 (enabled) void refresh()\n }, [enabled, 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 const latest = exports[0]\n const running = latest?.status === 'running'\n const retry = () => (latest?.status === 'running' ? void drive(latest.id) : void refresh())\n return { latest, error, loading, starting, running, start, retry }\n}\n\nexport function WorkspaceExportPanel({ endpoint, canExport, title = 'Export workspace data', description = DEFAULT_DESCRIPTION, className, fetcher, continuesInBackground = false }: WorkspaceExportPanelProps) {\n const { latest, error, loading, starting, running, start, retry } = useWorkspaceExport({ endpoint, enabled: canExport, fetcher })\n if (!canExport) return null\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={retry}>\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-sm 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-sm 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-sm 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,11 +1,11 @@
1
1
  import {
2
2
  usePopoverDialog
3
- } from "./chunk-YGBEGBJY.js";
3
+ } from "./chunk-HEPEGY6Q.js";
4
4
  import {
5
5
  CheckGlyph,
6
6
  OVERLAY_SHADOW,
7
7
  PopoverSurface
8
- } from "./chunk-TJ4DYKF2.js";
8
+ } from "./chunk-XG6FODYO.js";
9
9
 
10
10
  // src/web-react/workspace-switcher.tsx
11
11
  import { useCallback, useState } from "react";
@@ -116,4 +116,4 @@ export {
116
116
  WorkspaceSwitcher,
117
117
  WorkspaceInitial
118
118
  };
119
- //# sourceMappingURL=chunk-UDLA3NGY.js.map
119
+ //# sourceMappingURL=chunk-GNBBY3KN.js.map
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-I6BZVIRH.js";
5
5
  import {
6
6
  defineAppTool
7
- } from "./chunk-AJZ3EJB5.js";
7
+ } from "./chunk-IJAXOQJU.js";
8
8
  import {
9
9
  ToolInputError
10
- } from "./chunk-7BYY7QKC.js";
10
+ } from "./chunk-E65YV7VV.js";
11
11
 
12
12
  // src/agent-profiles/index.ts
13
13
  import {
@@ -772,4 +772,4 @@ export {
772
772
  PROFILE_MEMORY_PREFIX,
773
773
  profileTools
774
774
  };
775
- //# sourceMappingURL=chunk-KOTNMTHV.js.map
775
+ //# sourceMappingURL=chunk-HCFVEJQL.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  usePopover
3
- } from "./chunk-TJ4DYKF2.js";
3
+ } from "./chunk-XG6FODYO.js";
4
4
 
5
5
  // src/web-react/popover-dialog.ts
6
6
  import { useEffect, useId } from "react";
@@ -45,4 +45,4 @@ function usePopoverDialog(open, onOpenChange) {
45
45
  export {
46
46
  usePopoverDialog
47
47
  };
48
- //# sourceMappingURL=chunk-YGBEGBJY.js.map
48
+ //# sourceMappingURL=chunk-HEPEGY6Q.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  OPENUI_NODE_JSON_SCHEMA,
6
6
  OPENUI_NODE_TYPES
7
- } from "./chunk-7BYY7QKC.js";
7
+ } from "./chunk-E65YV7VV.js";
8
8
 
9
9
  // src/tools/openai.ts
10
10
  var APP_TOOL_NAMES = ["submit_proposal", "schedule_followup", "render_ui", "add_citation"];
@@ -112,4 +112,4 @@ export {
112
112
  customToolToOpenAI,
113
113
  findCustomTool
114
114
  };
115
- //# sourceMappingURL=chunk-AJZ3EJB5.js.map
115
+ //# sourceMappingURL=chunk-IJAXOQJU.js.map
@@ -11,7 +11,7 @@ import {
11
11
  OVERLAY_SHADOW,
12
12
  PopoverSurface,
13
13
  usePopover
14
- } from "./chunk-TJ4DYKF2.js";
14
+ } from "./chunk-XG6FODYO.js";
15
15
 
16
16
  // src/web-react/session-history.tsx
17
17
  import {
@@ -746,4 +746,4 @@ export {
746
746
  formatSessionTimestamp,
747
747
  SessionHistoryPanel
748
748
  };
749
- //# sourceMappingURL=chunk-EQRVOMMS.js.map
749
+ //# sourceMappingURL=chunk-KMIDRUUH.js.map
@@ -10,12 +10,12 @@ import {
10
10
  filterAcceptedFiles,
11
11
  isAcceptedFileType,
12
12
  renamePastedImages
13
- } from "./chunk-YESFGM56.js";
13
+ } from "./chunk-5IS3GCMR.js";
14
14
  import {
15
15
  OVERLAY_SHADOW,
16
16
  POPOVER_OPTION_FOCUS,
17
17
  PopoverSurface
18
- } from "./chunk-TJ4DYKF2.js";
18
+ } from "./chunk-XG6FODYO.js";
19
19
  import {
20
20
  ATTACHMENT_ACCEPT,
21
21
  ATTACHMENT_MAX_COUNT,
@@ -267,7 +267,7 @@ import {
267
267
  import { focusFieldWithin } from "@tangle-network/ui/utils";
268
268
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
269
269
  function createLazyMentionEditor() {
270
- return lazy2(() => import("./mention-editor-ZJAOUDEE.js").then((m) => m.loadMentionEditor()));
270
+ return lazy2(() => import("./mention-editor-NOS5R3AU.js").then((m) => m.loadMentionEditor()));
271
271
  }
272
272
  var IS_APPLE_PLATFORM = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/i.test(navigator.platform);
273
273
  function SendGlyph({ className }) {
@@ -1552,4 +1552,4 @@ export {
1552
1552
  ComposerModeControls,
1553
1553
  EntryComposer
1554
1554
  };
1555
- //# sourceMappingURL=chunk-BBHVV37T.js.map
1555
+ //# sourceMappingURL=chunk-MSAGTFN4.js.map
@@ -0,0 +1,70 @@
1
+ import {
2
+ ASSIGNABLE_WORKSPACE_ROLES,
3
+ hasWorkspaceRole
4
+ } from "./chunk-6XIAPIW6.js";
5
+
6
+ // src/teams-react/components/RoleSelect.tsx
7
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@tangle-network/ui/primitives";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var ROLE_CAPABILITY = {
10
+ viewer: "Can view",
11
+ editor: "Can edit",
12
+ admin: "Can manage settings and people",
13
+ owner: "Owner"
14
+ };
15
+ var ROLE_DESCRIPTION = {
16
+ viewer: "Reads everything; can\u2019t chat or edit",
17
+ editor: "Also chats with the agent and edits",
18
+ admin: "Also invites people and sets roles"
19
+ };
20
+ function RoleSelect({ value, onChange, ariaLabel, id, disabled }) {
21
+ return /* @__PURE__ */ jsxs(Select, { value, onValueChange: (role) => onChange(role), disabled, children: [
22
+ /* @__PURE__ */ jsx(SelectTrigger, { id, "aria-label": ariaLabel, children: /* @__PURE__ */ jsx(SelectValue, { children: ROLE_CAPABILITY[value] }) }),
23
+ /* @__PURE__ */ jsx(SelectContent, { children: ASSIGNABLE_WORKSPACE_ROLES.map((role) => /* @__PURE__ */ jsxs(SelectItem, { value: role, textValue: ROLE_CAPABILITY[role], children: [
24
+ /* @__PURE__ */ jsx("span", { className: "block", children: ROLE_CAPABILITY[role] }),
25
+ /* @__PURE__ */ jsx("span", { className: "block font-normal text-muted-foreground", "data-role-description": true, children: ROLE_DESCRIPTION[role] })
26
+ ] }, role)) })
27
+ ] });
28
+ }
29
+
30
+ // src/teams-react/components/member-access.ts
31
+ function canManageMember(member, currentRole, currentUserId) {
32
+ if (!hasWorkspaceRole(currentRole, "admin") || member.role === "owner" || member.inherited) return false;
33
+ return !isCurrentMember(member, currentUserId);
34
+ }
35
+ function isCurrentMember(member, currentUserId) {
36
+ return currentUserId != null && member.userId === currentUserId;
37
+ }
38
+ function memberRoleLabel(member) {
39
+ if (member.role === "owner") return member.inherited ? "Organization admin" : "Owner";
40
+ return ROLE_CAPABILITY[member.role];
41
+ }
42
+
43
+ // src/teams-react/components/invitation-detail.ts
44
+ function shortDate(value) {
45
+ return new Date(value).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
46
+ }
47
+ function invitationDetail(invitation) {
48
+ const role = ROLE_CAPABILITY[invitation.permissions];
49
+ switch (invitation.status) {
50
+ case "accepted":
51
+ return invitation.acceptedAt ? `${role} \xB7 joined ${shortDate(invitation.acceptedAt)}` : `${role} \xB7 joined`;
52
+ case "pending": {
53
+ const delivery = invitation.emailStatus === "sent" ? "email sent" : invitation.emailStatus === "not_sent" ? "email not sent yet" : null;
54
+ return [role, delivery, `expires ${shortDate(invitation.expiresAt)}`].filter(Boolean).join(" \xB7 ");
55
+ }
56
+ case "expired":
57
+ return `${role} \xB7 expired ${shortDate(invitation.expiresAt)}`;
58
+ case "revoked":
59
+ return `${role} \xB7 revoked`;
60
+ }
61
+ }
62
+
63
+ export {
64
+ RoleSelect,
65
+ canManageMember,
66
+ isCurrentMember,
67
+ memberRoleLabel,
68
+ invitationDetail
69
+ };
70
+ //# sourceMappingURL=chunk-OBKVNZPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/teams-react/components/RoleSelect.tsx","../src/teams-react/components/member-access.ts","../src/teams-react/components/invitation-detail.ts"],"sourcesContent":["/**\n * The role picker both team panels and the settings sections share: the ui\n * `Select`, with each role named by what it lets a person do and, in the open\n * list, one line on what it adds, so choosing one never needs a help page.\n * The trigger shows the name alone and fills its container; give the\n * container the width.\n */\n\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@tangle-network/ui/primitives'\nimport { ASSIGNABLE_WORKSPACE_ROLES, type AssignableWorkspaceRole, type WorkspaceRole } from '../../teams/roles'\n\n/** Each workspace role in the words a person decides by. */\nexport const ROLE_CAPABILITY: Record<WorkspaceRole, string> = {\n viewer: 'Can view',\n editor: 'Can edit',\n admin: 'Can manage settings and people',\n owner: 'Owner',\n}\n\n// What each assignable role adds, under its name in the open list; chatting\n// with the agent is what a viewer cannot do. Each line fits a phone-width\n// trigger, so the open list is never wider than the screen.\nconst ROLE_DESCRIPTION: Record<AssignableWorkspaceRole, string> = {\n viewer: 'Reads everything; can’t chat or edit',\n editor: 'Also chats with the agent and edits',\n admin: 'Also invites people and sets roles',\n}\n\nexport interface RoleSelectProps {\n value: WorkspaceRole\n onChange(role: WorkspaceRole): void\n /** Accessible name (no visible label — the panels label the row). */\n ariaLabel: string\n id?: string\n disabled?: boolean\n}\n\nexport function RoleSelect({ value, onChange, ariaLabel, id, disabled }: RoleSelectProps) {\n return (\n <Select value={value} onValueChange={(role) => onChange(role as WorkspaceRole)} disabled={disabled}>\n <SelectTrigger id={id} aria-label={ariaLabel}>\n <SelectValue>{ROLE_CAPABILITY[value]}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {ASSIGNABLE_WORKSPACE_ROLES.map((role) => (\n <SelectItem key={role} value={role} textValue={ROLE_CAPABILITY[role]}>\n <span className=\"block\">{ROLE_CAPABILITY[role]}</span>\n <span className=\"block font-normal text-muted-foreground\" data-role-description>{ROLE_DESCRIPTION[role]}</span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n","/**\n * Who may change a member's access, and the words for a role shown as text.\n * `MembersPanel` and the settings `MembersSection` both read these, so the two\n * member lists apply one rule.\n */\n\nimport { hasWorkspaceRole, type WorkspaceRole } from '../../teams/roles'\nimport type { MemberView } from '../contracts'\nimport { ROLE_CAPABILITY } from './RoleSelect'\n\n/**\n * Whether the viewer can change this member's role or remove them: an admin\n * can, except for an owner, an inherited organization admin, or their own row,\n * so no one demotes or removes themselves from a list.\n */\nexport function canManageMember(member: MemberView, currentRole: WorkspaceRole, currentUserId?: string | null): boolean {\n if (!hasWorkspaceRole(currentRole, 'admin') || member.role === 'owner' || member.inherited) return false\n return !isCurrentMember(member, currentUserId)\n}\n\n/** True on the viewer's own row. */\nexport function isCurrentMember(member: MemberView, currentUserId?: string | null): boolean {\n return currentUserId != null && member.userId === currentUserId\n}\n\n/** A role shown as text rather than a picker. */\nexport function memberRoleLabel(member: MemberView): string {\n if (member.role === 'owner') return member.inherited ? 'Organization admin' : 'Owner'\n return ROLE_CAPABILITY[member.role]\n}\n","/** The line under an invitation's address, shared by `InvitationsPanel` and the settings `InvitationsSection`. */\n\nimport type { InvitationView } from '../contracts'\nimport { ROLE_CAPABILITY } from './RoleSelect'\n\nfunction shortDate(value: Date | number | string): string {\n return new Date(value).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\n/**\n * The line under the address: what the invitee can do, then only what is true\n * of this invitation now. Every status says itself in words, so a past\n * invitation reads as joined, expired or revoked without a badge.\n */\nexport function invitationDetail(invitation: InvitationView): string {\n const role = ROLE_CAPABILITY[invitation.permissions]\n switch (invitation.status) {\n case 'accepted':\n return invitation.acceptedAt ? `${role} · joined ${shortDate(invitation.acceptedAt)}` : `${role} · joined`\n case 'pending': {\n const delivery = invitation.emailStatus === 'sent' ? 'email sent' : invitation.emailStatus === 'not_sent' ? 'email not sent yet' : null\n return [role, delivery, `expires ${shortDate(invitation.expiresAt)}`].filter(Boolean).join(' · ')\n }\n case 'expired':\n return `${role} · expired ${shortDate(invitation.expiresAt)}`\n case 'revoked':\n return `${role} · revoked`\n }\n}\n"],"mappings":";;;;;;AAQA,SAAS,QAAQ,eAAe,YAAY,eAAe,mBAAmB;AAiCtE,cAIE,YAJF;AA7BD,IAAM,kBAAiD;AAAA,EAC5D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AACT;AAKA,IAAM,mBAA4D;AAAA,EAChE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AACT;AAWO,SAAS,WAAW,EAAE,OAAO,UAAU,WAAW,IAAI,SAAS,GAAoB;AACxF,SACE,qBAAC,UAAO,OAAc,eAAe,CAAC,SAAS,SAAS,IAAqB,GAAG,UAC9E;AAAA,wBAAC,iBAAc,IAAQ,cAAY,WACjC,8BAAC,eAAa,0BAAgB,KAAK,GAAE,GACvC;AAAA,IACA,oBAAC,iBACE,qCAA2B,IAAI,CAAC,SAC/B,qBAAC,cAAsB,OAAO,MAAM,WAAW,gBAAgB,IAAI,GACjE;AAAA,0BAAC,UAAK,WAAU,SAAS,0BAAgB,IAAI,GAAE;AAAA,MAC/C,oBAAC,UAAK,WAAU,2CAA0C,yBAAqB,MAAE,2BAAiB,IAAI,GAAE;AAAA,SAFzF,IAGjB,CACD,GACH;AAAA,KACF;AAEJ;;;ACtCO,SAAS,gBAAgB,QAAoB,aAA4B,eAAwC;AACtH,MAAI,CAAC,iBAAiB,aAAa,OAAO,KAAK,OAAO,SAAS,WAAW,OAAO,UAAW,QAAO;AACnG,SAAO,CAAC,gBAAgB,QAAQ,aAAa;AAC/C;AAGO,SAAS,gBAAgB,QAAoB,eAAwC;AAC1F,SAAO,iBAAiB,QAAQ,OAAO,WAAW;AACpD;AAGO,SAAS,gBAAgB,QAA4B;AAC1D,MAAI,OAAO,SAAS,QAAS,QAAO,OAAO,YAAY,uBAAuB;AAC9E,SAAO,gBAAgB,OAAO,IAAI;AACpC;;;ACxBA,SAAS,UAAU,OAAuC;AACxD,SAAO,IAAI,KAAK,KAAK,EAAE,mBAAmB,SAAS,EAAE,OAAO,SAAS,KAAK,WAAW,MAAM,UAAU,CAAC;AACxG;AAOO,SAAS,iBAAiB,YAAoC;AACnE,QAAM,OAAO,gBAAgB,WAAW,WAAW;AACnD,UAAQ,WAAW,QAAQ;AAAA,IACzB,KAAK;AACH,aAAO,WAAW,aAAa,GAAG,IAAI,gBAAa,UAAU,WAAW,UAAU,CAAC,KAAK,GAAG,IAAI;AAAA,IACjG,KAAK,WAAW;AACd,YAAM,WAAW,WAAW,gBAAgB,SAAS,eAAe,WAAW,gBAAgB,aAAa,uBAAuB;AACnI,aAAO,CAAC,MAAM,UAAU,WAAW,UAAU,WAAW,SAAS,CAAC,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK;AAAA,IAClG;AAAA,IACA,KAAK;AACH,aAAO,GAAG,IAAI,iBAAc,UAAU,WAAW,SAAS,CAAC;AAAA,IAC7D,KAAK;AACH,aAAO,GAAG,IAAI;AAAA,EAClB;AACF;","names":[]}
@@ -10,15 +10,15 @@ import {
10
10
  } from "./chunk-S5SRJJQG.js";
11
11
  import {
12
12
  assertRenderUiSchema
13
- } from "./chunk-YXLN4PXK.js";
13
+ } from "./chunk-2RCPPOQ4.js";
14
14
  import {
15
15
  buildAppToolOpenAITools,
16
16
  findCustomTool,
17
17
  isAppToolName
18
- } from "./chunk-AJZ3EJB5.js";
18
+ } from "./chunk-IJAXOQJU.js";
19
19
  import {
20
20
  ToolInputError
21
- } from "./chunk-7BYY7QKC.js";
21
+ } from "./chunk-E65YV7VV.js";
22
22
 
23
23
  // src/tools/capability.ts
24
24
  async function createCapabilityToken(userId, opts) {
@@ -270,4 +270,4 @@ export {
270
270
  createAppToolRuntimeExecutor,
271
271
  handleAppToolRequest
272
272
  };
273
- //# sourceMappingURL=chunk-75ZIWV4U.js.map
273
+ //# sourceMappingURL=chunk-QAF7ZTO3.js.map
@@ -18,6 +18,7 @@ import {
18
18
  } from "react";
19
19
  import { createPortal } from "react-dom";
20
20
  import { StatusPill } from "@tangle-network/ui/primitives";
21
+ import { fieldPresentation, fieldSizes, fieldTouchText } from "@tangle-network/ui/presentation";
21
22
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
22
23
  function ChevronDown({ className }) {
23
24
  return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
@@ -187,7 +188,7 @@ function pickerRootClass(fullWidth) {
187
188
  return `relative ${fullWidth ? "flex w-full" : "inline-flex"}`;
188
189
  }
189
190
  function fieldPickerTriggerClass({ interactive = true } = {}) {
190
- 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 ${interactive ? "hover:border-[var(--border-strong)] data-[state=open]:border-[var(--focus-border)]" : "cursor-default"}`;
191
+ return `${fieldPresentation} ${fieldSizes.md} ${fieldTouchText} inline-flex w-full min-w-0 items-center justify-between gap-2 whitespace-nowrap border text-left font-normal text-foreground disabled:cursor-not-allowed disabled:opacity-50 ${interactive ? "data-[state=open]:border-[var(--ix-hover-border)] data-[state=open]:shadow-[var(--ix-after)]" : "cursor-default"}`;
191
192
  }
192
193
  var CHIP_PICKER_TRIGGER = "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";
193
194
  var QUIET_PICKER_TRIGGER_BASE = "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";
@@ -728,4 +729,4 @@ export {
728
729
  EffortMeter,
729
730
  EffortPicker
730
731
  };
731
- //# sourceMappingURL=chunk-TJ4DYKF2.js.map
732
+ //# sourceMappingURL=chunk-XG6FODYO.js.map