@lotics/ui 5.7.0 → 5.8.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 (65) hide show
  1. package/AGENTS.md +41 -26
  2. package/examples/tpl_approvals.tsx +10 -10
  3. package/examples/tpl_crm_desk.tsx +3 -3
  4. package/examples/tpl_dieline.tsx +7 -6
  5. package/examples/tpl_directory.tsx +3 -3
  6. package/examples/tpl_dossier.tsx +7 -7
  7. package/examples/tpl_draft.tsx +4 -4
  8. package/examples/tpl_extract.tsx +7 -7
  9. package/examples/tpl_inventory.tsx +3 -3
  10. package/examples/tpl_lookup.tsx +4 -3
  11. package/examples/tpl_match.tsx +1 -1
  12. package/examples/tpl_ratedesk.tsx +11 -11
  13. package/examples/tpl_report.tsx +3 -3
  14. package/examples/tpl_stock.tsx +3 -3
  15. package/examples/tpl_triage.tsx +1 -1
  16. package/package.json +3 -3
  17. package/src/agent_progress.tsx +14 -12
  18. package/src/agent_run.tsx +253 -66
  19. package/src/back_button.tsx +3 -2
  20. package/src/badge.tsx +2 -1
  21. package/src/button.tsx +11 -32
  22. package/src/button_colors.ts +23 -0
  23. package/src/calendar/calendar_view.tsx +2 -2
  24. package/src/card_select_item.tsx +2 -1
  25. package/src/change_review.tsx +5 -6
  26. package/src/{pill_button.tsx → chip.tsx} +7 -7
  27. package/src/chip_group.tsx +2 -2
  28. package/src/column_filter.tsx +4 -4
  29. package/src/combobox.tsx +2 -9
  30. package/src/composer.tsx +4 -3
  31. package/src/control_surface.ts +15 -15
  32. package/src/date_field.tsx +2 -1
  33. package/src/date_filter.tsx +2 -1
  34. package/src/date_range_filter_field.tsx +2 -1
  35. package/src/dialog.tsx +2 -2
  36. package/src/discrepancy.tsx +1 -1
  37. package/src/drawer.tsx +1 -2
  38. package/src/{filter_pill.tsx → filter_chip.tsx} +8 -8
  39. package/src/finding.tsx +1 -1
  40. package/src/gantt/gantt_view.tsx +2 -2
  41. package/src/icon_button.tsx +38 -14
  42. package/src/inline_edit.tsx +2 -2
  43. package/src/link_button.tsx +2 -1
  44. package/src/menu_button.tsx +2 -1
  45. package/src/menu_list_item.tsx +2 -1
  46. package/src/number_input.tsx +2 -1
  47. package/src/picker.tsx +3 -3
  48. package/src/popover_header.tsx +2 -2
  49. package/src/radio_picker.tsx +2 -1
  50. package/src/range_slider.tsx +1 -1
  51. package/src/review_card.tsx +2 -2
  52. package/src/scan_field.tsx +2 -1
  53. package/src/scored_option.tsx +0 -1
  54. package/src/search_input.tsx +3 -2
  55. package/src/segmented_control.tsx +2 -1
  56. package/src/status_badge.tsx +2 -1
  57. package/src/stepper.tsx +3 -2
  58. package/src/switch_button.tsx +2 -1
  59. package/src/switcher.tsx +3 -2
  60. package/src/tabs.tsx +2 -1
  61. package/src/tag_input.tsx +2 -1
  62. package/src/text_input_field.tsx +2 -1
  63. package/src/time_field.tsx +2 -1
  64. package/src/time_picker.tsx +2 -1
  65. package/src/triage_row.tsx +3 -3
@@ -1,5 +1,6 @@
1
1
  import type { KeyboardEvent } from "react";
2
2
  import { colors } from "./colors";
3
+ import { CONTROL_RADIUS } from "./control_surface";
3
4
  import { fontFamilyRegular, inputTextStyleWeb } from "./text_utils";
4
5
  import { useFormField } from "./form_field";
5
6
 
