@mcp-b/react-components 0.20.5 → 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 (116) 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/ModelSelector.d.ts +10 -4
  29. package/dist/components/ModelSelector.js +2 -2
  30. package/dist/components/PricingBillingToggle.js +1 -0
  31. package/dist/components/PromptInput.js +3 -2
  32. package/dist/components/Reasoning.js +1 -1
  33. package/dist/components/StatusPill.d.ts +43 -0
  34. package/dist/components/StatusPill.js +35 -0
  35. package/dist/components/ThinkChat.d.ts +9 -3
  36. package/dist/components/ThinkChatActivity.d.ts +2 -2
  37. package/dist/components/ThinkChatActivity.js +27 -15
  38. package/dist/components/ThinkChatActivitySummary.d.ts +2 -2
  39. package/dist/components/ThinkChatActivitySummary.js +67 -64
  40. package/dist/components/ThinkExecutionDetail.js +4 -4
  41. package/dist/components/ThinkExecutionWorkbench.js +5 -4
  42. package/dist/components/ThinkFullAppViewSwitch.d.ts +1 -1
  43. package/dist/components/ThinkRoutineBrowser.js +1 -0
  44. package/dist/components/ThinkRoutineRuns.js +3 -2
  45. package/dist/components/ThinkThreadPicker.js +3 -2
  46. package/dist/components/ToggleGroup.d.ts +1 -1
  47. package/dist/components/ToggleGroup.js +54 -7
  48. package/dist/components/ToolInvocation.js +1 -1
  49. package/dist/styles/activity-header.css +41 -10
  50. package/dist/styles/activity.css +26 -4
  51. package/dist/styles/agent-activity-preview-overlay.css +39 -12
  52. package/dist/styles/agent-activity-preview.css +31 -30
  53. package/dist/styles/agent-attention.css +2 -2
  54. package/dist/styles/agent-chat.css +199 -0
  55. package/dist/styles/agent-connection.css +8 -8
  56. package/dist/styles/alert-dialog.css +5 -2
  57. package/dist/styles/app-top-bar.css +4 -4
  58. package/dist/styles/approval.css +1 -7
  59. package/dist/styles/artifact.css +4 -4
  60. package/dist/styles/base-ui-motion.css +1 -1
  61. package/dist/styles/base.css +95 -3
  62. package/dist/styles/browser-control-overlay.css +16 -7
  63. package/dist/styles/browser-live-view.css +5 -38
  64. package/dist/styles/browser-replay.css +2 -6
  65. package/dist/styles/browser-run-panel.css +4 -18
  66. package/dist/styles/button.css +3 -1
  67. package/dist/styles/card.css +4 -4
  68. package/dist/styles/chain-of-thought.css +0 -1
  69. package/dist/styles/chatgpt-sign-in-screen.css +7 -5
  70. package/dist/styles/checkbox-group.css +3 -3
  71. package/dist/styles/code-block.css +4 -4
  72. package/dist/styles/context-menu.css +1 -1
  73. package/dist/styles/conversation.css +7 -42
  74. package/dist/styles/description-list.css +2 -2
  75. package/dist/styles/dialog.css +17 -2
  76. package/dist/styles/empty-state.css +48 -0
  77. package/dist/styles/environment-variables.css +4 -4
  78. package/dist/styles/field.css +2 -2
  79. package/dist/styles/index.css +3 -0
  80. package/dist/styles/inline-citation.css +1 -1
  81. package/dist/styles/kbd.css +40 -0
  82. package/dist/styles/media-preview-card.css +5 -3
  83. package/dist/styles/message-scroller.css +4 -4
  84. package/dist/styles/message.css +4 -6
  85. package/dist/styles/model-selector.css +5 -8
  86. package/dist/styles/popover.css +1 -1
  87. package/dist/styles/preview-card.css +1 -1
  88. package/dist/styles/prompt-input.css +18 -40
  89. package/dist/styles/queue.css +2 -2
  90. package/dist/styles/recipes.css +68 -9
  91. package/dist/styles/resizable-side-panel.css +1 -1
  92. package/dist/styles/responsive-sidebar-shell.css +5 -6
  93. package/dist/styles/scrollbars.css +32 -3
  94. package/dist/styles/sources.css +1 -1
  95. package/dist/styles/status-pill.css +39 -0
  96. package/dist/styles/stepper.css +2 -2
  97. package/dist/styles/streamdown.css +13 -12
  98. package/dist/styles/subagent-run.css +3 -3
  99. package/dist/styles/switch.css +1 -3
  100. package/dist/styles/tabs.css +1 -1
  101. package/dist/styles/task.css +1 -1
  102. package/dist/styles/terminal.css +2 -2
  103. package/dist/styles/test-results.css +1 -2
  104. package/dist/styles/textarea.css +1 -1
  105. package/dist/styles/theme-mode-control.css +1 -1
  106. package/dist/styles/think-execution-status.css +4 -23
  107. package/dist/styles/think-execution-workbench.css +2 -3
  108. package/dist/styles/think-full-app.css +2 -2
  109. package/dist/styles/think-routine-library.css +14 -16
  110. package/dist/styles/think-thread-picker.css +8 -25
  111. package/dist/styles/toast.css +2 -2
  112. package/dist/styles/toggle-group.css +76 -3
  113. package/dist/styles/tooltip.css +1 -2
  114. package/dist/styles/utilities.css +73 -0
  115. package/dist/styles/workspace-explorer.css +4 -6
  116. package/package.json +2 -2
