@lotics/ui 5.7.0 → 5.9.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 (80) hide show
  1. package/AGENTS.md +102 -42
  2. package/examples/tpl_approvals.tsx +10 -10
  3. package/examples/tpl_convert.tsx +11 -23
  4. package/examples/tpl_crm_desk.tsx +3 -3
  5. package/examples/tpl_dieline.tsx +7 -6
  6. package/examples/tpl_directory.tsx +3 -3
  7. package/examples/tpl_dossier.tsx +7 -7
  8. package/examples/tpl_draft.tsx +4 -4
  9. package/examples/tpl_extract.tsx +7 -7
  10. package/examples/tpl_inventory.tsx +3 -3
  11. package/examples/tpl_lookup.tsx +4 -3
  12. package/examples/tpl_match.tsx +1 -1
  13. package/examples/tpl_order.tsx +1 -1
  14. package/examples/tpl_ratedesk.tsx +12 -12
  15. package/examples/tpl_record.tsx +1 -1
  16. package/examples/tpl_report.tsx +3 -3
  17. package/examples/tpl_stock.tsx +3 -3
  18. package/examples/tpl_triage.tsx +1 -1
  19. package/package.json +12 -4
  20. package/src/agent_progress.tsx +14 -12
  21. package/src/agent_run.tsx +253 -66
  22. package/src/back_button.tsx +3 -2
  23. package/src/badge.tsx +2 -1
  24. package/src/button.tsx +11 -32
  25. package/src/button_colors.ts +23 -0
  26. package/src/calendar/calendar_view.tsx +2 -2
  27. package/src/card_select_item.tsx +2 -1
  28. package/src/change_review.tsx +5 -6
  29. package/src/{pill_button.tsx → chip.tsx} +7 -7
  30. package/src/chip_group.tsx +2 -2
  31. package/src/column_filter.tsx +4 -4
  32. package/src/combobox.tsx +2 -9
  33. package/src/composer.tsx +4 -3
  34. package/src/control_surface.ts +15 -15
  35. package/src/date_field.tsx +2 -1
  36. package/src/date_filter.tsx +2 -1
  37. package/src/date_range_filter_field.tsx +2 -1
  38. package/src/dialog.tsx +2 -2
  39. package/src/discrepancy.tsx +1 -1
  40. package/src/drawer.tsx +1 -2
  41. package/src/file_gallery_modal.tsx +220 -133
  42. package/src/file_grid.tsx +184 -0
  43. package/src/file_preview.web.tsx +329 -6
  44. package/src/file_preview_types.ts +40 -0
  45. package/src/file_row.tsx +114 -0
  46. package/src/file_rows.tsx +105 -0
  47. package/src/file_thumbnail.tsx +121 -79
  48. package/src/file_thumbnail_grid.tsx +130 -55
  49. package/src/{filter_pill.tsx → filter_chip.tsx} +8 -8
  50. package/src/finding.tsx +1 -1
  51. package/src/gantt/gantt_view.tsx +2 -2
  52. package/src/icon_button.tsx +49 -14
  53. package/src/image_gallery.tsx +0 -4
  54. package/src/inline_edit.tsx +2 -2
  55. package/src/link_button.tsx +2 -1
  56. package/src/menu_button.tsx +2 -1
  57. package/src/menu_list_item.tsx +2 -1
  58. package/src/number_input.tsx +2 -1
  59. package/src/pdfjs_worker.d.ts +5 -0
  60. package/src/picker.tsx +3 -3
  61. package/src/popover_header.tsx +2 -2
  62. package/src/radio_picker.tsx +2 -1
  63. package/src/range_slider.tsx +1 -1
  64. package/src/review_card.tsx +2 -2
  65. package/src/scan_field.tsx +2 -1
  66. package/src/scored_option.tsx +0 -1
  67. package/src/search_input.tsx +3 -2
  68. package/src/segmented_control.tsx +2 -1
  69. package/src/status_badge.tsx +2 -1
  70. package/src/stepper.tsx +3 -2
  71. package/src/switch_button.tsx +2 -1
  72. package/src/switcher.tsx +3 -2
  73. package/src/tabs.tsx +2 -1
  74. package/src/tag_input.tsx +2 -1
  75. package/src/text_input_field.tsx +2 -1
  76. package/src/time_field.tsx +2 -1
  77. package/src/time_picker.tsx +2 -1
  78. package/src/triage_row.tsx +3 -3
  79. package/src/uploading_thumbnail.tsx +212 -0
  80. package/src/hover_action.tsx +0 -54
