panelui-native 0.25.0 → 0.30.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 (242) hide show
  1. package/README.md +18 -1
  2. package/lib/module/components/accordion/index.js +8 -8
  3. package/lib/module/components/accordion/index.js.map +1 -1
  4. package/lib/module/components/alert/index.js +2 -2
  5. package/lib/module/components/alert/index.js.map +1 -1
  6. package/lib/module/components/area-chart/index.js +182 -50
  7. package/lib/module/components/area-chart/index.js.map +1 -1
  8. package/lib/module/components/attachment/index.js +2 -1
  9. package/lib/module/components/attachment/index.js.map +1 -1
  10. package/lib/module/components/avatar/index.js +3 -3
  11. package/lib/module/components/avatar/index.js.map +1 -1
  12. package/lib/module/components/badge/index.js +4 -4
  13. package/lib/module/components/badge/index.js.map +1 -1
  14. package/lib/module/components/bar-chart/index.js +197 -56
  15. package/lib/module/components/bar-chart/index.js.map +1 -1
  16. package/lib/module/components/bottom-sheet/index.js +15 -10
  17. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  18. package/lib/module/components/breadcrumb/index.js +6 -6
  19. package/lib/module/components/breadcrumb/index.js.map +1 -1
  20. package/lib/module/components/button/index.js +11 -6
  21. package/lib/module/components/button/index.js.map +1 -1
  22. package/lib/module/components/calendar/index.js +792 -0
  23. package/lib/module/components/calendar/index.js.map +1 -0
  24. package/lib/module/components/carousel/index.js +680 -0
  25. package/lib/module/components/carousel/index.js.map +1 -0
  26. package/lib/module/components/chip/index.js +4 -4
  27. package/lib/module/components/chip/index.js.map +1 -1
  28. package/lib/module/components/code-block/highlight.js +363 -0
  29. package/lib/module/components/code-block/highlight.js.map +1 -0
  30. package/lib/module/components/code-block/index.js +313 -0
  31. package/lib/module/components/code-block/index.js.map +1 -0
  32. package/lib/module/components/date-picker/index.js +165 -0
  33. package/lib/module/components/date-picker/index.js.map +1 -0
  34. package/lib/module/components/dialog/index.js +7 -2
  35. package/lib/module/components/dialog/index.js.map +1 -1
  36. package/lib/module/components/direction/index.js +2 -1
  37. package/lib/module/components/direction/index.js.map +1 -1
  38. package/lib/module/components/empty-state/index.js +2 -2
  39. package/lib/module/components/empty-state/index.js.map +1 -1
  40. package/lib/module/components/field/index.js +5 -5
  41. package/lib/module/components/field/index.js.map +1 -1
  42. package/lib/module/components/flow/index.js +3 -3
  43. package/lib/module/components/flow/index.js.map +1 -1
  44. package/lib/module/components/frame/index.js +5 -5
  45. package/lib/module/components/frame/index.js.map +1 -1
  46. package/lib/module/components/input-group/index.js +3 -2
  47. package/lib/module/components/input-group/index.js.map +1 -1
  48. package/lib/module/components/item/index.js +9 -9
  49. package/lib/module/components/item/index.js.map +1 -1
  50. package/lib/module/components/label/index.js +4 -4
  51. package/lib/module/components/label/index.js.map +1 -1
  52. package/lib/module/components/line-chart/index.js +167 -44
  53. package/lib/module/components/line-chart/index.js.map +1 -1
  54. package/lib/module/components/map/index.js +7 -7
  55. package/lib/module/components/map/index.js.map +1 -1
  56. package/lib/module/components/marker/index.js +3 -3
  57. package/lib/module/components/marker/index.js.map +1 -1
  58. package/lib/module/components/menu/index.js +714 -0
  59. package/lib/module/components/menu/index.js.map +1 -0
  60. package/lib/module/components/message/index.js +7 -7
  61. package/lib/module/components/message/index.js.map +1 -1
  62. package/lib/module/components/message-scroller/index.js +5 -4
  63. package/lib/module/components/message-scroller/index.js.map +1 -1
  64. package/lib/module/components/plan/index.js +296 -0
  65. package/lib/module/components/plan/index.js.map +1 -0
  66. package/lib/module/components/popover/index.js +73 -10
  67. package/lib/module/components/popover/index.js.map +1 -1
  68. package/lib/module/components/radio-group/index.js +15 -10
  69. package/lib/module/components/radio-group/index.js.map +1 -1
  70. package/lib/module/components/reasoning/index.js +244 -0
  71. package/lib/module/components/reasoning/index.js.map +1 -0
  72. package/lib/module/components/section-rail/index.js +4 -4
  73. package/lib/module/components/section-rail/index.js.map +1 -1
  74. package/lib/module/components/select/index.js +10 -4
  75. package/lib/module/components/select/index.js.map +1 -1
  76. package/lib/module/components/sources/index.js +221 -0
  77. package/lib/module/components/sources/index.js.map +1 -0
  78. package/lib/module/components/steps/index.js +4 -4
  79. package/lib/module/components/steps/index.js.map +1 -1
  80. package/lib/module/components/tabs/index.js +7 -7
  81. package/lib/module/components/tabs/index.js.map +1 -1
  82. package/lib/module/components/task/index.js +300 -0
  83. package/lib/module/components/task/index.js.map +1 -0
  84. package/lib/module/components/textarea/index.js +295 -0
  85. package/lib/module/components/textarea/index.js.map +1 -0
  86. package/lib/module/components/timeline/index.js +4 -4
  87. package/lib/module/components/timeline/index.js.map +1 -1
  88. package/lib/module/components/toast/index.js +2 -2
  89. package/lib/module/components/toast/index.js.map +1 -1
  90. package/lib/module/components/toggle-button/index.js +5 -5
  91. package/lib/module/components/toggle-button/index.js.map +1 -1
  92. package/lib/module/components/tooltip/index.js +648 -0
  93. package/lib/module/components/tooltip/index.js.map +1 -0
  94. package/lib/module/components/typography/index.js +2 -2
  95. package/lib/module/components/typography/index.js.map +1 -1
  96. package/lib/module/hooks/index.js +1 -0
  97. package/lib/module/hooks/index.js.map +1 -1
  98. package/lib/module/hooks/use-back-handler.js +37 -0
  99. package/lib/module/hooks/use-back-handler.js.map +1 -0
  100. package/lib/module/icons/index.js +202 -0
  101. package/lib/module/icons/index.js.map +1 -1
  102. package/lib/module/index.js +13 -1
  103. package/lib/module/index.js.map +1 -1
  104. package/lib/module/primitives/collapse.js +112 -0
  105. package/lib/module/primitives/collapse.js.map +1 -0
  106. package/lib/module/primitives/keyboard-avoider.js +2 -1
  107. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  108. package/lib/module/primitives/text.js +25 -1
  109. package/lib/module/primitives/text.js.map +1 -1
  110. package/lib/module/utils/date.js +386 -0
  111. package/lib/module/utils/date.js.map +1 -0
  112. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  113. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  114. package/lib/typescript/src/components/area-chart/index.d.ts +41 -1
  115. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  116. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
  117. package/lib/typescript/src/components/bar-chart/index.d.ts +44 -2
  118. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
  119. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
  121. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/calendar/index.d.ts +173 -0
  123. package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/carousel/index.d.ts +179 -0
  125. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
  127. package/lib/typescript/src/components/code-block/highlight.d.ts +39 -0
  128. package/lib/typescript/src/components/code-block/highlight.d.ts.map +1 -0
  129. package/lib/typescript/src/components/code-block/index.d.ts +113 -0
  130. package/lib/typescript/src/components/code-block/index.d.ts.map +1 -0
  131. package/lib/typescript/src/components/date-picker/index.d.ts +97 -0
  132. package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  134. package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
  135. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/field/index.d.ts.map +1 -1
  137. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  138. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  139. package/lib/typescript/src/components/item/index.d.ts.map +1 -1
  140. package/lib/typescript/src/components/label/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/line-chart/index.d.ts +41 -2
  142. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  143. package/lib/typescript/src/components/map/index.d.ts.map +1 -1
  144. package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/menu/index.d.ts +261 -0
  146. package/lib/typescript/src/components/menu/index.d.ts.map +1 -0
  147. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  148. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/plan/index.d.ts +127 -0
  150. package/lib/typescript/src/components/plan/index.d.ts.map +1 -0
  151. package/lib/typescript/src/components/popover/index.d.ts +35 -1
  152. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  153. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  154. package/lib/typescript/src/components/reasoning/index.d.ts +97 -0
  155. package/lib/typescript/src/components/reasoning/index.d.ts.map +1 -0
  156. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  157. package/lib/typescript/src/components/sources/index.d.ts +85 -0
  158. package/lib/typescript/src/components/sources/index.d.ts.map +1 -0
  159. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  160. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/task/index.d.ts +103 -0
  162. package/lib/typescript/src/components/task/index.d.ts.map +1 -0
  163. package/lib/typescript/src/components/textarea/index.d.ts +175 -0
  164. package/lib/typescript/src/components/textarea/index.d.ts.map +1 -0
  165. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  166. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  168. package/lib/typescript/src/components/tooltip/index.d.ts +180 -0
  169. package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
  170. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  171. package/lib/typescript/src/hooks/index.d.ts +1 -0
  172. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  173. package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
  174. package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
  175. package/lib/typescript/src/icons/index.d.ts +17 -0
  176. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  177. package/lib/typescript/src/index.d.ts +16 -4
  178. package/lib/typescript/src/index.d.ts.map +1 -1
  179. package/lib/typescript/src/primitives/collapse.d.ts +42 -0
  180. package/lib/typescript/src/primitives/collapse.d.ts.map +1 -0
  181. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  182. package/lib/typescript/src/primitives/text.d.ts +20 -0
  183. package/lib/typescript/src/primitives/text.d.ts.map +1 -1
  184. package/lib/typescript/src/utils/date.d.ts +116 -0
  185. package/lib/typescript/src/utils/date.d.ts.map +1 -0
  186. package/package.json +1 -1
  187. package/src/components/accordion/index.tsx +8 -12
  188. package/src/components/alert/index.tsx +2 -2
  189. package/src/components/area-chart/index.tsx +208 -55
  190. package/src/components/attachment/index.tsx +2 -2
  191. package/src/components/avatar/index.tsx +3 -3
  192. package/src/components/badge/index.tsx +6 -6
  193. package/src/components/bar-chart/index.tsx +214 -54
  194. package/src/components/bottom-sheet/index.tsx +17 -16
  195. package/src/components/breadcrumb/index.tsx +6 -8
  196. package/src/components/button/index.tsx +12 -8
  197. package/src/components/calendar/index.tsx +970 -0
  198. package/src/components/carousel/index.tsx +788 -0
  199. package/src/components/chip/index.tsx +4 -6
  200. package/src/components/code-block/highlight.ts +278 -0
  201. package/src/components/code-block/index.tsx +341 -0
  202. package/src/components/date-picker/index.tsx +255 -0
  203. package/src/components/dialog/index.tsx +7 -2
  204. package/src/components/direction/index.tsx +2 -1
  205. package/src/components/empty-state/index.tsx +2 -2
  206. package/src/components/field/index.tsx +3 -5
  207. package/src/components/flow/index.tsx +3 -3
  208. package/src/components/frame/index.tsx +5 -7
  209. package/src/components/input-group/index.tsx +3 -2
  210. package/src/components/item/index.tsx +9 -9
  211. package/src/components/label/index.tsx +2 -2
  212. package/src/components/line-chart/index.tsx +189 -50
  213. package/src/components/map/index.tsx +7 -9
  214. package/src/components/marker/index.tsx +3 -3
  215. package/src/components/menu/index.tsx +812 -0
  216. package/src/components/message/index.tsx +7 -7
  217. package/src/components/message-scroller/index.tsx +5 -4
  218. package/src/components/plan/index.tsx +325 -0
  219. package/src/components/popover/index.tsx +111 -8
  220. package/src/components/radio-group/index.tsx +13 -4
  221. package/src/components/reasoning/index.tsx +297 -0
  222. package/src/components/section-rail/index.tsx +4 -4
  223. package/src/components/select/index.tsx +10 -4
  224. package/src/components/sources/index.tsx +278 -0
  225. package/src/components/steps/index.tsx +4 -4
  226. package/src/components/tabs/index.tsx +7 -9
  227. package/src/components/task/index.tsx +329 -0
  228. package/src/components/textarea/index.tsx +381 -0
  229. package/src/components/timeline/index.tsx +4 -4
  230. package/src/components/toast/index.tsx +2 -2
  231. package/src/components/toggle-button/index.tsx +5 -7
  232. package/src/components/tooltip/index.tsx +793 -0
  233. package/src/components/typography/index.tsx +2 -2
  234. package/src/hooks/index.ts +1 -0
  235. package/src/hooks/use-back-handler.ts +37 -0
  236. package/src/icons/index.tsx +147 -0
  237. package/src/index.ts +116 -0
  238. package/src/primitives/collapse.tsx +128 -0
  239. package/src/primitives/keyboard-avoider.tsx +2 -1
  240. package/src/primitives/text.tsx +28 -1
  241. package/src/utils/date.ts +446 -0
  242. package/theme.css +112 -0