@@ -3,12 +3,22 @@ import { displayUrl, getToolDisplayName, isBrowserToolPart, isExecuteToolPart, i
3
3
  import { t as summarizeToolInput } from "../summarize-tool-input-CqTcGRYy.js";
4
4
  import { getToolName, isReasoningUIPart, isToolUIPart } from "ai";
5
5
  //#region src/components/ThinkChatActivitySummary.ts
6
+ function described(action, detail) {
7
+ return detail ? {
8
+ action,
9
+ detail
10
+ } : { action };
11
+ }
6
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
+ }
7
17
  function boundedActivityHeader(header) {
8
- if (header.label.length <= ACTIVITY_HEADER_LABEL_MAX_LENGTH) return header;
9
18
  return {
10
19
  ...header,
11
- label: `${header.label.slice(0, ACTIVITY_HEADER_LABEL_MAX_LENGTH - 1).trimEnd()}…`
20
+ action: bounded(header.action),
21
+ ...header.detail ? { detail: bounded(header.detail) } : {}
12
22
  };
13
23
  }
14
24
  function getToolInput(part) {
@@ -100,9 +110,7 @@ function genericToolPresentation(part, hasUrl) {
100
110
  }
101
111
  function describeGenericToolPart(part) {
102
112
  const labels = genericToolPresentation(part, activityPartUrl(part) !== null);
103
- const label = isPartFailed(part) ? labels.failed : isPartWorking(part) ? labels.working : labels.done;
104
- const detail = summarizeToolInput(getToolInput(part));
105
- return detail ? `${label} · ${detail}` : label;
113
+ return described(isPartFailed(part) ? labels.failed : isPartWorking(part) ? labels.working : labels.done, summarizeToolInput(getToolInput(part)));
106
114
  }
107
115
  function activityPartUrl(part) {
108
116
  if (isFetchToolPart(part)) return firstHttpUrl(part.input?.url ?? part.input?.path);
@@ -151,7 +159,7 @@ function activityPartWorkspaceDestination(part) {
151
159
  }
152
160
  }
153
161
  /**
154
- * 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",
155
163
  * "Fetched developers.cloudflare.com/agents/"); `null` for boundaries that
156
164
  * aren't steps (step-start). Exported so a custom `renderPart` can reuse the
157
165
  * built-in labels for tools it doesn't override.
@@ -159,12 +167,12 @@ function activityPartWorkspaceDestination(part) {
159
167
  function describeThinkChatActivityPart(part, toolRenderers) {
160
168
  if (isToolUIPart(part)) {
161
169
  const custom = toolRenderers?.[getToolName(part)]?.describe?.(part);
162
- if (custom !== void 0) return custom;
170
+ if (custom !== void 0) return custom ?? null;
163
171
  }
164
172
  if (isReasoningUIPart(part)) {
165
173
  const snippet = summarizeReasoningTitle(part.text);
166
- const label = part.state === "streaming" ? "Thinking…" : "Thought";
167
- return snippet ?? label;
174
+ const action = part.state === "streaming" ? "Thinking…" : "Thought";
175
+ return described(snippet ?? action);
168
176
  }
169
177
  if (isWorkspaceToolPart(part)) return describeWorkspaceToolPart(part);
170
178
  if (isSkillToolPart(part)) return describeSkillToolPart(part);
@@ -183,88 +191,83 @@ function describeWorkspaceToolPart(part) {
183
191
  const failed = isPartFailed(part);
184
192
  if (part.type === "tool-bash") {
185
193
  const command = part.input?.script?.trim() ?? "";
186
- if (failed) return command ? `Command failed · ${command}` : "Command failed";
194
+ if (failed) return described("Command failed", command);
187
195
  const verb = working ? "Running" : "Ran";
188
- return command ? `${verb} ${command}` : `${verb} a command`;
196
+ return command ? described(verb, command) : described(`${verb} a command`);
189
197
  }
190
- const label = (() => {
198
+ const { action, detail } = (() => {
191
199
  switch (part.type) {
192
- case "tool-read": return `${working ? "Reading" : "Read"} ${basename(part.input?.path) ?? "a file"}`;
193
- case "tool-write": return `${working ? "Writing" : "Wrote"} ${basename(part.input?.path) ?? "a file"}`;
194
- case "tool-edit": return `${working ? "Editing" : "Edited"} ${basename(part.input?.path) ?? "a file"}`;
195
- 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");
196
204
  case "tool-list": {
197
205
  const path = part.input?.path;
198
- return `${working ? "Listing" : "Listed"} ${path && path !== "/" ? path : "files"}`;
206
+ return described(working ? "Listing" : "Listed", path && path !== "/" ? path : "files");
199
207
  }
200
- case "tool-find": return `${working ? "Searching" : "Searched"} for ${part.input?.pattern || "files"}`;
201
- 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"}`);
202
210
  }
203
211
  })();
204
- return failed ? `${label} · failed` : label;
212
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
205
213
  }
206
214
  function describeSkillToolPart(part) {
207
215
  const working = isPartWorking(part);
208
216
  const failed = isPartFailed(part);
209
- const label = (() => {
217
+ const { action, detail } = (() => {
210
218
  switch (part.type) {
211
219
  case "tool-activate_skill": {
212
220
  const name = part.input?.name;
213
- return `${working ? "Using" : "Used"} ${name ? `the ${name} skill` : "a skill"}`;
221
+ return described(working ? "Using" : "Used", name ? `the ${name} skill` : "a skill");
214
222
  }
215
- case "tool-read_skill_resource": return `${working ? "Reading" : "Read"} ${part.input?.path ?? "a skill resource"}`;
216
- 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");
217
225
  }
218
226
  })();
219
- return failed ? `${label} · failed` : label;
227
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
220
228
  }
221
229
  function describeSessionContextToolPart(part) {
222
230
  const working = isPartWorking(part);
223
231
  const failed = isSessionContextToolFailure(part);
224
- const label = (() => {
232
+ const { action, detail } = (() => {
225
233
  switch (part.type) {
226
- case "tool-set_context": {
227
- const target = part.input?.label ? ` ${part.input.label}` : " context";
228
- return `${working ? "Updating" : "Updated"}${target}`;
229
- }
230
- case "tool-load_context": return `${working ? "Loading" : "Loaded"} ${part.input?.key ?? "context"}`;
231
- case "tool-unload_context": return `${working ? "Unloading" : "Unloaded"} ${part.input?.key ?? "context"}`;
232
- case "tool-search_context": return `${working ? "Searching" : "Searched"} ${part.input?.label ?? "context"}`;
233
- 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");
234
239
  }
235
240
  })();
236
- return failed ? `${label} · failed` : label;
241
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
237
242
  }
238
243
  function describeBrowserToolPart(part) {
239
244
  const working = isPartWorking(part);
240
245
  const failed = isPartFailed(part);
241
246
  if (part.type === "tool-browser_execute") {
242
247
  const output = part.state === "output-available" ? part.output : void 0;
243
- if (failed || output?.status === "error") return "Browser automation failed";
244
- if (output?.status === "paused") return isHumanWaitPause(output) ? "Browser waiting for human" : "Browser waiting for approval";
245
- 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");
246
251
  }
247
252
  const page = displayUrl(part.input?.url) || "a page";
248
- const label = (() => {
253
+ const { action, detail } = (() => {
249
254
  switch (part.type) {
250
- case "tool-browser_extract": return `${working ? "Extracting" : "Extracted"} data from ${page}`;
251
- case "tool-browser_links": return `${working ? "Listing" : "Listed"} links on ${page}`;
252
- case "tool-browser_scrape": return `${working ? "Scraping" : "Scraped"} ${page}`;
253
- 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);
254
259
  }
255
260
  })();
256
- return failed ? `${label} · failed` : label;
261
+ return failed ? described(`${action} ${detail ?? ""}`.trim() + " · failed") : described(action, detail);
257
262
  }
258
263
  function describeExecuteToolPart(part) {
259
264
  const output = part.state === "output-available" ? part.output : void 0;
260
- 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");
261
266
  const explicitSummary = part.input ? Reflect.get(part.input, "summary") : void 0;
262
267
  const summary = typeof explicitSummary === "string" ? summarizeToolInput({ summary: explicitSummary }) : null;
263
- if (summary) return summary;
264
- if (isPartFailed(part) || output?.status === "error") return "Code failed";
265
- const label = isPartWorking(part) ? "Running code" : "Ran code";
266
- const snippet = summarizeToolInput(part.input);
267
- 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));
268
271
  }
269
272
  function summarizeReasoningTitle(text) {
270
273
  const line = text.split("\n").find((candidate) => candidate.trim())?.trim();
@@ -279,9 +282,9 @@ function isHumanWaitPause(output) {
279
282
  function describeFetchToolPart(part) {
280
283
  const target = part.input?.url ?? part.input?.path ?? "";
281
284
  const output = part.state === "output-available" ? part.output : void 0;
282
- 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);
283
286
  const verb = isPartWorking(part) ? "Fetching" : "Fetched";
284
- return target ? `${verb} ${target}` : `${verb} a URL`;
287
+ return target ? described(verb, target) : described(`${verb} a URL`);
285
288
  }
286
289
  function summarizeActivitySteps(parts) {
287
290
  const byTool = /* @__PURE__ */ new Map();
@@ -344,38 +347,38 @@ function summarizeActivitySteps(parts) {
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
  ] })]