@@ -5,7 +5,7 @@ import { Text } from "@lotics/ui/text";
5
5
  import { Button } from "@lotics/ui/button";
6
6
  import { Card, CardBody, CardHeader, CardHeaderTitle } from "@lotics/ui/card";
7
7
  import { FileDropzone } from "@lotics/ui/file_dropzone";
8
- import { AgentRun, type AgentRunStep } from "@lotics/ui/agent_run";
8
+ import { AgentRun, type AgentRunStep, type AgentRunItem } from "@lotics/ui/agent_run";
9
9
  import { ReviewCard } from "@lotics/ui/review_card";
10
10
  import { RecordFields, type RecordField } from "@lotics/ui/record_fields";
11
11
  import { Sources } from "@lotics/ui/sources";
@@ -81,7 +81,8 @@ export function TplExtract() {
81
81
  return () => clearTimeout(t);
82
82
  }, [streaming, revealed]);
83
83
 
84
- const steps: AgentRunStep[] = EXTRACT.slice(0, revealed).map((s, i) => ({
84
+ const items: AgentRunItem[] = EXTRACT.slice(0, revealed).map((s, i) => ({
85
+ type: "step",
85
86
  ...s,
86
87
  status: streaming && revealed < EXTRACT.length && i === revealed - 1 ? "running" : "done",
87
88
  }));
@@ -119,7 +120,7 @@ export function TplExtract() {
119
120
  <Text size="xl" weight="semibold">Capture packing list</Text>
120
121
  <Text size="sm" color="muted">Drop a photo; the agent extracts each line, you review and edit, then save them all to the table.</Text>
121
122
  </View>
122
- {phase !== "intake" ? <Button title="Start over" color="muted" shape="rounded" onPress={reset} /> : null}
123
+ {phase !== "intake" ? <Button title="Start over" color="muted" onPress={reset} /> : null}
123
124
  </View>
124
125
 
125
126
  {phase === "intake" ? (
@@ -133,7 +134,7 @@ export function TplExtract() {
133
134
 
134
135
  {phase === "extracting" ? (
135
136
  <Card>
136
- <AgentRun steps={steps} state={streaming ? "streaming" : "done"} />
137
+ <AgentRun items={items} state={streaming ? "streaming" : "done"} />
137
138
  </Card>
138
139
  ) : null}
139
140
 
@@ -167,12 +168,11 @@ export function TplExtract() {
167
168
  ))}
168
169
  </View>
169
170
  <View style={{ flexDirection: "row", alignItems: "center", gap: 8 }}>
170
- <Button title="Confirm all" color="muted" shape="rounded" onPress={() => setStatuses(Object.fromEntries(RECORDS.map((r) => [r.id, "confirmed" as Status])))} />
171
+ <Button title="Confirm all" color="muted" onPress={() => setStatuses(Object.fromEntries(RECORDS.map((r) => [r.id, "confirmed" as Status])))} />
171
172
  <View style={{ flex: 1 }} />
172
173
  <Button
173
174
  title={`Save ${confirmedCount} ${confirmedCount === 1 ? "line" : "lines"}`}
174
175
  color="primary"
175
- shape="rounded"
176
176
  disabled={confirmedCount === 0}
177
177
  onPress={() => setPhase("done")}
178
178
  />
@@ -183,7 +183,7 @@ export function TplExtract() {
183
183
  {phase === "done" ? (
184
184
  <Card>
185
185
  <CompletionState title={`${confirmedCount} lines saved`} summary="The confirmed lines were written to the shipment table.">
186
- <Button title="Capture another" color="secondary" shape="rounded" onPress={reset} />
186
+ <Button title="Capture another" color="secondary" onPress={reset} />
187
187
  </CompletionState>
188
188
  </Card>
189
189
  ) : null}
@@ -5,7 +5,7 @@ import { colors, solid } from "@lotics/ui/colors";
5
5
  import { Badge } from "@lotics/ui/badge";
6
6
  import { Button } from "@lotics/ui/button";
7
7
  import { ChipGroup } from "@lotics/ui/chip_group";
8
- import { FilterPill } from "@lotics/ui/filter_pill";
8
+ import { FilterChip } from "@lotics/ui/filter_chip";
9
9
  import { RangeSlider, rangeSummary } from "@lotics/ui/range_slider";
10
10
  import { cycleSort, sortBy, type SortState } from "@lotics/ui/sort_header";
11
11
  import { Card } from "@lotics/ui/card";
@@ -259,9 +259,9 @@ export function TplInventory() {
259
259
  value={loc}
260
260
  onValueChange={setLoc}
261
261
  />
262
- <FilterPill label="Stock value" summary={amtSummary} onClear={() => setAmt([0, AMT_MAX])} clearLabel="Clear stock value filter">
262
+ <FilterChip label="Stock value" summary={amtSummary} onClear={() => setAmt([0, AMT_MAX])} clearLabel="Clear stock value filter">
263
263
  <RangeSlider min={0} max={AMT_MAX} step={5_000_000} value={amt} onValueChange={setAmt} format={fmtAmt} accessibilityLabel="Stock value" />
264
- </FilterPill>
264
+ </FilterChip>
265
265
  </View>
266
266
 
267
267
  {/* the stock register: eyebrow band → Divider-separated SKU rows */}
@@ -6,7 +6,7 @@ import { Divider } from "@lotics/ui/divider";
6
6
  import { Callout, CalloutText, CalloutTitle } from "@lotics/ui/callout";
7
7
  import { PressableHighlight } from "@lotics/ui/pressable_highlight";
8
8
  import { Composer } from "@lotics/ui/composer";
9
- import { AgentRun, type AgentRunStep } from "@lotics/ui/agent_run";
9
+ import { AgentRun, type AgentRunStep, type AgentRunItem } from "@lotics/ui/agent_run";
10
10
  import { SpecList, type SpecRow } from "@lotics/ui/spec_list";
11
11
  import { Confidence, type ConfidenceLevel } from "@lotics/ui/confidence";
12
12
  import { Badge } from "@lotics/ui/badge";
@@ -184,7 +184,8 @@ export function TplLookup() {
184
184
  };
185
185
 
186
186
  const streaming = phase === "running" && revealed < SCRIPT.length;
187
- const steps: AgentRunStep[] = SCRIPT.slice(0, revealed).map((s, i) => ({
187
+ const items: AgentRunItem[] = SCRIPT.slice(0, revealed).map((s, i) => ({
188
+ type: "step",
188
189
  ...s,
189
190
  status: streaming && i === revealed - 1 ? "running" : "done",
190
191
  }));
@@ -201,7 +202,7 @@ export function TplLookup() {
201
202
 
202
203
  <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 16 }}>
203
204
  {/* current run */}
204
- <AgentRun steps={steps} state={streaming ? "streaming" : "done"} />
205
+ <AgentRun items={items} state={streaming ? "streaming" : "done"} />
205
206
 
206
207
  <View style={{ gap: 8 }}>
207
208
  <Text size="xs" color="muted" weight="medium">Nearest matches</Text>
@@ -72,7 +72,7 @@ export function TplMatch() {
72
72
  <Text size="sm" color="muted">The agent pairs incoming payments to open invoices — you confirm, reassign, or reject</Text>
73
73
  </View>
74
74
  {tab === "open" && highOpen > 0 ? (
75
- <Button title={`Accept ${highOpen} high-confidence`} color="secondary" shape="rounded" onPress={acceptAllHigh} />
75
+ <Button title={`Accept ${highOpen} high-confidence`} color="secondary" onPress={acceptAllHigh} />
76
76
  ) : null}
77
77
  </View>
78
78
 
@@ -477,7 +477,7 @@ export function TplOrder() {
477
477
  </Screen>
478
478
  </Dialog>
479
479
 
480
- <FileGalleryModal files={files} activeIndex={activeFile} onIndexChange={setActiveFile} captionHint="ESC to close · ←/→ to move" />
480
+ <FileGalleryModal files={files} activeIndex={activeFile} onIndexChange={setActiveFile} />
481
481
  </ScrollView>
482
482
  );
483
483
  }
@@ -13,7 +13,7 @@ import { FileDropzone } from "@lotics/ui/file_dropzone";
13
13
  import { FileThumbnailGrid } from "@lotics/ui/file_thumbnail_grid";
14
14
  import { FileGalleryModal } from "@lotics/ui/file_gallery_modal";
15
15
  import { type DisplayFile } from "@lotics/ui/file_thumbnail";
16
- import { AgentRun, type AgentRunStep } from "@lotics/ui/agent_run";
16
+ import { AgentRun, type AgentRunItem } from "@lotics/ui/agent_run";
17
17
  import { Table, TableRow, TableCell, type TableColumn } from "@lotics/ui/table";
18
18
  import { InlineNumberInput } from "@lotics/ui/inline_number_input";
19
19
  import { CompletionState } from "@lotics/ui/completion_state";
@@ -167,8 +167,8 @@ export function TplRatedesk() {
167
167
  setPhase("done");
168
168
  }, [lines, decisions]);
169
169
 
170
- const runSteps: AgentRunStep[] = READ_STEPS.slice(0, Math.min(revealed + 1, READ_STEPS.length)).map((s, i) => ({
171
- id: `r${i}`, label: s.label, kind: s.kind, status: i < revealed ? "done" : "running",
170
+ const runItems: AgentRunItem[] = READ_STEPS.slice(0, Math.min(revealed + 1, READ_STEPS.length)).map((s, i) => ({
171
+ type: "step", id: `r${i}`, label: s.label, kind: s.kind, status: i < revealed ? "done" : "running",
172
172
  }));
173
173
  const newCount = lines.filter((l) => l.status === "new").length;
174
174
 
@@ -181,7 +181,7 @@ export function TplRatedesk() {
181
181
  <Text size="xl" weight="semibold">Rate desk</Text>
182
182
  <Text size="sm" color="muted">Look up any route, carrier or container. Prices come from carrier sheets — re-ingest to refresh.</Text>
183
183
  </View>
184
- <Button title="Import prices" color="primary" shape="rounded" onPress={() => setImportOpen(true)} />
184
+ <Button title="Import prices" color="primary" onPress={() => setImportOpen(true)} />
185
185
  </View>
186
186
 
187
187
  {/* REGISTER — the live rate card, filter to anything */}
@@ -193,11 +193,11 @@ export function TplRatedesk() {
193
193
  <View style={{ flexDirection: "row", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
194
194
  <View style={{ flexDirection: "row", alignItems: "center", gap: 6, flexGrow: 1, flexBasis: 360, minWidth: 280 }}>
195
195
  <View style={{ flex: 1, minWidth: 120 }}>
196
- <Combobox pill icon="map-pin" options={originOpts} recentOptions={originOpts} recentsLabel="Origin" value={fromV} onValueChange={setFromV} clearable onClear={() => setFromV(null)} placeholder="From" accessibilityLabel="Origin port" />
196
+ <Combobox icon="map-pin" options={originOpts} recentOptions={originOpts} recentsLabel="Origin" value={fromV} onValueChange={setFromV} clearable onClear={() => setFromV(null)} placeholder="From" accessibilityLabel="Origin port" />
197
197
  </View>
198
198
  <Icon name="arrow-right" size={14} color={colors.zinc[400]} />
199
199
  <View style={{ flex: 1, minWidth: 120 }}>
200
- <Combobox pill icon="map-pin" options={destOpts} recentOptions={destOpts} recentsLabel="Destination" value={toV} onValueChange={setToV} clearable onClear={() => setToV(null)} placeholder="To" accessibilityLabel="Destination port" />
200
+ <Combobox icon="map-pin" options={destOpts} recentOptions={destOpts} recentsLabel="Destination" value={toV} onValueChange={setToV} clearable onClear={() => setToV(null)} placeholder="To" accessibilityLabel="Destination port" />
201
201
  </View>
202
202
  </View>
203
203
  <ColumnFilter column={carrierCol} value={carrierF} onChange={setCarrierF} clearLabel="Clear carrier" />
@@ -238,7 +238,7 @@ export function TplRatedesk() {
238
238
  </View>
239
239
  ) : null}
240
240
 
241
- {phase === "reading" ? <AgentRun steps={runSteps} state="streaming" /> : null}
241
+ {phase === "reading" ? <AgentRun items={runItems} state="streaming" /> : null}
242
242
 
243
243
  {phase === "review" ? (
244
244
  <ChangeReview
@@ -270,8 +270,8 @@ export function TplRatedesk() {
270
270
  away inside the list. */}
271
271
  {phase === "idle" ? (
272
272
  <DialogFooter>
273
- {files.length > 0 ? <Button title="Clear" color="muted" shape="rounded" onPress={() => setFiles([])} /> : null}
274
- <Button title={files.length ? `Read ${files.length} sheets` : "Read sheets"} color="primary" shape="rounded" disabled={!files.length} onPress={() => { setRevealed(0); setPhase("reading"); }} />
273
+ {files.length > 0 ? <Button title="Clear" color="muted" onPress={() => setFiles([])} /> : null}
274
+ <Button title={files.length ? `Read ${files.length} sheets` : "Read sheets"} color="primary" disabled={!files.length} onPress={() => { setRevealed(0); setPhase("reading"); }} />
275
275
  </DialogFooter>
276
276
  ) : phase === "review" ? (
277
277
  <DialogFooter>
@@ -286,13 +286,13 @@ export function TplRatedesk() {
286
286
  </DialogFooter>
287
287
  ) : phase === "done" ? (
288
288
  <DialogFooter>
289
- <Button title="Import more" color="secondary" shape="rounded" onPress={reset} />
290
- <Button title="Done" color="primary" shape="rounded" onPress={closeImport} />
289
+ <Button title="Import more" color="secondary" onPress={reset} />
290
+ <Button title="Done" color="primary" onPress={closeImport} />
291
291
  </DialogFooter>
292
292
  ) : null}
293
293
  </Dialog>
294
294
 
295
- <FileGalleryModal files={files} activeIndex={activeFile} onIndexChange={setActiveFile} captionHint="ESC to close · ←/→ to move" />
295
+ <FileGalleryModal files={files} activeIndex={activeFile} onIndexChange={setActiveFile} />
296
296
  </>
297
297
  );
298
298
  }
@@ -295,7 +295,7 @@ export function TplRecord() {
295
295
  </Card>
296
296
  </View>
297
297
 
298
- <FileGalleryModal files={docs} activeIndex={activeDoc} onIndexChange={setActiveDoc} captionHint="ESC to close · ←/→ to move" />
298
+ <FileGalleryModal files={docs} activeIndex={activeDoc} onIndexChange={setActiveDoc} />
299
299
  </ScrollView>
300
300
  );
301
301
  }
@@ -15,7 +15,7 @@ import { EmptyState } from "@lotics/ui/empty_state";
15
15
  import { KPIStrip } from "@lotics/ui/kpi_strip";
16
16
  import { Link } from "@lotics/ui/link";
17
17
  import { Pagination } from "@lotics/ui/pagination";
18
- import { PillButton } from "@lotics/ui/pill_button";
18
+ import { Chip } from "@lotics/ui/chip";
19
19
  import { SegmentedControl, type SegmentOption } from "@lotics/ui/segmented_control";
20
20
  import { formatMoney } from "@lotics/ui/format_money";
21
21
  import { formatDate, parseDate } from "@lotics/ui/format_date";
@@ -311,9 +311,9 @@ export function TplReport() {
311
311
  {chips.length > 0 ? (
312
312
  <View style={{ flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
313
313
  {chips.map((c) => (
314
- <PillButton key={c.key} onDismiss={c.clear} dismissTooltip="Clear filter">
314
+ <Chip key={c.key} onDismiss={c.clear} dismissTooltip="Clear filter">
315
315
  <Text size="xs" weight="medium" color="muted">{c.label}</Text>
316
- </PillButton>
316
+ </Chip>
317
317
  ))}
318
318
  </View>
319
319
  ) : null}
@@ -12,7 +12,7 @@ import { Drawer } from "@lotics/ui/drawer";
12
12
  import { EmptyState } from "@lotics/ui/empty_state";
13
13
  import { KPIStrip } from "@lotics/ui/kpi_strip";
14
14
  import { Pagination } from "@lotics/ui/pagination";
15
- import { PillButton } from "@lotics/ui/pill_button";
15
+ import { Chip } from "@lotics/ui/chip";
16
16
  import { PressableRow } from "@lotics/ui/pressable_row";
17
17
  import { SearchInput } from "@lotics/ui/search_input";
18
18
  import { formatMoney } from "@lotics/ui/format_money";
@@ -266,9 +266,9 @@ export function TplStock() {
266
266
  </CardHeaderTitle>
267
267
  <View style={{ flexDirection: "row", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
268
268
  {chips.map((c) => (
269
- <PillButton key={c.key} onDismiss={c.clear} dismissTooltip="Clear filter">
269
+ <Chip key={c.key} onDismiss={c.clear} dismissTooltip="Clear filter">
270
270
  <Text size="xs" weight="medium" color="muted">{c.label}</Text>
271
- </PillButton>
271
+ </Chip>
272
272
  ))}
273
273
  <View style={{ width: 240 }}>
274
274
  <SearchInput
@@ -62,7 +62,7 @@ export function TplTriage() {
62
62
  <Text size="sm" color="muted">The agent classifies and routes each item — accept the call, override, or dismiss</Text>
63
63
  </View>
64
64
  {tab === "open" && highOpen > 0 ? (
65
- <Button title={`Accept ${highOpen} high-confidence`} color="secondary" shape="rounded" onPress={acceptAllHigh} />
65
+ <Button title={`Accept ${highOpen} high-confidence`} color="secondary" onPress={acceptAllHigh} />
66
66
  ) : null}
67
67
  </View>
68
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "5.7.0",
3
+ "version": "5.9.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
@@ -18,6 +18,8 @@
18
18
  "./file_badge": "./src/file_badge.tsx",
19
19
  "./file_dropzone": "./src/file_dropzone.tsx",
20
20
  "./file_thumbnail": "./src/file_thumbnail.tsx",
21
+ "./file_row": "./src/file_row.tsx",
22
+ "./file_rows": "./src/file_rows.tsx",
21
23
  "./file_preview": {
22
24
  "react-native": "./src/file_preview.tsx",
23
25
  "default": "./src/file_preview.web.tsx"
@@ -29,6 +31,8 @@
29
31
  "./use_image_rotation": "./src/use_image_rotation.ts",
30
32
  "./use_selection_mode": "./src/use_selection_mode.ts",
31
33
  "./file_thumbnail_grid": "./src/file_thumbnail_grid.tsx",
34
+ "./uploading_thumbnail": "./src/uploading_thumbnail.tsx",
35
+ "./file_grid": "./src/file_grid.tsx",
32
36
  "./pagination": "./src/pagination.tsx",
33
37
  "./bar_chart": "./src/bar_chart.tsx",
34
38
  "./line_chart": "./src/line_chart.tsx",
@@ -133,8 +137,8 @@
133
137
  "./radio_picker": "./src/radio_picker.tsx",
134
138
  "./form_text_input": "./src/form_text_input.tsx",
135
139
  "./form_switch": "./src/form_switch.tsx",
136
- "./pill_button": "./src/pill_button.tsx",
137
- "./filter_pill": "./src/filter_pill.tsx",
140
+ "./chip": "./src/chip.tsx",
141
+ "./filter_chip": "./src/filter_chip.tsx",
138
142
  "./range_slider": "./src/range_slider.tsx",
139
143
  "./counter": "./src/counter.tsx",
140
144
  "./link": "./src/link.tsx",
@@ -192,7 +196,6 @@
192
196
  },
193
197
  "./use_async_fn": "./src/use_async_fn.ts",
194
198
  "./use_form": "./src/use_form.ts",
195
- "./hover_action": "./src/hover_action.tsx",
196
199
  "./scroll_to_bottom": "./src/scroll_to_bottom.tsx",
197
200
  "./animation_fade_in": "./src/animation_fade_in.tsx",
198
201
  "./json_panel": "./src/json_panel.tsx",
@@ -233,6 +236,7 @@
233
236
  "expo-image": ">=3.0.0",
234
237
  "lucide-react": ">=0.460.0",
235
238
  "lucide-react-native": ">=0.460.0",
239
+ "pdfjs-dist": ">=6.0.0",
236
240
  "react": "^19.2.0",
237
241
  "react-dom": "^19.2.0",
238
242
  "react-native": ">=0.85.0",
@@ -246,6 +250,9 @@
246
250
  "@lotics/xlsx": {
247
251
  "optional": true
248
252
  },
253
+ "pdfjs-dist": {
254
+ "optional": true
255
+ },
249
256
  "expo-image": {
250
257
  "optional": true
251
258
  },
@@ -271,6 +278,7 @@
271
278
  "@types/react-dom": "~19.2.2",
272
279
  "@vitejs/plugin-react": "^4.3.4",
273
280
  "lucide-react": "^0.562.0",
281
+ "pdfjs-dist": "^6.0.227",
274
282
  "react-dom": "^19.2.0",
275
283
  "react-native-web": "^0.21.0",
276
284
  "vite": "^7.2.4"
@@ -4,19 +4,20 @@ import { colors } from "./colors";
4
4
  import { Text } from "./text";
5
5
  import { WaveAvatar } from "./wave_avatar";
6
6
  import { PressableHighlight } from "./pressable_highlight";
7
- import { AgentRun, resolveToolMeta, type AgentRunStep } from "./agent_run";
7
+ import { AgentRun, resolveToolMeta, type AgentRunItem } from "./agent_run";
8
8
 
9
9
  export interface AgentProgressProps {
10
- steps: AgentRunStep[];
10
+ /** The ordered transcript — same shape `AgentRun` takes. */
11
+ items: AgentRunItem[];
11
12
  state?: "streaming" | "done" | "error";
12
13
  /** The collapsed-pill label. Defaults to the running step's label (or
13
14
  * "Working…" / "Done"). */
14
15
  label?: string;
15
- /** The agent's streamed text — forwarded to the expanded `AgentRun`. */
16
- text?: string;
17
16
  /** Localize / override a tool step's label by raw tool name — applied to both
18
17
  * the collapsed pill and the expanded feed. */
19
18
  labelForTool?: (toolName: string) => string | undefined;
19
+ /** Localized "{n} steps" suffix, forwarded to the expanded feed. */
20
+ stepsLabel?: (n: number) => string;
20
21
  defaultExpanded?: boolean;
21
22
  }
22
23
 
@@ -29,15 +30,16 @@ export interface AgentProgressProps {
29
30
  * label, so the surface stays calm until you ask to see the steps.
30
31
  */
31
32
  export function AgentProgress(props: AgentProgressProps) {
32
- const { steps, state = "streaming", label, text, labelForTool, defaultExpanded } = props;
33
+ const { items, state = "streaming", label, labelForTool, stepsLabel, defaultExpanded } = props;
33
34
  const [expanded, setExpanded] = useState(defaultExpanded ?? false);
34
35
 
35
- const running = steps.filter((s) => s.status === "running").at(-1);
36
- const runningLabel = running
37
- ? running.kind === "tool"
38
- ? resolveToolMeta(running.label, labelForTool).label
39
- : running.label
40
- : undefined;
36
+ const running = [...items].reverse().find((it) => it.type === "step" && it.status === "running");
37
+ const runningLabel =
38
+ running && running.type === "step"
39
+ ? running.kind === "tool"
40
+ ? resolveToolMeta(running.label, labelForTool).label
41
+ : running.label
42
+ : undefined;
41
43
  const compact = label ?? runningLabel ?? (state === "done" ? "Done" : state === "error" ? "Stopped" : "Working…");
42
44
  const streaming = state === "streaming";
43
45
 
@@ -46,7 +48,7 @@ export function AgentProgress(props: AgentProgressProps) {
46
48
  {expanded ? (
47
49
  <View style={styles.panel}>
48
50
  <ScrollView style={{ maxHeight: 260 }} contentContainerStyle={{ padding: 16 }}>
49
- <AgentRun steps={steps} state={state} text={text} labelForTool={labelForTool} />
51
+ <AgentRun items={items} state={state} labelForTool={labelForTool} stepsLabel={stepsLabel} />
50
52
  </ScrollView>
51
53
  </View>
52
54
  ) : null}