@mcp-b/react-components 0.20.4 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/{AgentActivity-D2SHSGFX.d.ts → AgentActivity-D3OvrC02.d.ts} +14 -1
  2. package/dist/{AgentChat-C-ZyXbJz.d.ts → AgentChat-Dvkn8lce.d.ts} +23 -0
  3. package/dist/AgentChatStartScreen-CvXJBr4y.d.ts +57 -0
  4. package/dist/{ModelSelector-pecWyOFI.js → ModelSelector-CEd8M9eK.js} +14 -7
  5. package/dist/{ThinkChatActivity-CdbL0aoA.d.ts → ThinkChatActivity-FY1VWXgh.d.ts} +8 -7
  6. package/dist/{ThinkChatActivitySummary-D1xOrNrq.d.ts → ThinkChatActivitySummary-kr5fE0wg.d.ts} +14 -5
  7. package/dist/{ToggleGroup-DNd6cDJZ.d.ts → ToggleGroup-BW1jrhAF.d.ts} +10 -0
  8. package/dist/components/Activity.d.ts +33 -17
  9. package/dist/components/Activity.js +39 -27
  10. package/dist/components/AgentActivity.d.ts +1 -1
  11. package/dist/components/AgentActivityPreview.d.ts +1 -1
  12. package/dist/components/AgentActivityPreview.js +2 -2
  13. package/dist/components/AgentChat.d.ts +1 -1
  14. package/dist/components/AgentChat.js +142 -129
  15. package/dist/components/AgentChatStartScreen.d.ts +2 -0
  16. package/dist/components/AgentChatStartScreen.js +82 -0
  17. package/dist/components/Approval.js +2 -4
  18. package/dist/components/BrowserLiveView.js +5 -7
  19. package/dist/components/BrowserReplay.js +5 -4
  20. package/dist/components/BrowserRunPanel.js +1 -1
  21. package/dist/components/ChatGptSignInScreen.js +1 -1
  22. package/dist/components/Conversation.d.ts +1 -22
  23. package/dist/components/Conversation.js +2 -29
  24. package/dist/components/EmptyState.d.ts +46 -0
  25. package/dist/components/EmptyState.js +50 -0
  26. package/dist/components/Kbd.d.ts +32 -0
  27. package/dist/components/Kbd.js +34 -0
  28. package/dist/components/MessageScroller.js +1 -0
  29. package/dist/components/ModelSelector.d.ts +10 -4
  30. package/dist/components/ModelSelector.js +2 -2
  31. package/dist/components/PricingBillingToggle.js +1 -0
  32. package/dist/components/PromptInput.js +3 -2
  33. package/dist/components/Reasoning.js +1 -1
  34. package/dist/components/StatusPill.d.ts +43 -0
  35. package/dist/components/StatusPill.js +35 -0
  36. package/dist/components/Stepper.js +1 -5
  37. package/dist/components/TestResults.js +15 -15
  38. package/dist/components/ThinkChat.d.ts +9 -3
  39. package/dist/components/ThinkChatActivity.d.ts +2 -2
  40. package/dist/components/ThinkChatActivity.js +27 -15
  41. package/dist/components/ThinkChatActivitySummary.d.ts +2 -2
  42. package/dist/components/ThinkChatActivitySummary.js +69 -66
  43. package/dist/components/ThinkExecutionDetail.js +4 -4
  44. package/dist/components/ThinkExecutionRun.js +2 -2
  45. package/dist/components/ThinkExecutionWorkbench.js +5 -4
  46. package/dist/components/ThinkFullAppViewSwitch.d.ts +1 -1
  47. package/dist/components/ThinkRoutine.js +2 -10
  48. package/dist/components/ThinkRoutineBrowser.js +1 -0
  49. package/dist/components/ThinkRoutineRuns.js +3 -2
  50. package/dist/components/ThinkThreadPicker.js +4 -2
  51. package/dist/components/ToggleGroup.d.ts +1 -1
  52. package/dist/components/ToggleGroup.js +54 -7
  53. package/dist/components/ToolInvocation.js +1 -1
  54. package/dist/styles/accordion.css +2 -1
  55. package/dist/styles/activity-header.css +45 -25
  56. package/dist/styles/activity.css +26 -4
  57. package/dist/styles/agent-activity-preview-overlay.css +51 -12
  58. package/dist/styles/agent-activity-preview.css +34 -33
  59. package/dist/styles/agent-attention.css +9 -16
  60. package/dist/styles/agent-chat.css +199 -0
  61. package/dist/styles/agent-connection.css +10 -10
  62. package/dist/styles/alert-dialog.css +5 -2
  63. package/dist/styles/app-top-bar.css +4 -4
  64. package/dist/styles/approval.css +1 -7
  65. package/dist/styles/artifact.css +5 -5
  66. package/dist/styles/attachment-preview.css +1 -1
  67. package/dist/styles/badge.css +1 -1
  68. package/dist/styles/base-ui-motion.css +1 -1
  69. package/dist/styles/base.css +110 -3
  70. package/dist/styles/browser-control-overlay.css +16 -6
  71. package/dist/styles/browser-live-view.css +5 -38
  72. package/dist/styles/browser-replay.css +4 -8
  73. package/dist/styles/browser-run-panel.css +5 -19
  74. package/dist/styles/button.css +4 -2
  75. package/dist/styles/card.css +4 -4
  76. package/dist/styles/chain-of-thought.css +1 -2
  77. package/dist/styles/chatgpt-sign-in-screen.css +9 -7
  78. package/dist/styles/checkbox-group.css +4 -4
  79. package/dist/styles/code-block.css +9 -9
  80. package/dist/styles/commit.css +1 -1
  81. package/dist/styles/context-menu.css +1 -1
  82. package/dist/styles/conversation.css +7 -42
  83. package/dist/styles/description-list.css +2 -2
  84. package/dist/styles/dialog.css +17 -2
  85. package/dist/styles/empty-state.css +48 -0
  86. package/dist/styles/environment-variables.css +8 -8
  87. package/dist/styles/field.css +2 -2
  88. package/dist/styles/fieldset.css +1 -1
  89. package/dist/styles/file-tree.css +1 -1
  90. package/dist/styles/github-motion-mark.css +1 -1
  91. package/dist/styles/index.css +3 -0
  92. package/dist/styles/inline-citation.css +1 -1
  93. package/dist/styles/kbd.css +40 -0
  94. package/dist/styles/label.css +1 -1
  95. package/dist/styles/mcp-server-picker.css +1 -1
  96. package/dist/styles/media-preview-card.css +5 -3
  97. package/dist/styles/menu.css +1 -1
  98. package/dist/styles/message-scroller.css +7 -25
  99. package/dist/styles/message.css +5 -7
  100. package/dist/styles/model-selector.css +5 -8
  101. package/dist/styles/plan.css +1 -1
  102. package/dist/styles/popover.css +1 -1
  103. package/dist/styles/preview-card.css +1 -1
  104. package/dist/styles/pricing-page.css +37 -35
  105. package/dist/styles/prompt-input.css +19 -41
  106. package/dist/styles/queue.css +3 -3
  107. package/dist/styles/recipes.css +92 -9
  108. package/dist/styles/resizable-side-panel.css +1 -1
  109. package/dist/styles/responsive-sidebar-shell.css +5 -6
  110. package/dist/styles/scrollbars.css +32 -3
  111. package/dist/styles/sources.css +2 -2
  112. package/dist/styles/status-pill.css +39 -0
  113. package/dist/styles/stepper.css +2 -2
  114. package/dist/styles/streamdown.css +39 -35
  115. package/dist/styles/subagent-run.css +3 -3
  116. package/dist/styles/switch.css +1 -3
  117. package/dist/styles/tabs.css +1 -1
  118. package/dist/styles/task.css +2 -2
  119. package/dist/styles/terminal.css +6 -6
  120. package/dist/styles/test-results.css +2 -3
  121. package/dist/styles/textarea.css +1 -1
  122. package/dist/styles/theme-mode-control.css +3 -4
  123. package/dist/styles/think-execution-status.css +4 -23
  124. package/dist/styles/think-execution-workbench.css +3 -4
  125. package/dist/styles/think-full-app.css +3 -3
  126. package/dist/styles/think-routine-library.css +22 -24
  127. package/dist/styles/think-thread-picker.css +20 -47
  128. package/dist/styles/toast.css +2 -2
  129. package/dist/styles/toggle-group.css +76 -3
  130. package/dist/styles/tooltip.css +1 -2
  131. package/dist/styles/utilities.css +73 -0
  132. package/dist/styles/web-preview.css +4 -4
  133. package/dist/styles/workspace-explorer.css +6 -8
  134. package/package.json +2 -2