@@ -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
@@ -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";
@@ -247,9 +248,9 @@ function ThinkThreadPicker({ threads, renderThreadLink, onCreate, onDelete, clas
247
248
  }) : null]
248
249
  }, thread.id);
249
250
  })
250
- }) : /* @__PURE__ */ jsx("p", {
251
+ }) : /* @__PURE__ */ jsx(EmptyState, {
251
252
  className: "think-thread-picker__empty",
252
- children: threads.length ? "No matching threads." : "No threads yet."
253
+ description: threads.length ? "No matching threads." : "No threads yet."
253
254
  })
254
255
  }), /* @__PURE__ */ jsx(ScrollArea.Scrollbar, {
255
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,
@@ -11,13 +11,44 @@
11
11
  text-align: start;
12
12
  }
13
13
 
14
- /* Basis 0 keeps a long label from inflating its owner; the trailing
15
- affordance stays adjacent until the available width is exhausted. */
16
- .activity__header-label {
17
- flex: 1 1 0;
14
+ /*
15
+ * The action holds its content width and the detail absorbs whatever is left,
16
+ * so the two shrink in the right order: a long path loses its tail while
17
+ * "Reading" stays legible. Basis 0 on the detail is what makes it the flexible
18
+ * one; the trailing affordance stays adjacent until the width is exhausted.
19
+ *
20
+ * With no detail present the action is the only flexible item, so a settled
21
+ * summary sentence still truncates rather than inflating its owner.
22
+ */
23
+ .activity__header-action {
24
+ flex: 0 1 auto;
18
25
  min-width: 0;
19
26
  max-width: max-content;
27
+ overflow: hidden;
20
28
  color: var(--sigvelo-color-text);
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ }
32
+
33
+ /*
34
+ * The target of the action — a path, a command, a query. Muted, because in a
35
+ * column of activity lines the verb is what the reader scans for.
36
+ *
37
+ * Faded rather than ellipsed: this is data, and a trailing "…" reads as part of
38
+ * the value it is truncating.
39
+ *
40
+ * The fade is unconditional because it does not need a condition. Growing to
41
+ * fill the free space means the box is wider than short text, so the gradient
42
+ * lands on empty space and only bites once the text actually reaches the edge.
43
+ */
44
+ .activity__header-detail {
45
+ flex: 1 1 0;
46
+ min-width: 0;
47
+ overflow: hidden;
48
+ color: var(--sigvelo-color-text-muted);
49
+ font-weight: var(--sigvelo-font-weight-normal);
50
+ white-space: nowrap;
51
+ mask-image: linear-gradient(to right, #000 calc(100% - 1rem), transparent 100%);
21
52
  }
22
53
 
23
54
  .activity__item-icon {
@@ -26,8 +57,8 @@
26
57
  color: var(--sigvelo-color-text-muted);
27
58
  }
28
59
 
29
- .activity[data-streaming] .activity__header-label,
30
- .activity__header[data-streaming] .activity__header-label {
60
+ .activity[data-streaming] .activity__header-action,
61
+ .activity__header[data-streaming] .activity__header-action {
31
62
  --sigvelo-shimmer-base: color-mix(in oklab, var(--sigvelo-color-text-muted), transparent 20%);
32
63
  background: linear-gradient(
33
64
  90deg,
@@ -43,8 +74,8 @@
43
74
  }
44
75
 
45
76
  @media (prefers-reduced-motion: reduce) {
46
- .activity[data-streaming] .activity__header-label,
47
- .activity__header[data-streaming] .activity__header-label {
77
+ .activity[data-streaming] .activity__header-action,
78
+ .activity__header[data-streaming] .activity__header-action {
48
79
  animation: none;
49
80
  background: none;
50
81
  color: inherit;
@@ -52,8 +83,8 @@
52
83
  }
53
84
 
54
85
  @media (forced-colors: active) {
55
- .activity[data-streaming] .activity__header-label,
56
- .activity__header[data-streaming] .activity__header-label {
86
+ .activity[data-streaming] .activity__header-action,
87
+ .activity__header[data-streaming] .activity__header-action {
57
88
  animation: none;
58
89
  background: none;
59
90
  color: inherit;
@@ -91,7 +91,7 @@
91
91
  gap: var(--sigvelo-spacing-1-5);
92
92
  font-size: var(--sigvelo-text-compact);
93
93
  color: var(--sigvelo-color-text);
94
- line-height: var(--sigvelo-leading-body);
94
+ line-height: var(--sigvelo-leading-relaxed);
95
95
  }
96
96
 
97
97
  /* Second collapse level: the trigger reads as a plain item row; the panel is
@@ -118,7 +118,7 @@
118
118
  font-size: var(--sigvelo-text-compact);
119
119
  font-weight: inherit;
120
120
  color: var(--sigvelo-color-text);
121
- line-height: var(--sigvelo-leading-body);
121
+ line-height: var(--sigvelo-leading-relaxed);
122
122
  text-align: start;
123
123
  cursor: pointer;
124
124
  transition: background-color var(--sigvelo-motion-duration-interaction)
@@ -164,7 +164,7 @@
164
164
  .activity__group-text {
165
165
  margin: 0;
166
166
  font-size: var(--sigvelo-text-compact);
167
- line-height: var(--sigvelo-leading-body);
167
+ line-height: var(--sigvelo-leading-relaxed);
168
168
  color: var(--sigvelo-color-text-muted);
169
169
  white-space: pre-wrap;
170
170
  overflow-wrap: anywhere;
@@ -200,7 +200,7 @@
200
200
  margin: 0;
201
201
  font-family: var(--sigvelo-font-mono);
202
202
  font-size: var(--sigvelo-text-xs);
203
- line-height: var(--sigvelo-leading-body);
203
+ line-height: var(--sigvelo-leading-relaxed);
204
204
  color: var(--sigvelo-color-text);
205
205
  white-space: pre-wrap;
206
206
  overflow-wrap: anywhere;
@@ -323,6 +323,28 @@
323
323
  overflow-wrap: anywhere;
324
324
  }
325
325
 
326
+ /*
327
+ * The target of a row's action. Muted and shrink-first, so a column of rows
328
+ * reads down its verbs — "Read / Edited / Ran" — with the paths and commands
329
+ * present but out of the way.
330
+ */
331
+ .activity__item-detail {
332
+ flex: 1 1 0;
333
+ min-inline-size: 0;
334
+ overflow: hidden;
335
+ color: var(--sigvelo-color-text-muted);
336
+ white-space: nowrap;
337
+ mask-image: linear-gradient(to right, #000 calc(100% - 1rem), transparent 100%);
338
+ }
339
+
340
+ /* The action keeps its content width once a detail is beside it to shrink. */
341
+ .activity__item:has(.activity__item-detail) .activity__item-text,
342
+ .activity__group-trigger:has(.activity__item-detail) .activity__item-text {
343
+ flex: 0 1 auto;
344
+ overflow-wrap: normal;
345
+ white-space: nowrap;
346
+ }
347
+
326
348
  @media (hover: none) {
327
349
  .activity__group-icon {
328
350
  opacity: 1;