@@ -40,7 +40,7 @@ import Animated, {
40
40
  withSpring,
41
41
  } from 'react-native-reanimated';
42
42
  import { tv } from 'tailwind-variants';
43
- import { Text } from '../../primitives/text';
43
+ import { Text, textChildren } from '../../primitives/text';
44
44
  import { cn } from '../../utils/cn';
45
45
 
46
46
  const SPRING = { damping: 24, stiffness: 300, mass: 0.7 } as const;
@@ -197,7 +197,7 @@ function TabsRoot({
197
197
  return (
198
198
  <TabsContext.Provider value={context}>
199
199
  <View className={cn('gap-3', className)} {...props}>
200
- {children}
200
+ {textChildren(children)}
201
201
  </View>
202
202
  </TabsContext.Provider>
203
203
  );
@@ -274,7 +274,7 @@ function TabsList({ className, scrollable = false, children, ...props }: TabsLis
274
274
  const row = (
275
275
  <View accessibilityRole="tablist" className={cn(list(), className)} {...props}>
276
276
  <TabsIndicator />
277
- {children}
277
+ {textChildren(children)}
278
278
  </View>
279
279
  );
280
280
 
@@ -341,13 +341,11 @@ function TabsTrigger({
341
341
  className={cn(slots.trigger(), (icon || badge) && 'flex-row gap-1.5', className)}
342
342
  >
343
343
  {icon}
344
- {typeof children === 'string' ? (
344
+ {textChildren(children, (text) => (
345
345
  <Text size="sm" weight="medium" className={slots.label()}>
346
- {children}
346
+ {text}
347
347
  </Text>
348
- ) : (
349
- children
350
- )}
348
+ ))}
351
349
  {badge}
352
350
  </Pressable>
353
351
  );
@@ -384,7 +382,7 @@ function TabsContent({ className, value, children, style, ...props }: TabsConten
384
382
  className={className}
385
383
  {...props}
386
384
  >
387
- {children}
385
+ {textChildren(children)}
388
386
  </Animated.View>
389
387
  );
390
388
  }
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Task — one step an agent took, and what it did while it was there.
3
+ *
4
+ * The header is the step; the body is the detail nobody reads unless something
5
+ * went wrong. Open by default, because a task that is running is the thing the
6
+ * reader is watching — and it stays open afterwards rather than folding itself
7
+ * away, because unlike a reasoning trace the steps are the record of what
8
+ * happened and are worth scrolling back through.
9
+ *
10
+ * ```tsx
11
+ * <Task status="running">
12
+ * <Task.Trigger title="Searching the codebase" />
13
+ * <Task.Content>
14
+ * <Task.Item>
15
+ * Read <Task.File>calendar/index.tsx</Task.File>
16
+ * </Task.Item>
17
+ * </Task.Content>
18
+ * </Task>
19
+ * ```
20
+ *
21
+ * ## Where the props come from
22
+ *
23
+ * With the AI SDK a task is a tool-call part: `part.type` is `tool-<name>` and
24
+ * `part.state` runs `input-streaming` → `input-available` → `output-available`
25
+ * or `output-error`. Those map onto `status` as pending, running, complete and
26
+ * error; `part.input` is usually what the body should say.
27
+ */
28
+ import {
29
+ createContext,
30
+ useCallback,
31
+ useContext,
32
+ useEffect,
33
+ useMemo,
34
+ useState,
35
+ type ReactNode,
36
+ } from 'react';
37
+ import { Pressable, View, type PressableProps, type ViewProps } from 'react-native';
38
+ import Animated, {
39
+ useAnimatedStyle,
40
+ useReducedMotion,
41
+ useSharedValue,
42
+ withTiming,
43
+ } from 'react-native-reanimated';
44
+ import { tv } from 'tailwind-variants';
45
+ import { useCSSVariable } from 'uniwind';
46
+ import {
47
+ AlertTriangleIcon,
48
+ CheckCircleIcon,
49
+ ChevronDownIcon,
50
+ CircleIcon,
51
+ SearchIcon,
52
+ } from '../../icons';
53
+ import { Collapse } from '../../primitives/collapse';
54
+ import { Text, textChildren } from '../../primitives/text';
55
+ import { cn } from '../../utils/cn';
56
+ import { Shimmer } from '../shimmer';
57
+
58
+ export type TaskStatus = 'pending' | 'running' | 'complete' | 'error';
59
+
60
+ const taskVariants = tv({
61
+ slots: {
62
+ root: 'w-full gap-2',
63
+ trigger: 'flex-row items-center gap-2 py-0.5',
64
+ title: 'flex-1 text-sm text-muted-foreground',
65
+ /*
66
+ * Indented behind a rule rather than merely padded. The rule is what says
67
+ * these lines belong to the step above them instead of being the next few
68
+ * steps, which matters as soon as there is more than one task in a row.
69
+ */
70
+ content: 'ms-2 gap-1.5 border-s border-border ps-4',
71
+ item: 'text-sm leading-relaxed text-muted-foreground',
72
+ file: 'flex-row items-center gap-1 self-start rounded-md border border-border bg-muted px-1.5 py-0.5',
73
+ fileLabel: 'font-mono text-xs text-foreground',
74
+ },
75
+ variants: {
76
+ status: {
77
+ pending: { title: 'text-muted-foreground/60' },
78
+ running: {},
79
+ complete: {},
80
+ error: { title: 'text-destructive' },
81
+ },
82
+ },
83
+ defaultVariants: {
84
+ status: 'complete',
85
+ },
86
+ });
87
+
88
+ interface TaskContextValue {
89
+ status: TaskStatus;
90
+ open: boolean;
91
+ setOpen: (open: boolean) => void;
92
+ }
93
+
94
+ const TaskContext = createContext<TaskContextValue | null>(null);
95
+
96
+ function useTask(component: string): TaskContextValue {
97
+ const context = useContext(TaskContext);
98
+ if (!context) {
99
+ throw new Error(`${component} must be used within a <Task>`);
100
+ }
101
+ return context;
102
+ }
103
+
104
+ export interface TaskProps extends Omit<ViewProps, 'children'> {
105
+ className?: string;
106
+ /**
107
+ * Where the step has got to. Drives the leading glyph, and puts a shimmer on
108
+ * the title while it is `running`.
109
+ */
110
+ status?: TaskStatus;
111
+ /** Controlled open state. */
112
+ open?: boolean;
113
+ /** Initial state when uncontrolled. Open — the steps are the record. */
114
+ defaultOpen?: boolean;
115
+ onOpenChange?: (open: boolean) => void;
116
+ children?: ReactNode;
117
+ }
118
+
119
+ function TaskRoot({
120
+ className,
121
+ status = 'complete',
122
+ open: openProp,
123
+ defaultOpen = true,
124
+ onOpenChange,
125
+ children,
126
+ ...props
127
+ }: TaskProps) {
128
+ const { root } = taskVariants({ status });
129
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
130
+ const isControlled = openProp !== undefined;
131
+ const open = isControlled ? openProp : internalOpen;
132
+
133
+ const setOpen = useCallback(
134
+ (next: boolean) => {
135
+ if (!isControlled) setInternalOpen(next);
136
+ onOpenChange?.(next);
137
+ },
138
+ [isControlled, onOpenChange]
139
+ );
140
+
141
+ const context = useMemo(() => ({ status, open, setOpen }), [status, open, setOpen]);
142
+
143
+ return (
144
+ <TaskContext.Provider value={context}>
145
+ <View {...props} className={cn(root(), className)}>
146
+ {children}
147
+ </View>
148
+ </TaskContext.Provider>
149
+ );
150
+ }
151
+ TaskRoot.displayName = 'Task';
152
+
153
+ export interface TaskTriggerProps extends Omit<PressableProps, 'children' | 'style'> {
154
+ className?: string;
155
+ /** What the step is. Shimmers while the status is `running`. */
156
+ title?: string;
157
+ /** Leading glyph. Derived from `status` when not given. */
158
+ icon?: ReactNode;
159
+ /** Replaces the whole row. */
160
+ children?: ReactNode;
161
+ }
162
+
163
+ function TaskTrigger({
164
+ className,
165
+ title,
166
+ icon,
167
+ children,
168
+ onPress,
169
+ ...props
170
+ }: TaskTriggerProps) {
171
+ const { status, open, setOpen } = useTask('Task.Trigger');
172
+ const slots = taskVariants({ status });
173
+ const reducedMotion = useReducedMotion();
174
+ const progress = useSharedValue(open ? 1 : 0);
175
+
176
+ useEffect(() => {
177
+ progress.value = reducedMotion
178
+ ? open
179
+ ? 1
180
+ : 0
181
+ : withTiming(open ? 1 : 0, { duration: 180 });
182
+ }, [open, reducedMotion, progress]);
183
+
184
+ const chevronStyle = useAnimatedStyle(() => ({
185
+ transform: [{ rotate: `${progress.value * 180}deg` }],
186
+ }));
187
+
188
+ return (
189
+ <Pressable
190
+ accessibilityRole="button"
191
+ accessibilityState={{ expanded: open }}
192
+ onPress={(event) => {
193
+ onPress?.(event);
194
+ setOpen(!open);
195
+ }}
196
+ className={cn(slots.trigger(), className)}
197
+ {...props}
198
+ >
199
+ {children ?? (
200
+ <>
201
+ {icon ?? <TaskStatusIcon status={status} />}
202
+ {status === 'running' && title ? (
203
+ <View className="flex-1">
204
+ <Shimmer textClassName={slots.title()}>{title}</Shimmer>
205
+ </View>
206
+ ) : (
207
+ <Text numberOfLines={1} className={slots.title()}>
208
+ {title}
209
+ </Text>
210
+ )}
211
+ <Animated.View style={chevronStyle}>
212
+ <ChevronDownIcon size={16} />
213
+ </Animated.View>
214
+ </>
215
+ )}
216
+ </Pressable>
217
+ );
218
+ }
219
+
220
+ /**
221
+ * The glyph for each status. A magnifier while running: a step is a search.
222
+ *
223
+ * Every one is given an explicit colour. The filled status icons fall back to
224
+ * `currentColor`, which React Native's SVG does not resolve — it paints black,
225
+ * which on a dark theme is a black disc floating in the row. They are built to
226
+ * take their colour from an enclosing `IconColorProvider`, and a task row is
227
+ * not one, so the tint is resolved here.
228
+ */
229
+ function TaskStatusIcon({ status }: { status: TaskStatus }) {
230
+ const muted = useTint('--color-muted-foreground');
231
+ const success = useTint('--color-success');
232
+ const destructive = useTint('--color-destructive');
233
+
234
+ switch (status) {
235
+ case 'pending':
236
+ return <CircleIcon size={16} color={muted} />;
237
+ case 'error':
238
+ return <AlertTriangleIcon size={16} color={destructive} />;
239
+ case 'complete':
240
+ return <CheckCircleIcon size={16} color={success} />;
241
+ default:
242
+ return <SearchIcon size={16} color={muted} />;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * A theme token as a colour an icon will accept.
248
+ *
249
+ * `useCSSVariable` answers with whatever the token holds, which for a length or
250
+ * a number is not a colour at all — so anything that is not a string is dropped
251
+ * and the icon falls back to what it would have used anyway.
252
+ */
253
+ function useTint(variable: string): string | undefined {
254
+ const raw = useCSSVariable(variable);
255
+ return typeof raw === 'string' ? raw : undefined;
256
+ }
257
+
258
+ export interface TaskContentProps extends Omit<ViewProps, 'children'> {
259
+ className?: string;
260
+ children?: ReactNode;
261
+ }
262
+
263
+ function TaskContent({ className, children, ...props }: TaskContentProps) {
264
+ const { open } = useTask('Task.Content');
265
+ const { content } = taskVariants();
266
+
267
+ return (
268
+ <Collapse open={open} className={cn(content(), className)} {...props}>
269
+ {children}
270
+ </Collapse>
271
+ );
272
+ }
273
+
274
+ export interface TaskItemProps extends Omit<ViewProps, 'children'> {
275
+ className?: string;
276
+ children?: ReactNode;
277
+ }
278
+
279
+ /** One line of what the step did. */
280
+ function TaskItem({ className, children, ...props }: TaskItemProps) {
281
+ const { item } = taskVariants();
282
+
283
+ return (
284
+ <View {...props} className={cn('flex-row flex-wrap items-center gap-1', className)}>
285
+ {textChildren(children, (text) => (
286
+ <Text className={item()}>{text}</Text>
287
+ ))}
288
+ </View>
289
+ );
290
+ }
291
+
292
+ export interface TaskFileProps extends Omit<ViewProps, 'children'> {
293
+ className?: string;
294
+ /** A glyph for the file's kind, drawn before the name. */
295
+ icon?: ReactNode;
296
+ children?: ReactNode;
297
+ }
298
+
299
+ /**
300
+ * A filename inside a line, drawn as a chip.
301
+ *
302
+ * Bordered rather than merely monospaced, because a path in the middle of a
303
+ * sentence is otherwise indistinguishable from the sentence — and the paths
304
+ * are the part of a task line anyone actually scans for.
305
+ */
306
+ function TaskFile({ className, icon, children, ...props }: TaskFileProps) {
307
+ const { file, fileLabel } = taskVariants();
308
+
309
+ return (
310
+ <View {...props} className={cn(file(), className)}>
311
+ {icon}
312
+ {textChildren(children, (text) => (
313
+ <Text className={fileLabel()}>{text}</Text>
314
+ ))}
315
+ </View>
316
+ );
317
+ }
318
+
319
+ TaskTrigger.displayName = 'Task.Trigger';
320
+ TaskContent.displayName = 'Task.Content';
321
+ TaskItem.displayName = 'Task.Item';
322
+ TaskFile.displayName = 'Task.File';
323
+
324
+ export const Task = Object.assign(TaskRoot, {
325
+ Trigger: TaskTrigger,
326
+ Content: TaskContent,
327
+ Item: TaskItem,
328
+ File: TaskFile,
329
+ });