@@ -1,13 +1,24 @@
1
+ import { t as capitalize } from "../format-S6uWyoSb.js";
1
2
  import { displayUrl, getToolDisplayName, isBrowserToolPart, isExecuteToolPart, isFetchToolPart, isPartFailed, isPartWorking, isSessionContextToolFailure, isSessionContextToolPart, isSkillToolPart, isWorkspaceToolPart } from "./ThinkChatToolParts.js";
2
3
  import { t as summarizeToolInput } from "../summarize-tool-input-CqTcGRYy.js";
3
4
  import { getToolName, isReasoningUIPart, isToolUIPart } from "ai";
4
5
  //#region src/components/ThinkChatActivitySummary.ts
6
+ function described(action, detail) {
7
+ return detail ? {
8
+ action,
9
+ detail
10
+ } : { action };
11
+ }
5
12
  const ACTIVITY_HEADER_LABEL_MAX_LENGTH = 240;
13
+ function bounded(text) {
14
+ if (text.length <= ACTIVITY_HEADER_LABEL_MAX_LENGTH) return text;
15
+ return `${text.slice(0, ACTIVITY_HEADER_LABEL_MAX_LENGTH - 1).trimEnd()}…`;
16
+ }
6
17
  function boundedActivityHeader(header) {
7
- if (header.label.length <= ACTIVITY_HEADER_LABEL_MAX_LENGTH) return header;
8
18
  return {
9
19
  ...header,
10
- label: `${header.label.slice(0, ACTIVITY_HEADER_LABEL_MAX_LENGTH - 1).trimEnd()}…`
20
+ action: bounded(header.action),
21
+ ...header.detail ? { detail: bounded(header.detail) } : {}
11
22
  };
12
23
  }
