@mcp-b/react-components 0.31.6 → 0.32.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.
@@ -113,10 +113,14 @@ function getCopyableResponseText(message) {
113
113
  }
114
114
  function AgentChatAssistantActions({ message, responseNumber, showCopy, showRetry, onRegenerate, onCopyStatus }) {
115
115
  const copyText = getCopyableResponseText(message);
116
- if ((!showCopy || !copyText) && !showRetry) return null;
116
+ const hasCopy = showCopy && copyText !== void 0;
117
+ const hasRetry = showRetry && onRegenerate !== void 0;
118
+ const hasActions = hasCopy || hasRetry;
117
119
  return /* @__PURE__ */ jsxs(MessageActions, {
118
120
  "aria-label": `Actions for response ${responseNumber}`,
119
- children: [showCopy && copyText ? /* @__PURE__ */ jsx(CopyButton, {
121
+ "aria-hidden": hasActions ? void 0 : true,
122
+ className: "message__actions--reserved",
123
+ children: [hasCopy ? /* @__PURE__ */ jsx(CopyButton, {
120
124
  className: "message__action",
121
125
  label: `Copy response ${responseNumber}`,
122
126
  tooltip: "Copy response",
@@ -124,7 +128,7 @@ function AgentChatAssistantActions({ message, responseNumber, showCopy, showRetr
124
128
  size: "sm",
125
129
  onCopy: () => onCopyStatus(`Response ${responseNumber} copied.`),
126
130
  onError: () => onCopyStatus(`Could not copy response ${responseNumber}. Try again.`)
127
- }) : null, showRetry && onRegenerate ? /* @__PURE__ */ jsx(MessageAction, {
131
+ }) : null, hasRetry ? /* @__PURE__ */ jsx(MessageAction, {
128
132
  label: "Retry",
129
133
  tooltip: "Regenerate response",
130
134
  onClick: () => onRegenerate(message.id),
@@ -151,7 +155,7 @@ function isCompactedPart(message, partIndex) {
151
155
  * referentially stable across updates (AI SDK chat state does); see
152
156
  * "Transcript rendering performance" in docs/think-chat.md.
153
157
  */
154
- function AgentChatTranscriptRowBodyImpl({ message, messageIndex, status, renderPart, renderMessageAfter, editing, canEdit, onEditAndResend, onStartEdit, onCancelEdit, onFinishEdit, editButtonRef, responseNumber, showAssistantActions, showCopy, showRetry, onRegenerate, onCopyStatus }) {
158
+ function AgentChatTranscriptRowBodyImpl({ message, messageIndex, status, renderPart, renderMessageAfter, editing, canEdit, editDisabled, onEditAndResend, onStartEdit, onCancelEdit, onFinishEdit, editButtonRef, responseNumber, showAssistantActions, showCopy, showRetry, onRegenerate, onCopyStatus }) {
155
159
  return /* @__PURE__ */ jsx(Fragment, { children: isCompactionMessage(message) ? /* @__PURE__ */ jsx(Marker, {
156
160
  variant: "separator",
157
161
  children: /* @__PURE__ */ jsx(MarkerContent, { children: "Context compacted" })
@@ -183,8 +187,12 @@ function AgentChatTranscriptRowBodyImpl({ message, messageIndex, status, renderP
183
187
  }),
184
188
  canEdit && getEditableUserText(message) !== void 0 ? /* @__PURE__ */ jsx(MessageActions, {
185
189
  "aria-label": "Message actions",
190
+ "aria-hidden": editDisabled || void 0,
191
+ className: "message__actions--inline",
192
+ "data-unavailable": editDisabled || void 0,
186
193
  children: /* @__PURE__ */ jsx(MessageAction, {
187
194
  ref: editButtonRef,
195
+ disabled: editDisabled,
188
196
  label: "Edit message",
189
197
  tooltip: "Edit and resend",
190
198
  onClick: () => onStartEdit(message.id),
@@ -236,6 +244,16 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
236
244
  const [copyAnnouncement, setCopyAnnouncement] = React.useState("");
237
245
  const [composerDraft, setComposerDraft] = React.useState("");
238
246
  const [composerReferences, setComposerReferences] = React.useState([]);
247
+ const composerInputRef = React.useRef(null);
248
+ const restoreComposerFocusAfterApproval = React.useRef(false);
249
+ const respondToPendingApproval = React.useCallback((response) => {
250
+ restoreComposerFocusAfterApproval.current = true;
251
+ return onApprovalResponse?.(response);
252
+ }, [onApprovalResponse]);
253
+ const handlePendingExecutionSettled = React.useCallback(async () => {
254
+ restoreComposerFocusAfterApproval.current = true;
255
+ await onExecutionSettled?.();
256
+ }, [onExecutionSettled]);
239
257
  const [pendingPrompts, setPendingPrompts] = React.useState([]);
240
258
  const [queueHeld, setQueueHeld] = React.useState(false);
241
259
  const busy = status === "submitted" || status === "streaming";
@@ -425,10 +443,16 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
425
443
  thinkStub
426
444
  ]);
427
445
  const pendingApproval = !permissionModeControl && permissionMode === "bypass" ? void 0 : pendingApprovalItems[0];
446
+ const pendingApprovalKey = pendingApproval ? "part" in pendingApproval ? getApprovalStateKey(pendingApproval.part) : pendingApproval.approval.executionId : void 0;
447
+ React.useLayoutEffect(() => {
448
+ if (pendingApprovalKey || !restoreComposerFocusAfterApproval.current) return;
449
+ restoreComposerFocusAfterApproval.current = false;
450
+ composerInputRef.current?.focus();
451
+ }, [pendingApprovalKey]);
428
452
  const contextUsage = contextWindow ? getContextUsage(messages) : void 0;
429
453
  const newestAssistantIndex = messages.findLastIndex((message) => message.role === "assistant" && !isCompactionMessage(message));
430
454
  const lastAssistantIndex = onRegenerate && status !== "streaming" && status !== "submitted" ? newestAssistantIndex : -1;
431
- const lastUserIndex = onEditAndResend && !disabled && status !== "streaming" && status !== "submitted" ? messages.findLastIndex((message) => message.role === "user") : -1;
455
+ const lastUserIndex = onEditAndResend ? messages.findLastIndex((message) => message.role === "user") : -1;
432
456
  const [editingMessageId, setEditingMessageId] = React.useState();
433
457
  const restoreEditFocus = React.useRef(false);
434
458
  const editButtonRef = React.useRef(null);
@@ -463,6 +487,7 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
463
487
  renderMessageAfter,
464
488
  editing: editingMessageId === message.id,
465
489
  canEdit: messageIndex === lastUserIndex,
490
+ editDisabled: disabled || busy,
466
491
  onEditAndResend,
467
492
  onStartEdit: handleStartEdit,
468
493
  onCancelEdit: handleCancelEdit,
@@ -538,10 +563,11 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
538
563
  part: "part" in pendingApproval ? pendingApproval.part : void 0,
539
564
  approval: "approval" in pendingApproval ? pendingApproval.approval : void 0,
540
565
  count: pendingApprovalItems.length,
541
- onRespond: onApprovalResponse,
566
+ onRespond: onApprovalResponse ? respondToPendingApproval : void 0,
542
567
  thinkStub,
543
- onExecutionSettled,
568
+ onExecutionSettled: handlePendingExecutionSettled,
544
569
  onSkip: () => {
570
+ restoreComposerFocusAfterApproval.current = true;
545
571
  const id = "part" in pendingApproval ? getApprovalStateKey(pendingApproval.part) : pendingApproval.approval.executionId;
546
572
  if (id) updateApprovalSession((current) => ({
547
573
  ...current,
@@ -555,6 +581,7 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
555
581
  references: composerReferences,
556
582
  onRemove: removeComposerReference
557
583
  }), slashCommands || mentionCommands || onCompact ? /* @__PURE__ */ jsx(PromptInputCommandComposer, {
584
+ ref: composerInputRef,
558
585
  "aria-label": "Message",
559
586
  placeholder: composerReferences.length ? "" : placeholder,
560
587
  disabled,
@@ -564,6 +591,7 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
564
591
  onValueChange: setComposerDraft,
565
592
  onCommandSelect: selectComposerCommand
566
593
  }) : /* @__PURE__ */ jsx(PromptInputTextarea, {
594
+ ref: composerInputRef,
567
595
  "aria-label": "Message",
568
596
  placeholder,
569
597
  disabled,
@@ -7,6 +7,7 @@ interface ThinkRoutineBrowserProps<TRoutine extends ThinkRoutine = ThinkRoutine>
7
7
  routines: readonly TRoutine[];
8
8
  /** Routine selected by the current application route. */
9
9
  selectedRoutineId?: string | null;
10
+ /** Whether owner names are displayed and included in search. Defaults to true. */
10
11
  showOwner?: boolean;
11
12
  /** App-authored TanStack Router Link for each durable routine destination. */
12
13
  renderRoutineLink: (routine: TRoutine) => React.ReactElement;
@@ -10,20 +10,20 @@ import { getThinkRoutineNextRun } from "./ThinkRoutine.js";
10
10
  import * as React from "react";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  //#region src/components/agents-sdk/ThinkRoutineBrowser.tsx
13
- function filterRoutines(routines, filter, query) {
13
+ function filterRoutines(routines, filter, query, showOwner) {
14
14
  const normalizedQuery = query.trim().toLocaleLowerCase();
15
15
  return routines.filter((routine) => {
16
16
  if (filter === "active" && !routine.enabled) return false;
17
17
  if (filter === "paused" && routine.enabled) return false;
18
18
  if (!normalizedQuery) return true;
19
- return `${routine.name} ${routine.description ?? ""} ${routine.owner.name}`.toLocaleLowerCase().includes(normalizedQuery);
19
+ return `${routine.name} ${routine.description ?? ""}${showOwner ? ` ${routine.owner.name}` : ""}`.toLocaleLowerCase().includes(normalizedQuery);
20
20
  });
21
21
  }
22
22
  /** Searchable, filterable navigation for an app-owned collection of Think routines. */
23
23
  function ThinkRoutineBrowser({ className, routines, selectedRoutineId, showOwner = true, renderRoutineLink, createRoutineLink, onRoutineLinkClick, ref, ...props }) {
24
24
  const [filter, setFilter] = React.useState("all");
25
25
  const [query, setQuery] = React.useState("");
26
- const visibleRoutines = filterRoutines(routines, filter, query);
26
+ const visibleRoutines = filterRoutines(routines, filter, query, showOwner);
27
27
  return /* @__PURE__ */ jsxs("nav", {
28
28
  ref,
29
29
  className: cx("think-routine-browser", "think-routine-library__browser", className),
@@ -26,7 +26,7 @@ interface ThinkRoutineLibraryProps<TRoutine extends ThinkRoutine = ThinkRoutine>
26
26
  renderEditRoutineLink?: (routine: TRoutine) => React.ReactElement;
27
27
  /** App-authored TanStack Router Link for recent execution destinations. */
28
28
  renderRunLink?: (routine: TRoutine, run: ThinkRoutineRunOf<TRoutine>) => React.ReactElement;
29
- /** Whether to show routine owners in the browser and detail. Defaults to true. */
29
+ /** Whether owner names appear in the browser, search, and detail. Defaults to true. */
30
30
  showOwner?: boolean;
31
31
  /** Host-owned catalog error shown above the library without replacing loaded routines. */
32
32
  error?: React.ReactNode;
@@ -132,6 +132,7 @@ function ApprovalDecision({ className, onDecision, onSkip, subject = "this actio
132
132
  children: [
133
133
  /* @__PURE__ */ jsxs(Button, {
134
134
  ref: approveRef,
135
+ autoFocus: true,
135
136
  type: "button",
136
137
  size: "sm",
137
138
  variant: "ghost",
@@ -200,11 +200,11 @@ function MessageBranchContent({ className, children, ref, ...props }) {
200
200
  ref,
201
201
  className: cx("message__branch-content", className),
202
202
  ...props,
203
- children: items.map((item, index) => /* @__PURE__ */ jsx("div", {
203
+ children: React.Children.map(items, (item, index) => /* @__PURE__ */ jsx("div", {
204
204
  className: "message__branch-panel",
205
205
  hidden: index !== safeIndex,
206
206
  children: item
207
- }, React.isValidElement(item) && item.key != null ? item.key : index))
207
+ }))
208
208
  });
209
209
  }
210
210
  /**
@@ -159,8 +159,8 @@ declare function PlanSummaryContent({
159
159
  ...props
160
160
  }: PlanSummaryContentProps): React.JSX.Element;
161
161
  interface PlanSummaryStep {
162
- /** Stable optional identity; the list index is used when omitted. */
163
- id?: React.Key;
162
+ /** Stable identity retained when the host replaces or reorders the plan. */
163
+ id: React.Key;
164
164
  step: React.ReactNode;
165
165
  status: PlanStepStatus;
166
166
  }
@@ -198,10 +198,10 @@ function PlanSummaryList({ steps, side }) {
198
198
  total: steps.length,
199
199
  children: [/* @__PURE__ */ jsx(PlanSummaryTrigger, {}), /* @__PURE__ */ jsx(PlanSummaryContent, {
200
200
  side,
201
- children: /* @__PURE__ */ jsx(Plan, { children: /* @__PURE__ */ jsx(PlanContent, { children: steps.map((item, index) => /* @__PURE__ */ jsxs(PlanStep, {
201
+ children: /* @__PURE__ */ jsx(Plan, { children: /* @__PURE__ */ jsx(PlanContent, { children: steps.map((item) => /* @__PURE__ */ jsxs(PlanStep, {
202
202
  status: item.status,
203
203
  children: [/* @__PURE__ */ jsx(PlanStepIndicator, {}), /* @__PURE__ */ jsx(PlanStepTitle, { children: item.step })]
204
- }, item.id ?? index)) }) })
204
+ }, item.id)) }) })
205
205
  })]
206
206
  });
207
207
  }
@@ -421,6 +421,7 @@ function PromptInputTextarea({ className, placeholder = "How can I help?", rows
421
421
  ref: innerRef,
422
422
  "data-prompt-input-textarea": "true",
423
423
  className: cx("prompt-input__textarea", className),
424
+ "aria-label": "Message",
424
425
  name: "message",
425
426
  placeholder,
426
427
  rows,
@@ -12,7 +12,7 @@ declare function useReasoning(): ReasoningContextValue;
12
12
  interface ReasoningProps extends Omit<React.ComponentProps<typeof Collapsible.Root>, "onOpenChange" | "unstyled"> {
13
13
  /**
14
14
  * Whether the model is currently generating reasoning. When true the
15
- * component auto-opens; it auto-closes once streaming ends.
15
+ * component auto-opens.
16
16
  */
17
17
  isStreaming?: boolean;
18
18
  /**
@@ -25,7 +25,8 @@ interface ReasoningProps extends Omit<React.ComponentProps<typeof Collapsible.Ro
25
25
  }
26
26
  /**
27
27
  * A collapsible "thinking" block for showing model reasoning (e.g. DeepSeek
28
- * R1, OpenAI o-series). Auto-expands while streaming and collapses when done.
28
+ * R1, OpenAI o-series). Auto-expands while streaming and stays open when done
29
+ * so settling a response does not shift the transcript.
29
30
  * Adapted from Vercel AI Elements and composed from the design system's
30
31
  * existing Collapsible and Activity presentation primitives.
31
32
  *
@@ -7,7 +7,6 @@ import * as React from "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  //#region src/components/ai-sdk/Reasoning.tsx
9
9
  const ReasoningContext = React.createContext(null);
10
- const AUTO_CLOSE_DELAY_MS = 1e3;
11
10
  const MS_IN_S = 1e3;
12
11
  function useReasoning() {
13
12
  const ctx = React.useContext(ReasoningContext);
@@ -16,7 +15,8 @@ function useReasoning() {
16
15
  }
17
16
  /**
18
17
  * A collapsible "thinking" block for showing model reasoning (e.g. DeepSeek
19
- * R1, OpenAI o-series). Auto-expands while streaming and collapses when done.
18
+ * R1, OpenAI o-series). Auto-expands while streaming and stays open when done
19
+ * so settling a response does not shift the transcript.
20
20
  * Adapted from Vercel AI Elements and composed from the design system's
21
21
  * existing Collapsible and Activity presentation primitives.
22
22
  *
@@ -33,9 +33,7 @@ function useReasoning() {
33
33
  function Reasoning({ className, isStreaming = false, duration, open, defaultOpen, onOpenChange, children, ref, ...props }) {
34
34
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? isStreaming);
35
35
  const [internalDuration, setInternalDuration] = React.useState(duration);
36
- const previousStreamingRef = React.useRef(isStreaming);
37
36
  const startTimeRef = React.useRef(isStreaming ? Date.now() : null);
38
- const hasEverStreamedRef = React.useRef(isStreaming);
39
37
  const isControlled = open !== void 0;
40
38
  const currentOpen = isControlled ? open : internalOpen;
41
39
  const preventAutoOpen = defaultOpen === false;
@@ -51,7 +49,6 @@ function Reasoning({ className, isStreaming = false, duration, open, defaultOpen
51
49
  ]);
52
50
  React.useEffect(() => {
53
51
  if (isStreaming) {
54
- hasEverStreamedRef.current = true;
55
52
  if (startTimeRef.current === null) startTimeRef.current = Date.now();
56
53
  return;
57
54
  }
@@ -61,16 +58,7 @@ function Reasoning({ className, isStreaming = false, duration, open, defaultOpen
61
58
  }
62
59
  }, [duration, isStreaming]);
63
60
  React.useEffect(() => {
64
- const wasStreaming = previousStreamingRef.current;
65
61
  if (isStreaming && !currentOpen && !preventAutoOpen) setOpenState(true);
66
- if (!isStreaming && wasStreaming && hasEverStreamedRef.current && currentOpen) {
67
- const closeTimer = window.setTimeout(() => {
68
- setOpenState(false);
69
- }, AUTO_CLOSE_DELAY_MS);
70
- previousStreamingRef.current = isStreaming;
71
- return () => window.clearTimeout(closeTimer);
72
- }
73
- previousStreamingRef.current = isStreaming;
74
62
  }, [
75
63
  currentOpen,
76
64
  isStreaming,
@@ -122,7 +110,10 @@ function ReasoningTrigger({ className, children, getThinkingMessage = defaultGet
122
110
  "data-slot": "reasoning-trigger",
123
111
  className: cx("reasoning__trigger", className),
124
112
  "data-streaming": isStreaming ? "" : void 0,
125
- icon: children == null && isStreaming ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : void 0,
113
+ icon: children == null ? /* @__PURE__ */ jsx("span", {
114
+ className: "reasoning__status-icon",
115
+ children: isStreaming ? /* @__PURE__ */ jsx(SpinnerIcon, {}) : null
116
+ }) : void 0,
126
117
  action: children ?? getThinkingMessage(isStreaming, duration),
127
118
  ...props
128
119
  });
@@ -58,6 +58,8 @@
58
58
 
59
59
  .activity__trigger-icon {
60
60
  display: inline-flex;
61
+ flex: none;
62
+ margin-inline-start: auto;
61
63
  color: var(--sigvelo-color-text-muted);
62
64
  transition: rotate var(--sigvelo-motion-duration-disclosure) var(--sigvelo-motion-ease-enter);
63
65
  }
@@ -65,12 +65,21 @@
65
65
  }
66
66
 
67
67
  .agent-chat-start-screen__content {
68
+ --agent-chat-start-screen-padding-block: clamp(
69
+ var(--sigvelo-spacing-5),
70
+ 8vb,
71
+ var(--sigvelo-spacing-12)
72
+ );
73
+
68
74
  display: grid;
69
75
  grid-template-rows: minmax(0, 1fr) auto minmax(0, 1fr);
70
76
  inline-size: min(100%, var(--agent-chat-content-max-inline-size));
71
77
  min-block-size: 100%;
72
78
  margin-inline: auto;
73
- padding: clamp(var(--sigvelo-spacing-5), 8vb, var(--sigvelo-spacing-12)) var(--sigvelo-spacing-4);
79
+ padding-block: max(var(--agent-chat-start-screen-padding-block), env(safe-area-inset-top, 0px))
80
+ max(var(--agent-chat-start-screen-padding-block), env(safe-area-inset-bottom, 0px));
81
+ padding-inline: max(var(--sigvelo-spacing-4), env(safe-area-inset-left, 0px))
82
+ max(var(--sigvelo-spacing-4), env(safe-area-inset-right, 0px));
74
83
  }
75
84
 
76
85
  .agent-chat-start-screen__primary {
@@ -323,7 +332,9 @@
323
332
  position: absolute;
324
333
  inset-inline: 0;
325
334
  inset-block-end: 0;
326
- padding: 0 var(--sigvelo-spacing-6) var(--sigvelo-spacing-6);
335
+ padding-block: 0 max(var(--sigvelo-spacing-6), env(safe-area-inset-bottom, 0px));
336
+ padding-inline: max(var(--sigvelo-spacing-6), env(safe-area-inset-left, 0px))
337
+ max(var(--sigvelo-spacing-6), env(safe-area-inset-right, 0px));
327
338
  }
328
339
 
329
340
  /* Solid behind the composer itself, dissolving upward so passing transcript
@@ -415,6 +426,12 @@
415
426
  }
416
427
 
417
428
  @media (any-pointer: coarse) {
429
+ .agent-chat-start-screen__composer-tray > .button,
430
+ .agent-chat-starter__dismiss.button {
431
+ min-inline-size: var(--sigvelo-control-height-xl);
432
+ min-block-size: var(--sigvelo-control-height-xl);
433
+ }
434
+
418
435
  .agent-chat-starter__dismiss {
419
436
  opacity: 1;
420
437
  }
@@ -487,15 +504,17 @@
487
504
  only where there is genuinely no room for it. */
488
505
  @container agent-chat (width <= 36rem) {
489
506
  .agent-chat-start-screen__content {
490
- padding-block: var(--sigvelo-spacing-5);
507
+ --agent-chat-start-screen-padding-block: var(--sigvelo-spacing-5);
491
508
  }
492
509
 
493
510
  .agent-chat-start-screen__heading {
494
511
  font-size: var(--sigvelo-text-2xl);
495
512
  }
496
513
 
497
- .agent-chat-starter__action {
498
- font-size: var(--sigvelo-text-sm);
514
+ .agent-chat-starter__label {
515
+ overflow: hidden;
516
+ text-overflow: ellipsis;
517
+ white-space: nowrap;
499
518
  }
500
519
  }
501
520
 
@@ -509,8 +528,9 @@
509
528
 
510
529
  @media (max-height: 40rem) {
511
530
  .agent-chat-start-screen__content {
531
+ --agent-chat-start-screen-padding-block: var(--sigvelo-spacing-5);
532
+
512
533
  grid-template-rows: auto auto;
513
- padding-block: var(--sigvelo-spacing-5);
514
534
  }
515
535
 
516
536
  .agent-chat-start-screen__primary {
@@ -164,6 +164,24 @@ a {
164
164
  touch-action: manipulation;
165
165
  }
166
166
 
167
+ button,
168
+ input,
169
+ optgroup,
170
+ select,
171
+ textarea {
172
+ font: inherit;
173
+ color: inherit;
174
+ }
175
+
176
+ :where(img, svg, video, canvas, audio, iframe, embed, object) {
177
+ display: block;
178
+ }
179
+
180
+ :where(img, video) {
181
+ max-inline-size: 100%;
182
+ block-size: auto;
183
+ }
184
+
167
185
  ::selection {
168
186
  background-color: var(--sigvelo-color-selection-bg);
169
187
  color: var(--sigvelo-color-selection-text);
@@ -211,6 +211,14 @@
211
211
  }
212
212
  }
213
213
 
214
+ @media (any-pointer: coarse) {
215
+ .codex-model-selector__trigger {
216
+ min-inline-size: var(--sigvelo-control-height-xl);
217
+ min-block-size: var(--sigvelo-control-height-xl);
218
+ max-inline-size: calc(100vi - var(--sigvelo-spacing-8));
219
+ }
220
+ }
221
+
214
222
  @media (forced-colors: active) {
215
223
  .codex-model-selector__summary-effort--ultra {
216
224
  color: LinkText;
@@ -122,8 +122,34 @@
122
122
  color: var(--sigvelo-color-text-muted);
123
123
  }
124
124
 
125
- .message[data-from="user"] .message__actions {
126
- align-self: flex-end;
125
+ .message__actions--reserved {
126
+ min-block-size: var(--sigvelo-control-height-sm);
127
+ }
128
+
129
+ .message[data-from="user"]:has(> .message__actions--inline) {
130
+ flex-direction: row-reverse;
131
+ align-items: center;
132
+ }
133
+
134
+ .message[data-from="user"] > .message__actions--inline {
135
+ align-self: center;
136
+ }
137
+
138
+ .message[data-from="user"] > .message__actions--inline[data-unavailable] {
139
+ visibility: hidden;
140
+ }
141
+
142
+ @media (hover: hover) and (pointer: fine) {
143
+ .message[data-from="user"] > .message__actions--inline {
144
+ opacity: 0;
145
+ pointer-events: none;
146
+ }
147
+
148
+ .message[data-from="user"]:hover > .message__actions--inline,
149
+ .message[data-from="user"]:focus-within > .message__actions--inline {
150
+ opacity: 1;
151
+ pointer-events: auto;
152
+ }
127
153
  }
128
154
 
129
155
  .message__action {
@@ -135,6 +161,10 @@
135
161
  }
136
162
 
137
163
  @media (any-pointer: coarse) {
164
+ .message__actions--reserved {
165
+ min-block-size: var(--sigvelo-control-height-xl);
166
+ }
167
+
138
168
  .message__action {
139
169
  --sigvelo-recipe-button-height: var(--sigvelo-control-height-xl);
140
170
  }
@@ -17,7 +17,7 @@
17
17
 
18
18
  .pricing-page__title {
19
19
  font-family: inherit;
20
- font-size: 2.5rem;
20
+ font-size: var(--sigvelo-text-4xl);
21
21
  font-weight: var(--sigvelo-font-weight-bold);
22
22
  line-height: var(--sigvelo-leading-tight);
23
23
  color: var(--sigvelo-color-text);
@@ -125,7 +125,7 @@
125
125
 
126
126
  .pricing-page__price-amount {
127
127
  font-family: inherit;
128
- font-size: 2.5rem;
128
+ font-size: var(--sigvelo-text-4xl);
129
129
  font-weight: var(--sigvelo-font-weight-bold);
130
130
  font-variant-numeric: tabular-nums;
131
131
  color: var(--sigvelo-color-text);
@@ -274,7 +274,7 @@
274
274
 
275
275
  .pricing-page__calculator-amount {
276
276
  font-family: inherit;
277
- font-size: 2rem;
277
+ font-size: var(--sigvelo-text-3xl);
278
278
  font-weight: var(--sigvelo-font-weight-bold);
279
279
  font-variant-numeric: tabular-nums;
280
280
  color: var(--sigvelo-color-text);
@@ -369,7 +369,7 @@
369
369
 
370
370
  @container (max-width: 768px) {
371
371
  .pricing-page__title {
372
- font-size: 1.75rem;
372
+ font-size: var(--sigvelo-text-3xl);
373
373
  }
374
374
 
375
375
  .pricing-page__subtitle {
@@ -381,6 +381,6 @@
381
381
  }
382
382
 
383
383
  .pricing-page__price-amount {
384
- font-size: 2rem;
384
+ font-size: var(--sigvelo-text-3xl);
385
385
  }
386
386
  }
@@ -373,18 +373,18 @@
373
373
  }
374
374
 
375
375
  @container prompt-input (inline-size <= 36rem) {
376
- .prompt-input__textarea {
377
- font-size: var(--sigvelo-text-base);
378
- line-height: var(--sigvelo-text-base-leading);
379
- }
380
-
381
376
  .prompt-input__footer {
382
377
  gap: var(--sigvelo-spacing-1);
378
+ flex-wrap: wrap;
379
+ }
380
+
381
+ .prompt-input__footer-end {
382
+ margin-inline-start: auto;
383
383
  }
384
384
 
385
385
  .prompt-input__tools {
386
386
  min-inline-size: 0;
387
- flex-wrap: nowrap;
387
+ flex-wrap: wrap;
388
388
  }
389
389
 
390
390
  .prompt-input__button[data-responsive-label] {
@@ -664,7 +664,8 @@
664
664
 
665
665
  .prompt-input__button,
666
666
  .prompt-input__footer .button,
667
- .prompt-input__attachment-remove {
667
+ .prompt-input__attachment-remove,
668
+ .prompt-input__reference.button {
668
669
  --sigvelo-recipe-button-height: var(--sigvelo-control-height-xl);
669
670
  }
670
671
 
@@ -676,6 +677,12 @@
676
677
  .prompt-input__permissions-trigger,
677
678
  .prompt-input__context-trigger {
678
679
  min-inline-size: var(--sigvelo-control-height-xl);
680
+ min-block-size: var(--sigvelo-control-height-xl);
681
+ }
682
+
683
+ .prompt-input__reference-remove {
684
+ position: static;
685
+ opacity: 1;
679
686
  }
680
687
 
681
688
  .prompt-input__attachment[data-image] .prompt-input__attachment-remove {
@@ -1,3 +1,9 @@
1
+ .reasoning__status-icon {
2
+ display: inline-flex;
3
+ inline-size: var(--sigvelo-control-icon-size-md);
4
+ block-size: var(--sigvelo-control-icon-size-md);
5
+ }
6
+
1
7
  .reasoning__text {
2
8
  padding-inline-start: var(--sigvelo-spacing-3);
3
9
  border-inline-start: var(--sigvelo-border-style) calc(var(--sigvelo-border-width) * 2)
@@ -289,15 +289,14 @@ body.is-resizing-side-panel .think-full-app__view-scope {
289
289
  .think-full-app .agent-chat__composer {
290
290
  padding-block: var(--sigvelo-spacing-2)
291
291
  max(var(--sigvelo-spacing-2), env(safe-area-inset-bottom, 0px));
292
- padding-inline: max(var(--sigvelo-spacing-2), env(safe-area-inset-left, 0px))
293
- max(var(--sigvelo-spacing-2), env(safe-area-inset-right, 0px));
292
+ padding-inline: max(var(--sigvelo-spacing-3), env(safe-area-inset-left, 0px))
293
+ max(var(--sigvelo-spacing-3), env(safe-area-inset-right, 0px));
294
294
  }
295
295
 
296
296
  .think-full-app .prompt-input {
297
297
  gap: var(--sigvelo-spacing-1);
298
298
  padding-block: var(--sigvelo-spacing-2);
299
299
  padding-inline: var(--sigvelo-spacing-2-5);
300
- border-radius: var(--sigvelo-radius-lg);
301
300
  }
302
301
 
303
302
  .think-full-app .message__content {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-components",
3
- "version": "0.31.6",
3
+ "version": "0.32.0",
4
4
  "description": "MCP-B React components built on Base UI and shared design tokens, including Cloudflare Think chat primitives.",
5
5
  "homepage": "https://design-system.sigvelo.com",
6
6
  "bugs": {
@@ -58,7 +58,7 @@
58
58
  "streamdown": "^2.5.0",
59
59
  "unist-util-visit": "^5.1.0",
60
60
  "yet-another-react-lightbox": "^3.32.1",
61
- "@mcp-b/design-tokens": "0.31.6"
61
+ "@mcp-b/design-tokens": "0.32.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@ai-sdk/react": "^3.0.230",