@lotics/ui 22.2.0 → 22.3.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.
@@ -174,9 +174,11 @@ that tool's `output-error` part and flows like a normal call — the amber dot m
174
174
  group's header goes amber too), the reason waits in the expanded Error panel; real runs retry and
175
175
  move on, so the feed doesn't dramatize it. A BREAKING error that terminates the whole run lives
176
176
  OUTSIDE `parts` (chat persists it in the message's `errors`; `useAgentRun().error` carries it) —
177
- pass it as the `error` prop and it renders as a terminal danger row under the transcript. Pass
178
- **`onRetry`** alongside it and a Retry action (a secondary `Button`) rides under that row so the
179
- operator can re-fire the run; omit it and the error row renders exactly as before.
177
+ pass it as the `error` prop and it terminates the feed with an error `Callout` a contained,
178
+ tinted panel rather than another step row, because the run did not advance, it stopped, and it
179
+ carries `role="alert"` so a screen reader announces it. Pass **`onRetry`** alongside it and a Retry
180
+ action (a secondary `Button`) sits INSIDE that panel so the operator can re-fire the run; omit it
181
+ and the panel carries the message alone.
180
182
 
181
183
  **In a bounded container, wrap it in `FollowScroll`.** An `AgentRun` streaming inside a dialog,
182
184
  drawer, or fixed-height panel grows BELOW the fold — a plain scroll container doesn't follow. Wrap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "22.2.0",
3
+ "version": "22.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./vite": {
package/src/agent_run.tsx CHANGED
@@ -4,6 +4,7 @@ import { colors } from "./colors";
4
4
  import { Text } from "./text";
5
5
  import { Button } from "./button";
6
6
  import { Icon, type IconName } from "./icon";
7
+ import { Callout, CalloutActions, CalloutText } from "./callout";
7
8
  import { Markdown } from "./markdown";
8
9
  import { JsonPanel, stringifyData } from "./json_panel";
9
10
  import { PressableHighlight } from "./pressable_highlight";
@@ -187,27 +188,44 @@ export function AgentRun(props: AgentRunProps) {
187
188
  />
188
189
  );
189
190
  })}
190
- {/* A run-level BREAKING error terminates the feed with a danger row. It lives
191
- outside `parts` (the stream failed), so the caller passes it explicitly.
192
- A retry action rides UNDER it when the caller can restart the run. */}
191
+ {/* A run-level BREAKING error terminates the feed. It lives outside `parts`
192
+ (the stream failed), so the caller passes it explicitly.
193
+ A CONTAINED surface, not another step row: the run didn't advance, it
194
+ stopped, and a tinted panel says that at a glance where a bare red line
195
+ in the step gutter reads as one more step that happened to fail. It also
196
+ gives the retry a container — free-floating, a full-weight button in a
197
+ column of text reads as a stray control — and `Callout` carries
198
+ `role="alert"`, which the plain row did not.
199
+ FULL WIDTH, unlike `reasoning` / `rowDetail`: those indent to a step's
200
+ text column because they are that step's sub-content. This hangs under
201
+ no step — it is the whole run ending — so indenting it would make a
202
+ run-level failure read as the last step's detail. */}
193
203
  {props.error ? (
194
- <View style={{ gap: 8 }}>
195
- <View style={styles.row}>
196
- <View style={styles.dotCol}>
197
- <Icon name="circle-alert" size={17} color={colors.red[500]} />
198
- </View>
199
- <View style={styles.rowBody}>
200
- <Text size="sm" color="danger">
201
- {props.error}
202
- </Text>
203
- </View>
204
- </View>
204
+ <Callout tone="error">
205
+ <CalloutText>{props.error}</CalloutText>
205
206
  {onRetry ? (
206
- <View style={styles.retry}>
207
- <Button title={locale.agentRun.retry} color="secondary" icon="rotate-ccw" alignSelf="flex-start" onPress={onRetry} />
208
- </View>
207
+ <CalloutActions>
208
+ {/* PRIMARY, deliberately. `Callout` is a calm panel by design (a
209
+ 50 tint behind a soft 100 border), so a filled button inside it
210
+ is not emphasis twice — it is the one action on a quiet
211
+ surface. Every unfilled variant loses here: default and muted
212
+ hover to zinc-100, a grey wash over the red tint, and
213
+ danger-secondary puts red ink on a red-50 ground, the weakest
214
+ contrast of the options. The run just died; the only way out
215
+ should be the most legible thing in the panel.
216
+ Still a Button, never a link: a link navigates, this re-fires a
217
+ run, and that distinction is what a screen reader and the
218
+ keyboard both rely on. */}
219
+ <Button
220
+ title={locale.agentRun.retry}
221
+ color="primary"
222
+ icon="rotate-ccw"
223
+ alignSelf="flex-start"
224
+ onPress={onRetry}
225
+ />
226
+ </CalloutActions>
209
227
  ) : null}
210
- </View>
228
+ </Callout>
211
229
  ) : null}
212
230
  </View>
213
231
  );
@@ -486,7 +504,4 @@ const styles = StyleSheet.create({
486
504
  reasoning: { paddingLeft: 28, paddingBottom: 4 },
487
505
  // A step's expanded I/O panels — same left edge as the reasoning body.
488
506
  rowDetail: { paddingLeft: 28, paddingBottom: 6, paddingTop: 2 },
489
- // The retry action under the terminal error — aligned to the message body
490
- // (past the dot column + its gap), so it reads as a response to that row.
491
- retry: { paddingLeft: 28 },
492
507
  });