@@ -50,7 +51,7 @@ export function NumberInput(props: NumberInputProps) {
50
51
  height: 40,
51
52
  paddingLeft: 8,
52
53
  paddingRight: 8,
53
- borderRadius: 8,
54
+ borderRadius: CONTROL_RADIUS,
54
55
  borderWidth: 1,
55
56
  borderStyle: "solid",
56
57
  borderColor: colors.border,
package/src/picker.tsx CHANGED
@@ -2,7 +2,7 @@ import { Picker as RNPicker } from "@react-native-picker/picker";
2
2
  import { StyleSheet, View, Pressable, StyleProp, ViewStyle, TextStyle } from "react-native";
3
3
  import { useState, useCallback, useMemo, useEffect, useRef } from "react";
4
4
  import { colors } from "./colors";
5
- import { ACTIVE_RING } from "./control_surface";
5
+ import { ACTIVE_RING, CONTROL_RADIUS } from "./control_surface";
6
6
  import { Text } from "./text";
7
7
  import { fontFamilyRegular, getInputTextStyle } from "./text_utils";
8
8
  import { Icon } from "./icon";
@@ -310,7 +310,7 @@ const styles = StyleSheet.create({
310
310
  height: 40,
311
311
  paddingHorizontal: 6,
312
312
  paddingRight: 28,
313
- borderRadius: 8,
313
+ borderRadius: CONTROL_RADIUS,
314
314
  borderWidth: 1,
315
315
  borderColor: colors.border,
316
316
  fontFamily: fontFamilyRegular,
@@ -324,7 +324,7 @@ const styles = StyleSheet.create({
324
324
  paddingHorizontal: 6,
325
325
  borderWidth: 1,
326
326
  borderColor: colors.border,
327
- borderRadius: 8,
327
+ borderRadius: CONTROL_RADIUS,
328
328
  cursor: "auto",
329
329
  height: 40,
330
330
  },
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
- import { Button } from "./button";
3
+ import { IconButton } from "./icon_button";
4
4
  import { Text } from "./text";
5
5
  import { usePopoverNav } from "./popover_nav";
6
6
 
@@ -18,7 +18,7 @@ export function PopoverHeader(props: PopoverHeaderProps) {
18
18
  return (
19
19
  <View style={styles.container}>
20
20
  {canGoBack && (
21
- <Button icon="chevron-left" color="secondary" accessibilityLabel={backLabel} onPress={goBack} />
21
+ <IconButton icon="chevron-left" size="lg" color="secondary" accessibilityLabel={backLabel} onPress={goBack} />
22
22
  )}
23
23
  <Text size="sm" weight="medium" style={{ flex: 1 }}>
24
24
  {title}
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useRef } from "react";
2
2
  import { View } from "react-native";
3
3
  import { colors } from "./colors";
4
+ import { CONTROL_RADIUS } from "./control_surface";
4
5
  import { Text } from "./text";
5
6
  import { PressableHighlight } from "./pressable_highlight";
6
7
  import { Icon } from "./icon";
@@ -120,7 +121,7 @@ function RadioOption<T extends string | number | symbol>(
120
121
  flexDirection: "row",
121
122
  alignItems: "center",
122
123
  padding: compact ? 8 : 12,
123
- borderRadius: 8,
124
+ borderRadius: CONTROL_RADIUS,
124
125
  gap: compact ? 8 : 16,
125
126
  }}
126
127
  onPress={handlePress}
@@ -24,7 +24,7 @@ export interface RangeSliderProps {
24
24
  type Thumb = "low" | "high";
25
25
 
26
26
  /**
27
- * A short human summary of a range selection for a `FilterPill` preview —
27
+ * A short human summary of a range selection for a `FilterChip` preview —
28
28
  * "5tr–10tr", or "≥ 5tr" / "≤ 10tr" when one end sits at its bound, and
29
29
  * `undefined` at the full range (no active filter). Mirrors `columnFilterSummary`.
30
30
  */
@@ -79,7 +79,7 @@ export function ReviewCard(props: ReviewCardProps) {
79
79
  <Text size="sm" weight="medium" color="muted" numberOfLines={1}>
80
80
  {accepted ? props.acceptedLabel ?? "Accepted" : props.dismissedLabel ?? "Dismissed"}
81
81
  </Text>
82
- {props.onUndo ? <Button title="Undo" color="muted" shape="rounded" onPress={props.onUndo} /> : null}
82
+ {props.onUndo ? <Button title="Undo" color="muted" onPress={props.onUndo} /> : null}
83
83
  </View>
84
84
  );
85
85
  }
@@ -132,7 +132,7 @@ export function ReviewCard(props: ReviewCardProps) {
132
132
  <View style={styles.footer}>
133
133
  {actions.map((a, i) => {
134
134
  const kind = a.kind ?? (i === actions.length - 1 ? "primary" : "muted");
135
- return <Button key={a.label} title={a.label} color={kind} shape="rounded" onPress={a.onPress} />;
135
+ return <Button key={a.label} title={a.label} color={kind} onPress={a.onPress} />;
136
136
  })}
137
137
  </View>
138
138
  ) : null}
@@ -1,5 +1,6 @@
1
1
  import { StyleSheet, View } from "react-native";
2
2
  import { colors, solid } from "./colors";
3
+ import { CONTROL_RADIUS } from "./control_surface";
3
4
  import { Text } from "./text";
4
5
  import { TextInputField } from "./text_input_field";
5
6
 
@@ -52,7 +53,7 @@ export function ScanField(props: ScanFieldProps) {
52
53
 
53
54
  const styles = StyleSheet.create({
54
55
  field: {
55
- borderRadius: 10,
56
+ borderRadius: CONTROL_RADIUS,
56
57
  backgroundColor: colors.white,
57
58
  },
58
59
  });
@@ -94,7 +94,6 @@ export function ScoredOption(props: ScoredOptionProps) {
94
94
  title={selected ? "Selected" : selectLabel ?? "Choose"}
95
95
  icon={selected ? "check" : undefined}
96
96
  color={selected ? "secondary" : "primary"}
97
- shape="rounded"
98
97
  onPress={onSelect}
99
98
  />
100
99
  </View>
@@ -9,6 +9,7 @@ import {
9
9
  type ViewStyle,
10
10
  } from "react-native";
11
11
  import { colors } from "./colors";
12
+ import { CONTROL_RADIUS } from "./control_surface";
12
13
  import { TextInputField } from "./text_input_field";
13
14
 
14
15
  type SearchInputProps = Omit<
@@ -57,7 +58,7 @@ export function SearchInput(props: SearchInputProps) {
57
58
 
58
59
  const styles = StyleSheet.create({
59
60
  // Search reads as a view-control by its leading search glyph + white fill —
60
- // NOT by a distinct shape. It shares the rounded-8 radius of every other
61
+ // NOT by a distinct shape. It shares the kit-wide `CONTROL_RADIUS` of every other
61
62
  // input/picker (TextInputField, Combobox, the date filter), so a toolbar of
62
63
  // controls reads as one consistent band. The white fill still pops against
63
64
  // the zinc-50 canvas where most toolbars live; a zinc-300 border (one step up
@@ -65,7 +66,7 @@ const styles = StyleSheet.create({
65
66
  // the fill alone would vanish. 40px (TextInputField default) — the system
66
67
  // control height every band aligns to.
67
68
  search: {
68
- borderRadius: 8,
69
+ borderRadius: CONTROL_RADIUS,
69
70
  backgroundColor: colors.white,
70
71
  // A thin 1px resting border (TextInputField's default width) — interaction
71
72
  // emphasis comes from the input's own focus outline, so it stays quiet at
@@ -3,6 +3,7 @@ import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
3
3
  import { PressableHighlight } from "./pressable_highlight";
4
4
  import { Text } from "./text";
5
5
  import { colors, withAlpha } from "./colors";
6
+ import { CONTROL_RADIUS } from "./control_surface";
6
7
 
7
8
  export interface SegmentOption<T extends string> {
8
9
  label: string;
@@ -183,7 +184,7 @@ const styles = StyleSheet.create({
183
184
  height: 40,
184
185
  gap: 2,
185
186
  padding: 3,
186
- borderRadius: 10,
187
+ borderRadius: CONTROL_RADIUS,
187
188
  backgroundColor: colors.zinc["100"],
188
189
  },
189
190
  trackDisabled: {
@@ -2,6 +2,7 @@ import { useEffect, useRef } from "react";
2
2
  import { View, StyleSheet, Animated, Easing } from "react-native";
3
3
  import { Text } from "./text";
4
4
  import { colors } from "./colors";
5
+ import { CONTROL_RADIUS } from "./control_surface";
5
6
 
6
7
  interface StatusBadgeProps {
7
8
  enabled: boolean;
@@ -69,7 +70,7 @@ const styles = StyleSheet.create({
69
70
  gap: 8,
70
71
  paddingVertical: 4,
71
72
  paddingHorizontal: 8,
72
- borderRadius: 8,
73
+ borderRadius: CONTROL_RADIUS,
73
74
  },
74
75
  dotWrapper: {
75
76
  width: 10,
package/src/stepper.tsx CHANGED
@@ -155,8 +155,9 @@ export function Step(props: StepProps) {
155
155
 
156
156
  // The node — status drives fill, never size. Monochrome: reached fills with ink,
157
157
  // the current step is a ring around white (pulsing when live), what's ahead is a
158
- // faint grey ring, the terminal is a checked fill.
159
- function Marker({ status, color, live }: { status: StepStatus; color: string; live: boolean }) {
158
+ // faint grey ring, the terminal is a checked fill. Exported so `AgentRun` reuses
159
+ // the exact same dot in its flat activity rows.
160
+ export function Marker({ status, color, live }: { status: StepStatus; color: string; live: boolean }) {
160
161
  if (status === "done") {
161
162
  // A passed step: filled in the accent ink with a white check.
162
163
  return (
@@ -3,6 +3,7 @@ import { Text } from "./text";
3
3
  import { Switch, type SwitchProps } from "./switch";
4
4
  import { PressableHighlight } from "./pressable_highlight";
5
5
  import { Icon, type IconName } from "./icon";
6
+ import { CONTROL_RADIUS } from "./control_surface";
6
7
 
7
8
  export interface SwitchButtonProps extends SwitchProps {
8
9
  icon?: IconName;
@@ -22,7 +23,7 @@ export function SwitchButton(props: SwitchButtonProps) {
22
23
  paddingVertical: 4,
23
24
  paddingLeft: 8,
24
25
  paddingRight: 6,
25
- borderRadius: 8,
26
+ borderRadius: CONTROL_RADIUS,
26
27
  }}
27
28
  onPress={() => onChange?.(!value)}
28
29
  tooltip={tooltip}
package/src/switcher.tsx CHANGED
@@ -7,6 +7,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "./popover";
7
7
  import { Stack } from "./stack";
8
8
  import { Text } from "./text";
9
9
  import { colors } from "./colors";
10
+ import { CONTROL_HEIGHT, CONTROL_RADIUS } from "./control_surface";
10
11
 
11
12
  export interface SwitcherItem {
12
13
  id: string;
@@ -100,9 +101,9 @@ const styles = StyleSheet.create({
100
101
  flexDirection: "row",
101
102
  alignItems: "center",
102
103
  gap: 6,
104
+ height: CONTROL_HEIGHT,
103
105
  paddingHorizontal: 10,
104
- paddingVertical: 6,
105
- borderRadius: 8,
106
+ borderRadius: CONTROL_RADIUS,
106
107
  },
107
108
  popoverBody: {
108
109
  minWidth: 220,
package/src/tabs.tsx CHANGED
@@ -3,6 +3,7 @@ import { View } from "react-native";
3
3
  import { PressableHighlight } from "./pressable_highlight";
4
4
  import { Text } from "./text";
5
5
  import { colors } from "./colors";
6
+ import { CONTROL_RADIUS } from "./control_surface";
6
7
 
7
8
  export interface TabOption<T extends string> {
8
9
  label: string;
@@ -138,7 +139,7 @@ function TabButton<T extends string>(props: TabButtonProps<T>) {
138
139
  style={{
139
140
  paddingVertical: 8,
140
141
  paddingHorizontal: 16,
141
- borderRadius: 8,
142
+ borderRadius: CONTROL_RADIUS,
142
143
  backgroundColor: pressed
143
144
  ? colors.zinc["100"]
144
145
  : hovered
package/src/tag_input.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  import { useMemo, useRef, useState } from "react";
2
2
  import { Pressable, ScrollView, StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
3
3
  import { colors } from "./colors";
4
+ import { CONTROL_RADIUS } from "./control_surface";
4
5
  import { Text } from "./text";
5
6
  import { Icon } from "./icon";
6
7
  import { TextInputField } from "./text_input_field";
@@ -161,7 +162,7 @@ const styles = StyleSheet.create({
161
162
  paddingVertical: 5,
162
163
  borderWidth: 1,
163
164
  borderColor: colors.border,
164
- borderRadius: 8,
165
+ borderRadius: CONTROL_RADIUS,
165
166
  backgroundColor: colors.background,
166
167
  },
167
168
  chip: {
@@ -5,6 +5,7 @@ import {
5
5
  View,
6
6
  } from "react-native";
7
7
  import { colors } from "./colors";
8
+ import { CONTROL_RADIUS } from "./control_surface";
8
9
  import { Ref, useCallback } from "react";
9
10
  import { Icon, IconName } from "./icon";
10
11
  import { IconButton } from "./icon_button";
@@ -163,7 +164,7 @@ export function TextInputField(props: TextInputFieldProps) {
163
164
 
164
165
  const styles = StyleSheet.create({
165
166
  input: {
166
- borderRadius: 8,
167
+ borderRadius: CONTROL_RADIUS,
167
168
  borderWidth: 1,
168
169
  borderColor: colors.border,
169
170
  height: 40,
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { Pressable, ScrollView, StyleSheet, View } from "react-native";
3
3
  import { colors } from "./colors";
4
+ import { CONTROL_RADIUS } from "./control_surface";
4
5
  import { Text } from "./text";
5
6
  import { Icon } from "./icon";
6
7
  import { Popover, PopoverContent } from "./popover";
@@ -266,7 +267,7 @@ const styles = StyleSheet.create({
266
267
  height: 40,
267
268
  paddingHorizontal: 8,
268
269
  gap: 2,
269
- borderRadius: 8,
270
+ borderRadius: CONTROL_RADIUS,
270
271
  borderWidth: 1,
271
272
  borderColor: colors.border,
272
273
  backgroundColor: colors.background,
@@ -1,6 +1,7 @@
1
1
  import type { KeyboardEvent } from "react";
2
2
  import { colors } from "@lotics/ui/colors";
3
3
  import { fontFamilyRegular, inputTextStyleWeb } from "@lotics/ui/text_utils";
4
+ import { CONTROL_RADIUS } from "./control_surface";
4
5
  export interface TimePickerProps {
5
6
  value?: string;
6
7
  onValueChange: (value: string) => void;
@@ -31,7 +32,7 @@ export function TimePicker(props: TimePickerProps) {
31
32
  height: 40,
32
33
  paddingLeft: 8,
33
34
  paddingRight: 8,
34
- borderRadius: 8,
35
+ borderRadius: CONTROL_RADIUS,
35
36
  borderWidth: 1,
36
37
  borderStyle: "solid",
37
38
  borderColor: colors.border,
@@ -74,9 +74,9 @@ export function TriageRow(props: TriageRowProps) {
74
74
  </Text>
75
75
  ) : (
76
76
  <View style={styles.footer}>
77
- {props.onOverride ? <Button title="Reclassify" color="muted" shape="rounded" onPress={props.onOverride} /> : null}
78
- {props.onDismiss ? <Button title="Dismiss" color="muted" shape="rounded" onPress={props.onDismiss} /> : null}
79
- {props.onAccept ? <Button title="Accept" color="primary" shape="rounded" onPress={props.onAccept} /> : null}
77
+ {props.onOverride ? <Button title="Reclassify" color="muted" onPress={props.onOverride} /> : null}
78
+ {props.onDismiss ? <Button title="Dismiss" color="muted" onPress={props.onDismiss} /> : null}
79
+ {props.onAccept ? <Button title="Accept" color="primary" onPress={props.onAccept} /> : null}
80
80
  </View>
81
81
  )}
82
82
  </View>