13
24
  function getToolInput(part) {
@@ -99,9 +110,7 @@ function genericToolPresentation(part, hasUrl) {
99
110
  }
100
111
  function describeGenericToolPart(part) {
101
112
  const labels = genericToolPresentation(part, activityPartUrl(part) !== null);
102
- const label = isPartFailed(part) ? labels.failed : isPartWorking(part) ? labels.working : labels.done;
103
- const detail = summarizeToolInput(getToolInput(part));
104
- return detail ? `${label} · ${detail}` : label;
113
+ return described(isPartFailed(part) ? labels.failed : isPartWorking(part) ? labels.working : labels.done, summarizeToolInput(getToolInput(part)));
105
114
  }
106
115
  function activityPartUrl(part) {
107
116
  if (isFetchToolPart(part)) return firstHttpUrl(part.input?.url ?? part.input?.path);
@@ -150,7 +159,7 @@ function activityPartWorkspaceDestination(part) {
150
159
  }
151
160
  }
152
161
  /**
153
- * One-line natural-language label for an activity part ("Ran vp test",
162
+ * One-line natural-language action for an activity part ("Ran vp test",
154
163
  * "Fetched developers.cloudflare.com/agents/"); `null` for boundaries that
155
164
  * aren't steps (step-start). Exported so a custom `renderPart` can reuse the
156
165
  * built-in labels for tools it doesn't override.
@@ -158,12 +167,12 @@ function activityPartWorkspaceDestination(part) {
158
167
  function describeThinkChatActivityPart(part, toolRenderers) {
159
168
  if (isToolUIPart(part)) {
160
169
  const custom = toolRenderers?.[getToolName(part)]?.describe?.(part);
161
- if (custom !== void 0) return custom;
170
+ if (custom !== void 0) return custom ?? null;
162
171
  }
163
172
  if (isReasoningUIPart(part)) {
164
173
  const snippet = summarizeReasoningTitle(part.text);
165
- const label = part.state === "streaming" ? "Thinking…" : "Thought";
166
- return snippet ?? label;
174
+ const action = part.state === "streaming" ? "Thinking…" : "Thought";
175
+ return described(snippet ?? action);
167
176
  }
168
177
  if (isWorkspaceToolPart(part)) return describeWorkspaceToolPart(part);
169
178
  if (isSkillToolPart(part)) return describeSkillToolPart(part);
@@ -182,88 +191,83 @@ function describeWorkspaceToolPart(part) {
182
191
  const failed = isPartFailed(part);
183
192
  if (part.type === "tool-bash") {
184
193
  const command = part.input?.script?.trim() ?? "";
185
- if (failed) return command ? `Command failed · ${command}` : "Command failed";
194
+ if (failed) return described("Command failed", command);
186
195
  const verb = working ? "Running" : "Ran";
187
- return command ? `${verb} ${command}` : `${verb} a command`;
196
+ return command ? described(verb, command) : described(`${verb} a command`);
188
197
  }
189
- const label = (() => {
198
+ const { action, detail } = (() => {
190
199
  switch (part.type) {
191
- case "tool-read": return `${working ? "Reading" : "Read"} ${basename(part.input?.path) ?? "a file"}`;
192
- case "tool-write": return `${working ? "Writing" : "Wrote"} ${basename(part.input?.path) ?? "a file"}`;
193
- case "tool-edit": return `${working ? "Editing" : "Edited"} ${basename(part.input?.path) ?? "a file"}`;
194
- case "tool-delete": return `${working ? "Deleting" : "Deleted"} ${part.input?.path || "a file"}`;
200
+ case "tool-read": return described(working ? "Reading" : "Read", basename(part.input?.path) ?? "a file");
201
+ case "tool-write": return described(working ? "Writing" : "Wrote", basename(part.input?.path) ?? "a file");
202
+ case "tool-edit": return described(working ? "Editing" : "Edited", basename(part.input?.path) ?? "a file");
203
+ case "tool-delete": return described(working ? "Deleting" : "Deleted", part.input?.path || "a file");
195
204
  case "tool-list": {
196
205
  const path = part.input?.path;
197
- return `${working ? "Listing" : "Listed"} ${path && path !== "/" ? path : "files"}`;
206
+ return described(working ? "Listing" : "Listed", path && path !== "/" ? path : "files");
198
207
  }
199
- case "tool-find": return `${working ? "Searching" : "Searched"} for ${part.input?.pattern || "files"}`;
200
- case "tool-grep": return `${working ? "Searching" : "Searched"} for ${part.input?.query || "text"}`;
208
+ case "tool-find": return described(working ? "Searching" : "Searched", `for ${part.input?.pattern || "files"}`);
209
+ case "tool-grep": return described(working ? "Searching" : "Searched", `for ${part.input?.query || "text"}`);
201
210
  }
202
211
  })();
203
- return failed ? `${label} · failed` : label;
212
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
204
213
  }
205
214
  function describeSkillToolPart(part) {
206
215
  const working = isPartWorking(part);
207
216
  const failed = isPartFailed(part);
208
- const label = (() => {
217
+ const { action, detail } = (() => {
209
218
  switch (part.type) {
210
219
  case "tool-activate_skill": {
211
220
  const name = part.input?.name;
212
- return `${working ? "Using" : "Used"} ${name ? `the ${name} skill` : "a skill"}`;
221
+ return described(working ? "Using" : "Used", name ? `the ${name} skill` : "a skill");
213
222
  }
214
- case "tool-read_skill_resource": return `${working ? "Reading" : "Read"} ${part.input?.path ?? "a skill resource"}`;
215
- case "tool-run_skill_script": return `${working ? "Running" : "Ran"} ${part.input?.path ?? "a skill script"}`;
223
+ case "tool-read_skill_resource": return described(working ? "Reading" : "Read", part.input?.path ?? "a skill resource");
224
+ case "tool-run_skill_script": return described(working ? "Running" : "Ran", part.input?.path ?? "a skill script");
216
225
  }
217
226
  })();
218
- return failed ? `${label} · failed` : label;
227
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
219
228
  }
220
229
  function describeSessionContextToolPart(part) {
221
230
  const working = isPartWorking(part);
222
231
  const failed = isSessionContextToolFailure(part);
223
- const label = (() => {
232
+ const { action, detail } = (() => {
224
233
  switch (part.type) {
225
- case "tool-set_context": {
226
- const target = part.input?.label ? ` ${part.input.label}` : " context";
227
- return `${working ? "Updating" : "Updated"}${target}`;
228
- }
229
- case "tool-load_context": return `${working ? "Loading" : "Loaded"} ${part.input?.key ?? "context"}`;
230
- case "tool-unload_context": return `${working ? "Unloading" : "Unloaded"} ${part.input?.key ?? "context"}`;
231
- case "tool-search_context": return `${working ? "Searching" : "Searched"} ${part.input?.label ?? "context"}`;
232
- case "tool-session_search": return `${working ? "Searching" : "Searched"} conversations`;
234
+ case "tool-set_context": return described(working ? "Updating" : "Updated", part.input?.label || "context");
235
+ case "tool-load_context": return described(working ? "Loading" : "Loaded", part.input?.key ?? "context");
236
+ case "tool-unload_context": return described(working ? "Unloading" : "Unloaded", part.input?.key ?? "context");
237
+ case "tool-search_context": return described(working ? "Searching" : "Searched", part.input?.label ?? "context");
238
+ case "tool-session_search": return described(working ? "Searching" : "Searched", "conversations");
233
239
  }
234
240
  })();
235
- return failed ? `${label} · failed` : label;
241
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
236
242
  }
237
243
  function describeBrowserToolPart(part) {
238
244
  const working = isPartWorking(part);
239
245
  const failed = isPartFailed(part);
240
246
  if (part.type === "tool-browser_execute") {
241
247
  const output = part.state === "output-available" ? part.output : void 0;
242
- if (failed || output?.status === "error") return "Browser automation failed";
243
- if (output?.status === "paused") return isHumanWaitPause(output) ? "Browser waiting for human" : "Browser waiting for approval";
244
- return working ? "Using the browser" : "Used the browser";
248
+ if (failed || output?.status === "error") return described("Browser automation failed");
249
+ if (output?.status === "paused") return described(isHumanWaitPause(output) ? "Browser waiting for human" : "Browser waiting for approval");
250
+ return described(working ? "Using the browser" : "Used the browser");
245
251
  }
246
252
  const page = displayUrl(part.input?.url) || "a page";
247
- const label = (() => {
253
+ const { action, detail } = (() => {
248
254
  switch (part.type) {
249
- case "tool-browser_extract": return `${working ? "Extracting" : "Extracted"} data from ${page}`;
250
- case "tool-browser_links": return `${working ? "Listing" : "Listed"} links on ${page}`;
251
- case "tool-browser_scrape": return `${working ? "Scraping" : "Scraped"} ${page}`;
252
- default: return `${working ? "Browsing" : "Browsed"} ${page}`;
255
+ case "tool-browser_extract": return described(working ? "Extracting data" : "Extracted data", `from ${page}`);
256
+ case "tool-browser_links": return described(working ? "Listing links" : "Listed links", `on ${page}`);
257
+ case "tool-browser_scrape": return described(working ? "Scraping" : "Scraped", page);
258
+ default: return described(working ? "Browsing" : "Browsed", page);
253
259
  }
254
260
  })();
255
- return failed ? `${label} · failed` : label;
261
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
256
262
  }
257
263
  function describeExecuteToolPart(part) {
258
264
  const output = part.state === "output-available" ? part.output : void 0;
259
- if (output?.status === "paused") return isHumanWaitPause(output) ? "Code waiting for human" : "Code waiting for approval";
265
+ if (output?.status === "paused") return described(isHumanWaitPause(output) ? "Code waiting for human" : "Code waiting for approval");
260
266
  const explicitSummary = part.input ? Reflect.get(part.input, "summary") : void 0;
261
267
  const summary = typeof explicitSummary === "string" ? summarizeToolInput({ summary: explicitSummary }) : null;
262
- if (summary) return summary;
263
- if (isPartFailed(part) || output?.status === "error") return "Code failed";
264
- const label = isPartWorking(part) ? "Running code" : "Ran code";
265
- const snippet = summarizeToolInput(part.input);
266
- return snippet ? `${label} · ${snippet}` : label;
268
+ if (summary) return described(summary);
269
+ if (isPartFailed(part) || output?.status === "error") return described("Code failed");
270
+ return described(isPartWorking(part) ? "Running code" : "Ran code", summarizeToolInput(part.input));
267
271
  }
268
272
  function summarizeReasoningTitle(text) {
269
273
  const line = text.split("\n").find((candidate) => candidate.trim())?.trim();
@@ -278,9 +282,9 @@ function isHumanWaitPause(output) {
278
282
  function describeFetchToolPart(part) {
279
283
  const target = part.input?.url ?? part.input?.path ?? "";
280
284
  const output = part.state === "output-available" ? part.output : void 0;
281
- if (isPartFailed(part) || output?.ok === false) return target ? `Fetch failed · ${target}` : "Fetch failed";
285
+ if (isPartFailed(part) || output?.ok === false) return described("Fetch failed", target);
282
286
  const verb = isPartWorking(part) ? "Fetching" : "Fetched";
283
- return target ? `${verb} ${target}` : `${verb} a URL`;
287
+ return target ? described(verb, target) : described(`${verb} a URL`);
284
288
  }
285
289
  function summarizeActivitySteps(parts) {
286
290
  const byTool = /* @__PURE__ */ new Map();
@@ -339,43 +343,42 @@ function summarizeActivitySteps(parts) {
339
343
  for (const segment of genericSegments) if (!includedSegments.has(segment)) segments.push(segment);
340
344
  if (segments.length === 0) return "Worked";
341
345
  const top = segments.slice(0, 3);
342
- const joined = top.length === 1 ? top[0] : `${top.slice(0, -1).join(", ")} and ${top.at(-1)}`;
343
- return joined.charAt(0).toUpperCase() + joined.slice(1);
346
+ return capitalize(top.length === 1 ? top[0] : `${top.slice(0, -1).join(", ")} and ${top.at(-1)}`);
344
347
  }
345
348
  /** Derive the serializable collapsed header from persisted AI SDK message parts. */
346
349
  function deriveThinkChatActivityHeader(parts, options = {}) {
347
- const described = parts.flatMap((part) => {
348
- const label = describeThinkChatActivityPart(part, options.toolDescribers);
349
- return label ? [{
350
- label,
350
+ const steps = parts.flatMap((part) => {
351
+ const description = describeThinkChatActivityPart(part, options.toolDescribers);
352
+ return description ? [{
353
+ description,
351
354
  part
352
355
  }] : [];
353
356
  });
354
- if (described.length === 0) return null;
357
+ if (steps.length === 0) return null;
355
358
  if (options.isStreaming ?? parts.some(isPartWorking)) {
356
- const latest = described.at(-1);
359
+ const latest = steps.at(-1);
357
360
  const workspaceDestination = activityPartWorkspaceDestination(latest.part);
358
361
  return boundedActivityHeader({
359
- label: latest.label,
362
+ ...latest.description,
360
363
  iconKind: activityPartIconKind(latest.part),
361
364
  ...activityPartUrl(latest.part) ? { url: activityPartUrl(latest.part) } : {},
362
365
  ...workspaceDestination ? { workspaceDestination } : {}
363
366
  });
364
367
  }
365
368
  if (parts.some((part) => isToolUIPart(part) && isPartFailed(part))) return boundedActivityHeader({
366
- label: summarizeActivitySteps(parts),
369
+ action: summarizeActivitySteps(parts),
367
370
  iconKind: "approval"
368
371
  });
369
372
  const workspaceRepresentative = parts.find((part) => activityPartIconKind(part) === "edit" && activityPartWorkspaceDestination(part)) ?? parts.find((part) => activityPartWorkspaceDestination(part));
370
373
  const workspaceDestination = workspaceRepresentative ? activityPartWorkspaceDestination(workspaceRepresentative) : null;
371
374
  const representative = parts.find((part) => activityPartIconKind(part) === "edit") ?? parts.find((part) => activityPartUrl(part)) ?? parts.find((part) => describeThinkChatActivityPart(part));
372
375
  if (!representative) return boundedActivityHeader({
373
- label: summarizeActivitySteps(parts),
376
+ action: summarizeActivitySteps(parts),
374
377
  iconKind: "tool"
375
378
  });
376
379
  const url = activityPartUrl(representative);
377
380
  return boundedActivityHeader({
378
- label: summarizeActivitySteps(parts),
381
+ action: summarizeActivitySteps(parts),
379
382
  iconKind: activityPartIconKind(representative),
380
383
  ...url ? { url } : {},
381
384
  ...workspaceDestination ? { workspaceDestination } : {}
@@ -387,14 +390,14 @@ function deriveThinkChatActivityHeader(parts, options = {}) {
387
390
  */
388
391
  function deriveThinkChatToolLifecycleHeader(snapshot) {
389
392
  if (snapshot.state === "approval-required") return boundedActivityHeader({
390
- label: "Approval needed",
393
+ action: "Approval needed",
391
394
  iconKind: "approval"
392
395
  });
393
396
  const part = lifecycleSnapshotToToolPart(snapshot);
394
397
  const url = activityPartUrl(part);
395
398
  const workspaceDestination = activityPartWorkspaceDestination(part);
396
399
  return boundedActivityHeader({
397
- label: describeThinkChatActivityPart(part) ?? "Used a tool",
400
+ ...describeThinkChatActivityPart(part) ?? described("Used a tool"),
398
401
  iconKind: activityPartIconKind(part),
399
402
  ...url ? { url } : {},
400
403
  ...workspaceDestination ? { workspaceDestination } : {}
@@ -38,7 +38,7 @@ function ThinkExecutionDetail({ className, emptyState = "Select a run to view it
38
38
  defaultOpen: true,
39
39
  children: [/* @__PURE__ */ jsx(ActivityTrigger, {
40
40
  icon: /* @__PURE__ */ jsx(TerminalIcon, {}),
41
- label: "Activity"
41
+ action: "Activity"
42
42
  }), /* @__PURE__ */ jsxs(ActivityContent, { children: [
43
43
  /* @__PURE__ */ jsx(ActivityItem, {
44
44
  icon: /* @__PURE__ */ jsx(ArrowDownIcon, {}),
@@ -54,7 +54,7 @@ function ThinkExecutionDetail({ className, emptyState = "Select a run to view it
54
54
  }) : null,
55
55
  error ? /* @__PURE__ */ jsx(ActivityItemGroup, {
56
56
  icon: /* @__PURE__ */ jsx(XIcon, {}),
57
- label: "Failure",
57
+ action: "Failure",
58
58
  children: /* @__PURE__ */ jsx(ActivityPanel, {
59
59
  label: "Error",
60
60
  text: error,
@@ -64,12 +64,12 @@ function ThinkExecutionDetail({ className, emptyState = "Select a run to view it
64
64
  }) : null,
65
65
  run.metadata ? /* @__PURE__ */ jsx(ActivityItemGroup, {
66
66
  icon: /* @__PURE__ */ jsx(InfoIcon, {}),
67
- label: "Metadata",
67
+ action: "Metadata",
68
68
  children: /* @__PURE__ */ jsx(ActivityOutput, { children: formatJson(run.metadata) })
69
69
  }) : null,
70
70
  /* @__PURE__ */ jsx(ActivityItemGroup, {
71
71
  icon: /* @__PURE__ */ jsx(FileIcon, {}),
72
- label: "Raw record",
72
+ action: "Raw record",
73
73
  children: /* @__PURE__ */ jsx(ActivityOutput, { children: formatJson(run) })
74
74
  })
75
75
  ] })]
@@ -1,3 +1,4 @@
1
+ import { t as capitalize } from "../format-S6uWyoSb.js";
1
2
  //#region src/components/ThinkExecutionRun.ts
2
3
  /** Narrows the direct upstream execution union without wrapping either record. */
3
4
  function isThinkSubmissionRun(run) {
@@ -35,8 +36,7 @@ function getThinkExecutionRunStatusTone(run) {
35
36
  return "neutral";
36
37
  }
37
38
  function formatThinkExecutionStatus(status) {
38
- const words = status.replaceAll(/([a-z])([A-Z])/g, "$1 $2");
39
- return words.charAt(0).toUpperCase() + words.slice(1);
39
+ return capitalize(status.replaceAll(/([a-z])([A-Z])/g, "$1 $2"));
40
40
  }
41
41
  function formatThinkExecutionTimestamp(value, fallback = "Not recorded") {
42
42
  if (value == null) return fallback;
@@ -3,6 +3,7 @@ import { U as RefreshIcon, y as EllipsisIcon } from "../icons-DyOM0EJs.js";
3
3
  import { t as NavigationLink } from "../navigation-link-Cob7KBlM.js";
4
4
  import { Button, buttonVariants } from "./Button.js";
5
5
  import { Tooltip } from "./Tooltip.js";
6
+ import { EmptyState } from "./EmptyState.js";
6
7
  import { Menu } from "./Menu.js";
7
8
  import { AppTopBar } from "./AppTopBar.js";
8
9
  import { formatThinkExecutionRunProgress, getThinkExecutionRunAccessibleName, getThinkExecutionRunId, getThinkExecutionRunStatus, getThinkExecutionRunStatusTone, getThinkExecutionRunSubtitle, getThinkExecutionRunTitle, isSameThinkExecutionRun, isThinkSubmissionRun } from "./ThinkExecutionRun.js";
@@ -188,13 +189,13 @@ function ThinkExecutionWorkbench({ backButton, className, pageSize = executionPa
188
189
  id: runsId,
189
190
  children: "Runs"
190
191
  })
191
- }), runsQuery.isPending && runs.length === 0 ? /* @__PURE__ */ jsx("p", {
192
+ }), runsQuery.isPending && runs.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
192
193
  className: "think-execution-workbench__empty",
193
194
  role: "status",
194
- children: "Loading runs..."
195
- }) : runs.length === 0 ? /* @__PURE__ */ jsx("p", {
195
+ description: "Loading runs..."
196
+ }) : runs.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
196
197
  className: "think-execution-workbench__empty",
197
- children: "No runs yet."
198
+ description: "No runs yet."
198
199
  }) : /* @__PURE__ */ jsx("ul", {
199
200
  className: "think-execution-workbench__list",
200
201
  role: "list",
@@ -1,4 +1,4 @@
1
- import { r as ToggleGroupProps } from "../ToggleGroup-DNd6cDJZ.js";
1
+ import { r as ToggleGroupProps } from "../ToggleGroup-BW1jrhAF.js";
2
2
  import * as React from "react";
3
3
 
4
4
  //#region src/components/ThinkFullAppViewSwitch.d.ts
@@ -1,17 +1,9 @@
1
+ import { formatThinkExecutionShortTimestamp } from "./ThinkExecutionRun.js";
1
2
  //#region src/components/ThinkRoutine.ts
2
3
  function getThinkRoutineNextRun(routine) {
3
4
  if (!routine.schedule) return null;
4
5
  if (!routine.enabled) return "Paused";
5
- return formatThinkExecutionDate(routine.schedule.time * 1e3, "Not scheduled");
6
- }
7
- function formatThinkExecutionDate(value, fallback) {
8
- const date = value instanceof Date ? value : new Date(value);
9
- return Number.isNaN(date.getTime()) ? fallback : date.toLocaleString(void 0, {
10
- month: "short",
11
- day: "numeric",
12
- hour: "numeric",
13
- minute: "2-digit"
14
- });
6
+ return formatThinkExecutionShortTimestamp(routine.schedule.time * 1e3, "Not scheduled");
15
7
  }
16
8
  //#endregion
17
9
  export { getThinkRoutineNextRun };
@@ -36,6 +36,7 @@ function ThinkRoutineBrowser({ className, routines, selectedRoutineId, showOwner
36
36
  /* @__PURE__ */ jsxs("div", {
37
37
  className: "think-routine-library__browser-toolbar",
38
38
  children: [/* @__PURE__ */ jsx(ToggleGroup.Root, {
39
+ segmented: true,
39
40
  className: "think-routine-library__filters",
40
41
  size: "sm",
41
42
  "aria-label": "Filter schedules",
@@ -1,6 +1,7 @@
1
1
  import { t as cx } from "../class-names-BiMDVpUo.js";
2
2
  import { t as NavigationLink } from "../navigation-link-Cob7KBlM.js";
3
3
  import { buttonVariants } from "./Button.js";
4
+ import { EmptyState } from "./EmptyState.js";
4
5
  import { Badge } from "./Badge.js";
5
6
  import { formatThinkExecutionShortTimestamp, getThinkExecutionRunId, getThinkExecutionRunStatus, isThinkSubmissionRun } from "./ThinkExecutionRun.js";
6
7
  import { ThinkExecutionStatus } from "./ThinkExecutionStatus.js";
@@ -65,9 +66,9 @@ function ThinkRoutineRuns({ className, runs, title = "Recent runs", emptyState =
65
66
  })
66
67
  }, `${kind}:${getThinkExecutionRunId(run)}`);
67
68
  })
68
- }) : /* @__PURE__ */ jsx("p", {
69
+ }) : /* @__PURE__ */ jsx(EmptyState, {
69
70
  className: "think-routine-library__empty-runs",
70
- children: emptyState
71
+ description: emptyState
71
72
  })]
72
73
  });
73
74
  }
@@ -2,6 +2,7 @@ import { t as cx } from "../class-names-BiMDVpUo.js";
2
2
  import { K as SpinnerIcon, Q as WarningIcon, V as PlusIcon, _ as DotIcon, t as ArchiveIcon } from "../icons-DyOM0EJs.js";
3
3
  import { t as NavigationLink } from "../navigation-link-Cob7KBlM.js";
4
4
  import { Button, buttonVariants } from "./Button.js";
5
+ import { EmptyState } from "./EmptyState.js";
5
6
  import { SearchField } from "./SearchField.js";
6
7
  import { AlertDialog } from "./AlertDialog.js";
7
8
  import { ScrollArea } from "./ScrollArea.js";
@@ -187,6 +188,7 @@ function ThinkThreadPicker({ threads, renderThreadLink, onCreate, onDelete, clas
187
188
  className: "think-thread-picker__scroll",
188
189
  children: [/* @__PURE__ */ jsx(ScrollArea.Viewport, {
189
190
  className: "think-thread-picker__viewport",
191
+ "data-recipe": "scroll-fade-y",
190
192
  "aria-label": "Thread list",
191
193
  children: visibleThreads.length ? /* @__PURE__ */ jsx("ul", {
192
194
  className: "think-thread-picker__list",
@@ -246,9 +248,9 @@ function ThinkThreadPicker({ threads, renderThreadLink, onCreate, onDelete, clas
246
248
  }) : null]
247
249
  }, thread.id);
248
250
  })
249
- }) : /* @__PURE__ */ jsx("p", {
251
+ }) : /* @__PURE__ */ jsx(EmptyState, {
250
252
  className: "think-thread-picker__empty",
251
- children: threads.length ? "No matching threads." : "No threads yet."
253
+ description: threads.length ? "No matching threads." : "No threads yet."
252
254
  })
253
255
  }), /* @__PURE__ */ jsx(ScrollArea.Scrollbar, {
254
256
  orientation: "vertical",
@@ -1,2 +1,2 @@
1
- import { n as ToggleGroupItemProps, r as ToggleGroupProps, t as ToggleGroup } from "../ToggleGroup-DNd6cDJZ.js";
1
+ import { n as ToggleGroupItemProps, r as ToggleGroupProps, t as ToggleGroup } from "../ToggleGroup-BW1jrhAF.js";
2
2
  export { ToggleGroup, ToggleGroupItemProps, ToggleGroupProps };
@@ -1,10 +1,52 @@
1
1
  import { n as mergeBaseClassName, t as cx } from "../class-names-BiMDVpUo.js";
2
+ import { n as mergeRefs } from "../merge-refs-DF_7w2x_.js";
2
3
  import { toggleVariants } from "./Toggle.js";
3
4
  import * as React from "react";
4
- import { jsx } from "react/jsx-runtime";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
5
6
  import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
6
7
  import { Toggle } from "@base-ui/react/toggle";
7
8
  //#region src/components/ToggleGroup.tsx
9
+ /**
10
+ * Tracks the pressed item's box so the segmented thumb can slide to it instead
11
+ * of appearing under it.
12
+ *
13
+ * The measurement is here because CSS cannot do it. Anchor positioning places
14
+ * the thumb correctly but teleports: `inset: anchor(...)` keeps the same
15
+ * computed value when the anchor changes, and transitions run on computed
16
+ * values. Counting siblings with `:has()` slides, but only while every segment
17
+ * is the same width, which stops being true the moment one label is longer.
18
+ *
19
+ * `offsetLeft` is physical, so the thumb is placed with `left` rather than
20
+ * `inset-inline-start` — a logical property fed a physical number lands on the
21
+ * wrong side in RTL.
22
+ */
23
+ function useSegmentedThumb(enabled) {
24
+ const ref = React.useRef(null);
25
+ React.useLayoutEffect(() => {
26
+ const root = ref.current;
27
+ if (!enabled || !root) return;
28
+ const measure = () => {
29
+ const pressed = root.querySelector("[data-pressed]");
30
+ if (!pressed) return;
31
+ root.style.setProperty("--toggle-group-thumb-start", `${pressed.offsetLeft}px`);
32
+ root.style.setProperty("--toggle-group-thumb-size", `${pressed.offsetWidth}px`);
33
+ };
34
+ measure();
35
+ const attributes = new MutationObserver(measure);
36
+ attributes.observe(root, {
37
+ attributes: true,
38
+ attributeFilter: ["data-pressed"],
39
+ subtree: true
40
+ });
41
+ const resize = new ResizeObserver(measure);
42
+ resize.observe(root);
43
+ return () => {
44
+ attributes.disconnect();
45
+ resize.disconnect();
46
+ };
47
+ }, [enabled]);
48
+ return ref;
49
+ }
8
50
  const ToggleGroupContext = React.createContext({
9
51
  variant: "default",
10
52
  size: "md",
@@ -35,7 +77,9 @@ const ToggleGroupContext = React.createContext({
35
77
  *
36
78
  * @see {@link https://base-ui.com/react/components/toggle-group | Base UI Toggle Group}
37
79
  */
38
- function ToggleGroupRoot({ size = "md", spacing = 0, variant = "default", className, children, style, ref, ...props }) {
80
+ function ToggleGroupRoot({ size = "md", spacing = 0, segmented = false, variant = "default", className, children, style, ref, ...props }) {
81
+ const slidesThumb = segmented && !props.multiple;
82
+ const thumbRef = useSegmentedThumb(slidesThumb);
39
83
  const gap = `${spacing * .25}rem`;
40
84
  const groupStyle = typeof style === "function" ? (state) => ({
41
85
  ...style(state),
@@ -44,7 +88,7 @@ function ToggleGroupRoot({ size = "md", spacing = 0, variant = "default", classN
44
88
  ...style,
45
89
  gap
46
90
  };
47
- const classes = mergeBaseClassName(cx("toggle-group", size !== "md" && `toggle-group--${size}`), className);
91
+ const classes = mergeBaseClassName(cx("toggle-group", size !== "md" && `toggle-group--${size}`, slidesThumb && "toggle-group--segmented"), className);
48
92
  const contextValue = React.useMemo(() => ({
49
93
  variant,
50
94
  size,
@@ -54,8 +98,8 @@ function ToggleGroupRoot({ size = "md", spacing = 0, variant = "default", classN
54
98
  spacing,
55
99
  variant
56
100
  ]);
57
- return /* @__PURE__ */ jsx(ToggleGroup$1, {
58
- ref,
101
+ return /* @__PURE__ */ jsxs(ToggleGroup$1, {
102
+ ref: mergeRefs(ref, thumbRef),
59
103
  "data-size": size,
60
104
  "data-slot": "toggle-group",
61
105
  "data-spacing": spacing,
@@ -63,10 +107,13 @@ function ToggleGroupRoot({ size = "md", spacing = 0, variant = "default", classN
63
107
  className: classes,
64
108
  style: groupStyle,
65
109
  ...props,
66
- children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, {
110
+ children: [slidesThumb ? /* @__PURE__ */ jsx("span", {
111
+ "aria-hidden": "true",
112
+ className: "toggle-group__thumb"
113
+ }) : null, /* @__PURE__ */ jsx(ToggleGroupContext.Provider, {
67
114
  value: contextValue,
68
115
  children
69
- })
116
+ })]
70
117
  });
71
118
  }
72
119
  /**
@@ -55,7 +55,7 @@ function ToolInvocation({ part, defaultOpen }) {
55
55
  return /* @__PURE__ */ jsxs(Activity, {
56
56
  isStreaming: isToolPartWorking(part),
57
57
  defaultOpen,
58
- children: [/* @__PURE__ */ jsx(ActivityTrigger, { label: describeToolPart(part) }), /* @__PURE__ */ jsxs(ActivityContent, { children: [/* @__PURE__ */ jsx(ActivityPanel, {
58
+ children: [/* @__PURE__ */ jsx(ActivityTrigger, { action: describeToolPart(part) }), /* @__PURE__ */ jsxs(ActivityContent, { children: [/* @__PURE__ */ jsx(ActivityPanel, {
59
59
  label: part.title ?? getToolName(part),
60
60
  text: formatToolValue(getToolInput(part)),
61
61
  status: inputStatus,
@@ -3,7 +3,8 @@
3
3
  }
4
4
 
5
5
  .accordion__item {
6
- border-block-end: 1px solid var(--sigvelo-color-neutral-border-muted);
6
+ border-block-end: var(--sigvelo-border-style) var(--sigvelo-border-width)
7
+ var(--sigvelo-color-neutral-border-muted);
7
8
  }
8
9
 
9
10
  .accordion__item:last-child {