paseo-plugin-genui 0.0.0-stage → 0.1.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 (375) hide show
  1. package/LICENSE +202 -0
  2. package/OVERVIEW.md +21 -0
  3. package/README.md +77 -2
  4. package/assets/fonts/Excalifont-OFL.txt +109 -0
  5. package/assets/fonts/Excalifont-Regular-latin.woff2 +0 -0
  6. package/assets/icon.png +0 -0
  7. package/assets/icon.svg +1 -0
  8. package/assets/screenshot-chat.png +0 -0
  9. package/assets/screenshot-gallery.png +0 -0
  10. package/assets/screenshot-playground.png +0 -0
  11. package/client/components/content.tsx +468 -0
  12. package/client/components/data.tsx +967 -0
  13. package/client/components/dev.tsx +998 -0
  14. package/client/components/diagram.tsx +513 -0
  15. package/client/components/inputs.tsx +994 -0
  16. package/client/components/layout.tsx +239 -0
  17. package/client/components/media.tsx +428 -0
  18. package/client/components/project.tsx +641 -0
  19. package/client/components/widgets.tsx +836 -0
  20. package/client/gallery.tsx +449 -0
  21. package/client/live-ui.tsx +525 -0
  22. package/client/loaders.ts +342 -0
  23. package/client/markdown.tsx +172 -0
  24. package/client/node-renderer.tsx +149 -0
  25. package/client/render.tsx +186 -0
  26. package/client/runtime.ts +103 -0
  27. package/client/settings.tsx +57 -0
  28. package/client/web.ts +186 -0
  29. package/gallery/Acceptance-baseline.ui +1 -0
  30. package/gallery/Acceptance-checklist.ui +14 -0
  31. package/gallery/Acceptance-evidence.ui +9 -0
  32. package/gallery/Acceptance.ui +1 -0
  33. package/gallery/Badge.ui +3 -0
  34. package/gallery/Benchmark-context.ui +8 -0
  35. package/gallery/Benchmark-hyperfine.ui +1 -0
  36. package/gallery/Benchmark-runs-list.ui +9 -0
  37. package/gallery/Benchmark-runs.ui +1 -0
  38. package/gallery/Benchmark-vitest.ui +1 -0
  39. package/gallery/Benchmark.ui +9 -0
  40. package/gallery/Board-bb-tasks.ui +1 -0
  41. package/gallery/Board-gh-issues.ui +1 -0
  42. package/gallery/Board-gh-prs.ui +1 -0
  43. package/gallery/Board.ui +12 -0
  44. package/gallery/Button-destructive.ui +4 -0
  45. package/gallery/Button.ui +5 -0
  46. package/gallery/Calendar.ui +15 -0
  47. package/gallery/CalendarHeatmap-short.ui +11 -0
  48. package/gallery/CalendarHeatmap.ui +4 -0
  49. package/gallery/Callout.ui +4 -0
  50. package/gallery/Card.ui +4 -0
  51. package/gallery/Carousel.ui +8 -0
  52. package/gallery/Changes-paths.ui +1 -0
  53. package/gallery/Changes-uncommitted.ui +1 -0
  54. package/gallery/Changes.ui +1 -0
  55. package/gallery/Chart-area.ui +3 -0
  56. package/gallery/Chart-funnel.ui +3 -0
  57. package/gallery/Chart-heatmap.ui +11 -0
  58. package/gallery/Chart-pie.ui +3 -0
  59. package/gallery/Chart-radar.ui +3 -0
  60. package/gallery/Chart-scatter.ui +4 -0
  61. package/gallery/Chart-stacked.ui +5 -0
  62. package/gallery/Chart.ui +3 -0
  63. package/gallery/Checklist.ui +6 -0
  64. package/gallery/Choice.ui +8 -0
  65. package/gallery/Code.ui +3 -0
  66. package/gallery/Commits-limit.ui +1 -0
  67. package/gallery/Commits.ui +1 -0
  68. package/gallery/Compare-conditions.ui +4 -0
  69. package/gallery/Compare-three.ui +7 -0
  70. package/gallery/Compare.ui +4 -0
  71. package/gallery/Coverage-go.ui +1 -0
  72. package/gallery/Coverage-lcov.ui +1 -0
  73. package/gallery/Coverage-python.ui +1 -0
  74. package/gallery/Coverage.ui +1 -0
  75. package/gallery/DateInput.ui +5 -0
  76. package/gallery/Decision-percent.ui +10 -0
  77. package/gallery/Decision.ui +12 -0
  78. package/gallery/Dependencies-audit.ui +1 -0
  79. package/gallery/Dependencies-both.ui +1 -0
  80. package/gallery/Dependencies-list.ui +7 -0
  81. package/gallery/Dependencies-pip.ui +1 -0
  82. package/gallery/Dependencies-pnpm.ui +1 -0
  83. package/gallery/Dependencies.ui +1 -0
  84. package/gallery/Details.ui +4 -0
  85. package/gallery/Diagram-flow.ui +16 -0
  86. package/gallery/Diagram-sequence.ui +15 -0
  87. package/gallery/Diagram.ui +16 -0
  88. package/gallery/Diff.ui +3 -0
  89. package/gallery/Divider.ui +5 -0
  90. package/gallery/EditableTable.ui +9 -0
  91. package/gallery/Entity-list.ui +6 -0
  92. package/gallery/Entity.ui +8 -0
  93. package/gallery/Environments.ui +1 -0
  94. package/gallery/Estimate-points.ui +6 -0
  95. package/gallery/Estimate.ui +8 -0
  96. package/gallery/File.ui +4 -0
  97. package/gallery/Findings-biome.ui +1 -0
  98. package/gallery/Findings-eslint.ui +1 -0
  99. package/gallery/Findings-oxlint.ui +1 -0
  100. package/gallery/Findings-review.ui +1 -0
  101. package/gallery/Findings-ruff.ui +1 -0
  102. package/gallery/Findings-sarif.ui +1 -0
  103. package/gallery/Findings-ship.ui +11 -0
  104. package/gallery/Findings-tsc.ui +1 -0
  105. package/gallery/Findings.ui +7 -0
  106. package/gallery/Flashcards.ui +3 -0
  107. package/gallery/FollowUps.ui +4 -0
  108. package/gallery/Form.ui +8 -0
  109. package/gallery/Gallery-links.ui +8 -0
  110. package/gallery/Gallery-strip.ui +14 -0
  111. package/gallery/Gallery.ui +10 -0
  112. package/gallery/Gauge.ui +7 -0
  113. package/gallery/Grid.ui +8 -0
  114. package/gallery/Hotspots-photo.ui +8 -0
  115. package/gallery/Hotspots.ui +11 -0
  116. package/gallery/Icon.ui +5 -0
  117. package/gallery/Image.ui +4 -0
  118. package/gallery/ImageSearch-aspects.ui +6 -0
  119. package/gallery/ImageSearch.ui +4 -0
  120. package/gallery/Input.ui +5 -0
  121. package/gallery/Json-api.ui +1 -0
  122. package/gallery/Json.ui +8 -0
  123. package/gallery/KeyValue.ui +3 -0
  124. package/gallery/List-dots.ui +8 -0
  125. package/gallery/List.ui +8 -0
  126. package/gallery/ListEditor.ui +5 -0
  127. package/gallery/Logs-jsonl.ui +1 -0
  128. package/gallery/Logs-lines.ui +11 -0
  129. package/gallery/Logs-python.ui +1 -0
  130. package/gallery/Logs.ui +1 -0
  131. package/gallery/Map-city.ui +8 -0
  132. package/gallery/Map.ui +8 -0
  133. package/gallery/Metric.ui +3 -0
  134. package/gallery/OptionCards-multi.ui +10 -0
  135. package/gallery/OptionCards.ui +10 -0
  136. package/gallery/Pipeline-checks.ui +1 -0
  137. package/gallery/Pipeline-passed.ui +1 -0
  138. package/gallery/Pipeline-running.ui +1 -0
  139. package/gallery/Pipeline-stages.ui +13 -0
  140. package/gallery/Pipeline.ui +1 -0
  141. package/gallery/Plan.ui +9 -0
  142. package/gallery/Progress.ui +4 -0
  143. package/gallery/Provenance-deploy.ui +6 -0
  144. package/gallery/Provenance-no-receipt.ui +1 -0
  145. package/gallery/Provenance.ui +1 -0
  146. package/gallery/PullRequest.ui +1 -0
  147. package/gallery/Quiz.ui +6 -0
  148. package/gallery/Radio.ui +5 -0
  149. package/gallery/Rating.ui +5 -0
  150. package/gallery/Roadmap-sprint.ui +8 -0
  151. package/gallery/Roadmap-year.ui +9 -0
  152. package/gallery/Roadmap.ui +12 -0
  153. package/gallery/Row.ui +3 -0
  154. package/gallery/SegmentedBar-status.ui +8 -0
  155. package/gallery/SegmentedBar.ui +8 -0
  156. package/gallery/Select.ui +5 -0
  157. package/gallery/Slider.ui +7 -0
  158. package/gallery/Snippet.ui +3 -0
  159. package/gallery/Sources-chips.ui +4 -0
  160. package/gallery/Sources.ui +8 -0
  161. package/gallery/Sparkline.ui +6 -0
  162. package/gallery/Stack.ui +7 -0
  163. package/gallery/StackTrace-aggregate.ui +1 -0
  164. package/gallery/StackTrace-browser.ui +1 -0
  165. package/gallery/StackTrace-go.ui +1 -0
  166. package/gallery/StackTrace-java.ui +1 -0
  167. package/gallery/StackTrace-python.ui +1 -0
  168. package/gallery/StackTrace-rust.ui +1 -0
  169. package/gallery/StackTrace.ui +1 -0
  170. package/gallery/Status.ui +9 -0
  171. package/gallery/Stepper.ui +5 -0
  172. package/gallery/Steps.ui +8 -0
  173. package/gallery/Swatches-long-names.ui +4 -0
  174. package/gallery/Swatches-picker.ui +5 -0
  175. package/gallery/Swatches.ui +10 -0
  176. package/gallery/Table.ui +7 -0
  177. package/gallery/Tabs.ui +8 -0
  178. package/gallery/Tests-junit.ui +1 -0
  179. package/gallery/Tests-vitest-junit.ui +1 -0
  180. package/gallery/Tests-vitest.ui +1 -0
  181. package/gallery/Tests.ui +9 -0
  182. package/gallery/Text.ui +4 -0
  183. package/gallery/TextArea.ui +5 -0
  184. package/gallery/Threads-ids.ui +1 -0
  185. package/gallery/Threads.ui +1 -0
  186. package/gallery/Timeline.ui +8 -0
  187. package/gallery/Timer.ui +4 -0
  188. package/gallery/Toggle.ui +5 -0
  189. package/gallery/Transcript-text.ui +1 -0
  190. package/gallery/Transcript-untimed.ui +1 -0
  191. package/gallery/Transcript-whisper.ui +1 -0
  192. package/gallery/Transcript.ui +1 -0
  193. package/gallery/Tree.ui +3 -0
  194. package/gallery/Treemap-du.ui +1 -0
  195. package/gallery/Treemap-esbuild.ui +1 -0
  196. package/gallery/Treemap-tree.ui +7 -0
  197. package/gallery/Treemap.ui +18 -0
  198. package/gallery/Walkthrough.ui +9 -0
  199. package/gallery/Waterfall-har.ui +1 -0
  200. package/gallery/Waterfall-trace.ui +1 -0
  201. package/gallery/Waterfall.ui +11 -0
  202. package/gallery/data/acceptance/coil/viewport-lifecycle.json +25 -0
  203. package/gallery/data/acceptance/coil-gate.json +153 -0
  204. package/gallery/data/acceptance/orbit-baseline.json +163 -0
  205. package/gallery/data/acceptance/shots/gate-cell-opened.png +0 -0
  206. package/gallery/data/acceptance/shots/gate-light.png +0 -0
  207. package/gallery/data/acceptance/shots/gate-phone-dark.png +0 -0
  208. package/gallery/data/activity.json +1 -0
  209. package/gallery/data/bb-tasks-orb.json +420 -0
  210. package/gallery/data/biome.json +229 -0
  211. package/gallery/data/compare/status-after.png +0 -0
  212. package/gallery/data/compare/status-before.png +0 -0
  213. package/gallery/data/compare/status-dark-2x.png +0 -0
  214. package/gallery/data/compare/status-rerender.png +0 -0
  215. package/gallery/data/coverage-py.json +1 -0
  216. package/gallery/data/coverage-summary.json +1 -0
  217. package/gallery/data/coverage.out +23 -0
  218. package/gallery/data/deploys.json +1 -0
  219. package/gallery/data/device-runs.json +285 -0
  220. package/gallery/data/du-node-modules.txt +359 -0
  221. package/gallery/data/esbuild-meta.json +292 -0
  222. package/gallery/data/eslint.json +266 -0
  223. package/gallery/data/eslint.sarif +343 -0
  224. package/gallery/data/findings-review.json +76 -0
  225. package/gallery/data/gh-issues-nextjs.json +293 -0
  226. package/gallery/data/gh-pr-checks.json +272 -0
  227. package/gallery/data/gh-prs-nextjs.json +419 -0
  228. package/gallery/data/gh-run-failed.json +1 -0
  229. package/gallery/data/gh-run-passed.json +1 -0
  230. package/gallery/data/gh-run-running.json +1 -0
  231. package/gallery/data/github-release.json +57 -0
  232. package/gallery/data/hyperfine-startup.json +187 -0
  233. package/gallery/data/lcov.info +234 -0
  234. package/gallery/data/logs/media-sync.log +127 -0
  235. package/gallery/data/logs/thumbnailer.jsonl +17 -0
  236. package/gallery/data/logs/vitest-run.log +89 -0
  237. package/gallery/data/npm-audit.json +672 -0
  238. package/gallery/data/npm-outdated.json +179 -0
  239. package/gallery/data/oxlint.json +156 -0
  240. package/gallery/data/pip-outdated.json +68 -0
  241. package/gallery/data/pnpm-outdated.json +58 -0
  242. package/gallery/data/provenance/devicectl-install.json +35 -0
  243. package/gallery/data/provenance/install-timeout.log +4 -0
  244. package/gallery/data/provenance/iphone-install.json +72 -0
  245. package/gallery/data/provenance/iphone-no-receipt.json +57 -0
  246. package/gallery/data/provenance/live-home.jpg +0 -0
  247. package/gallery/data/provenance/live-probe.txt +10 -0
  248. package/gallery/data/provenance/xcodebuild-2811.log +19 -0
  249. package/gallery/data/provenance/xcodebuild-4712.log +19 -0
  250. package/gallery/data/pytest-junit.xml +64 -0
  251. package/gallery/data/python-org-trace.json +35 -0
  252. package/gallery/data/python-org.har +31 -0
  253. package/gallery/data/retry.ts +13 -0
  254. package/gallery/data/ruff.json +261 -0
  255. package/gallery/data/src/durations.py +63 -0
  256. package/gallery/data/src/json-logic.ts +156 -0
  257. package/gallery/data/src/mapMath.ts +135 -0
  258. package/gallery/data/src/semver/bump.go +73 -0
  259. package/gallery/data/traces/browser-error.json +5 -0
  260. package/gallery/data/traces/go/main.go +27 -0
  261. package/gallery/data/traces/go-panic.txt +16 -0
  262. package/gallery/data/traces/java-caused-by.txt +15 -0
  263. package/gallery/data/traces/node-aggregate.txt +30 -0
  264. package/gallery/data/traces/node-cause.txt +21 -0
  265. package/gallery/data/traces/py/deploy.json +5 -0
  266. package/gallery/data/traces/py/deploy.py +17 -0
  267. package/gallery/data/traces/py/settings.py +19 -0
  268. package/gallery/data/traces/python-chain.txt +30 -0
  269. package/gallery/data/traces/rust/main.rs +11 -0
  270. package/gallery/data/traces/rust-panic.txt +16 -0
  271. package/gallery/data/traces/shop/cart.mjs +19 -0
  272. package/gallery/data/traces/shop/pricing.mjs +12 -0
  273. package/gallery/data/traces/shop/status.mjs +9 -0
  274. package/gallery/data/traces/web/app.js +11 -0
  275. package/gallery/data/traces/web/index.html +1 -0
  276. package/gallery/data/traces/web/search.js +10 -0
  277. package/gallery/data/transcript/call-noisy.wav +0 -0
  278. package/gallery/data/transcript/call.json +362 -0
  279. package/gallery/data/transcript/call.wav +0 -0
  280. package/gallery/data/transcript/mlx-whisper-tiny.en-noisy.json +1 -0
  281. package/gallery/data/transcript/whisper-cpp-base.en-noisy.json +658 -0
  282. package/gallery/data/transcript/whisper-cpp-base.en.json +658 -0
  283. package/gallery/data/transcript/whisper-cpp-base.en.srt +16 -0
  284. package/gallery/data/transcript/whisper-cpp-tiny.en-noisy.json +619 -0
  285. package/gallery/data/tsc.txt +4 -0
  286. package/gallery/data/vitest-bench.json +119 -0
  287. package/gallery/data/vitest-junit.xml +133 -0
  288. package/gallery/data/vitest-report.json +612 -0
  289. package/gallery/images/bell.svg +10 -0
  290. package/gallery/images/bike.svg +47 -0
  291. package/gallery/images/footer-planet.webp +0 -0
  292. package/gallery/images/front-light.svg +13 -0
  293. package/gallery/images/golden-hour.webp +0 -0
  294. package/gallery/images/option-flowers.webp +0 -0
  295. package/gallery/images/option-hilltop.webp +0 -0
  296. package/gallery/images/option-meadow.webp +0 -0
  297. package/gallery/images/option-sky.webp +0 -0
  298. package/gallery/images/u-lock.svg +10 -0
  299. package/index.client.tsx +164 -0
  300. package/index.server.ts +216 -0
  301. package/package.json +50 -4
  302. package/paseo-plugin.json +14 -0
  303. package/server/data-source.ts +407 -0
  304. package/server/project.ts +981 -0
  305. package/server/web/address.ts +57 -0
  306. package/server/web/cache.ts +85 -0
  307. package/server/web/commons.ts +126 -0
  308. package/server/web/geocode.ts +40 -0
  309. package/server/web/image.ts +35 -0
  310. package/server/web/lookups.ts +248 -0
  311. package/server/web/net.ts +210 -0
  312. package/shared/core/acceptance.ts +744 -0
  313. package/shared/core/ast.ts +76 -0
  314. package/shared/core/benchmark.ts +743 -0
  315. package/shared/core/catalog.ts +733 -0
  316. package/shared/core/compare.ts +225 -0
  317. package/shared/core/coverage.ts +456 -0
  318. package/shared/core/data.ts +334 -0
  319. package/shared/core/decisions.ts +527 -0
  320. package/shared/core/dependencies.ts +604 -0
  321. package/shared/core/diagnostics.ts +24 -0
  322. package/shared/core/environments.ts +212 -0
  323. package/shared/core/evaluate.ts +737 -0
  324. package/shared/core/fields.ts +66 -0
  325. package/shared/core/findings.ts +1172 -0
  326. package/shared/core/form.ts +69 -0
  327. package/shared/core/index.ts +44 -0
  328. package/shared/core/junit.ts +252 -0
  329. package/shared/core/lexer.ts +275 -0
  330. package/shared/core/logs.ts +496 -0
  331. package/shared/core/markdown.ts +2374 -0
  332. package/shared/core/parser.ts +355 -0
  333. package/shared/core/perf-units.ts +228 -0
  334. package/shared/core/pipeline.ts +679 -0
  335. package/shared/core/project.ts +472 -0
  336. package/shared/core/provenance.ts +725 -0
  337. package/shared/core/stack-trace.ts +718 -0
  338. package/shared/core/streaming.ts +136 -0
  339. package/shared/core/test-results.ts +562 -0
  340. package/shared/core/thread-activity.ts +203 -0
  341. package/shared/core/tracking.ts +887 -0
  342. package/shared/core/transcript.ts +1364 -0
  343. package/shared/core/treemap.ts +446 -0
  344. package/shared/core/types.ts +70 -0
  345. package/shared/core/validate.ts +574 -0
  346. package/shared/core/waterfall.ts +536 -0
  347. package/shared/diagram/flowchart.ts +205 -0
  348. package/shared/diagram/layout.ts +440 -0
  349. package/shared/diagram/markdown.ts +135 -0
  350. package/shared/diagram/sequence.ts +122 -0
  351. package/shared/diagram/zoom.ts +114 -0
  352. package/shared/fallback.ts +120 -0
  353. package/shared/genui.test.ts +122 -0
  354. package/shared/instructions.ts +144 -0
  355. package/shared/logic/calendarMath.ts +130 -0
  356. package/shared/logic/chartMath.ts +289 -0
  357. package/shared/logic/choose-logic.ts +196 -0
  358. package/shared/logic/compare-logic.ts +270 -0
  359. package/shared/logic/content-model.ts +227 -0
  360. package/shared/logic/environments-logic.ts +53 -0
  361. package/shared/logic/explain-logic.ts +315 -0
  362. package/shared/logic/fieldLogic.ts +138 -0
  363. package/shared/logic/gallery-search.ts +71 -0
  364. package/shared/logic/icons.ts +322 -0
  365. package/shared/logic/json-logic.ts +153 -0
  366. package/shared/logic/mapMath.ts +133 -0
  367. package/shared/logic/project-logic.ts +39 -0
  368. package/shared/logic/references-model.ts +201 -0
  369. package/shared/logic/sketch.ts +165 -0
  370. package/shared/logic/supersession.ts +77 -0
  371. package/shared/logic/values.ts +133 -0
  372. package/shared/logic/widget-logic.ts +163 -0
  373. package/shared/rpc.ts +252 -0
  374. package/shared/segment.ts +81 -0
  375. package/shared/settings.ts +12 -0
@@ -0,0 +1,468 @@
1
+ import { Icon as PaseoIcon, Modal } from "@getpaseo/plugin/client/react-native";
2
+ import { ExternalLink } from "@getpaseo/plugin/client/ui";
3
+ import React, { useContext, useEffect, useState } from "react";
4
+ import { Image as RNImage, Pressable, StyleSheet, Text as RNText, View } from "react-native";
5
+ import { keyValuePairs } from "../../shared/logic/content-model";
6
+ import { parseListRows } from "../../shared/logic/explain-logic";
7
+ import { resolveIconName } from "../../shared/logic/icons";
8
+ import { entityKind, imageRef, inlineFacts, sourceLinks } from "../../shared/logic/references-model";
9
+ import { finiteNumber, isNode, list, optionalText, text, tone, type Props } from "../../shared/logic/values";
10
+ import { MarkdownText } from "../markdown";
11
+ import { ErrorLine, RenderNode } from "../node-renderer";
12
+ import { InTableContext, toneColor, toneMutedColor, useUiRuntime, type ImageUrl } from "../runtime";
13
+
14
+ export function Text({ props }: { props: Props }) {
15
+ const { theme, compact } = useUiRuntime();
16
+ const content = text(props.text);
17
+ const t = tone(props.tone);
18
+ const color = t === "neutral" ? theme.colors.foreground : toneColor(theme, t);
19
+ return <MarkdownText text={content} theme={theme} compact={compact} color={color} />;
20
+ }
21
+
22
+ export function Callout({ props }: { props: Props }) {
23
+ const { theme, compact } = useUiRuntime();
24
+ const body = text(props.text);
25
+ const title = optionalText(props.title);
26
+ const t = tone(props.tone);
27
+ const borderColor = t === "neutral" ? theme.colors.border : toneColor(theme, t);
28
+
29
+ return (
30
+ <View
31
+ style={{
32
+ borderRadius: 8,
33
+ borderWidth: StyleSheet.hairlineWidth,
34
+ borderColor,
35
+ borderLeftWidth: 3,
36
+ borderLeftColor: borderColor,
37
+ backgroundColor: theme.colors.surface2,
38
+ paddingHorizontal: 12,
39
+ paddingVertical: 10,
40
+ gap: 4,
41
+ }}
42
+ >
43
+ {title ? (
44
+ <RNText style={{ color: toneColor(theme, t), fontSize: 13, fontWeight: "600" }}>
45
+ {title}
46
+ </RNText>
47
+ ) : null}
48
+ <MarkdownText text={body} theme={theme} compact={compact} />
49
+ </View>
50
+ );
51
+ }
52
+
53
+ export function Metric({ props }: { props: Props }) {
54
+ const { theme } = useUiRuntime();
55
+ const label = text(props.label);
56
+ const value = text(props.value);
57
+ const note = optionalText(props.note);
58
+ const t = tone(props.tone);
59
+
60
+ return (
61
+ <View
62
+ style={{
63
+ borderRadius: 8,
64
+ borderWidth: StyleSheet.hairlineWidth,
65
+ borderColor: theme.colors.border,
66
+ backgroundColor: theme.colors.surface2,
67
+ paddingHorizontal: 12,
68
+ paddingVertical: 10,
69
+ gap: 2,
70
+ }}
71
+ >
72
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 11.5 }}>{label}</RNText>
73
+ <RNText
74
+ selectable
75
+ style={{
76
+ color: toneColor(theme, t),
77
+ fontSize: 20,
78
+ fontWeight: "700",
79
+ }}
80
+ >
81
+ {value}
82
+ </RNText>
83
+ {note ? (
84
+ <RNText style={{ color: toneMutedColor(theme, t), fontSize: 11.5 }}>{note}</RNText>
85
+ ) : null}
86
+ </View>
87
+ );
88
+ }
89
+
90
+ export function Badge({ props }: { props: Props }) {
91
+ const { theme } = useUiRuntime();
92
+ const inTable = useContext(InTableContext);
93
+ const label = text(props.text);
94
+ const t = tone(props.tone);
95
+ const color = t === "neutral" ? theme.colors.foregroundMuted : toneColor(theme, t);
96
+
97
+ return (
98
+ <View
99
+ style={{
100
+ alignSelf: "flex-start",
101
+ flexDirection: "row",
102
+ alignItems: "center",
103
+ gap: 5,
104
+ paddingHorizontal: inTable ? 6 : 8,
105
+ paddingVertical: 2,
106
+ borderRadius: 999,
107
+ backgroundColor: theme.colors.surface2,
108
+ borderWidth: StyleSheet.hairlineWidth,
109
+ borderColor: t === "neutral" ? theme.colors.border : color,
110
+ }}
111
+ >
112
+ <View style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: color }} />
113
+ <RNText style={{ color, fontSize: 11.5, fontWeight: "500" }}>{label}</RNText>
114
+ </View>
115
+ );
116
+ }
117
+
118
+ export function Icon({ props }: { props: Props }) {
119
+ const { theme } = useUiRuntime();
120
+ const rawName = text(props.name);
121
+ const resolved = resolveIconName(rawName) ?? rawName;
122
+ const t = tone(props.tone);
123
+ return <PaseoIcon name={resolved} size={16} color={toneColor(theme, t)} />;
124
+ }
125
+
126
+ export function KeyValue({ props }: { props: Props }) {
127
+ const { theme } = useUiRuntime();
128
+ const pairs = keyValuePairs(props.pairs);
129
+ if (pairs === null) return <ErrorLine message="KeyValue expects an object or [[key, value]] list" />;
130
+
131
+ return (
132
+ <View
133
+ style={{
134
+ borderRadius: 8,
135
+ borderWidth: StyleSheet.hairlineWidth,
136
+ borderColor: theme.colors.border,
137
+ overflow: "hidden",
138
+ }}
139
+ >
140
+ {pairs.map(([k, v], idx) => (
141
+ <View
142
+ key={`${k}:${idx}`}
143
+ style={{
144
+ flexDirection: "row",
145
+ justifyContent: "space-between",
146
+ alignItems: "center",
147
+ gap: 12,
148
+ paddingHorizontal: 12,
149
+ paddingVertical: 8,
150
+ borderTopWidth: idx === 0 ? 0 : StyleSheet.hairlineWidth,
151
+ borderTopColor: theme.colors.border,
152
+ }}
153
+ >
154
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12.5, flexShrink: 0 }}>
155
+ {k}
156
+ </RNText>
157
+ {isNode(v) ? (
158
+ <RenderNode node={v} />
159
+ ) : (
160
+ <RNText
161
+ selectable
162
+ style={{ color: theme.colors.foreground, fontSize: 12.5, flex: 1, textAlign: "right" }}
163
+ >
164
+ {text(v)}
165
+ </RNText>
166
+ )}
167
+ </View>
168
+ ))}
169
+ </View>
170
+ );
171
+ }
172
+
173
+ export function LoadedImageView({
174
+ src,
175
+ alt,
176
+ height = 200,
177
+ }: {
178
+ src: string;
179
+ alt?: string | null;
180
+ height?: number;
181
+ }) {
182
+ const { theme, loadImage } = useUiRuntime();
183
+ const [state, setState] = useState<ImageUrl | null>(null);
184
+
185
+ useEffect(() => {
186
+ if (!loadImage || !src) return;
187
+ let live = true;
188
+ void loadImage(src).then((res) => {
189
+ if (live) setState(res);
190
+ });
191
+ return () => {
192
+ live = false;
193
+ };
194
+ }, [loadImage, src]);
195
+
196
+ if (!src) return <ErrorLine message="Image src is empty" />;
197
+ if (state === null) {
198
+ return (
199
+ <View
200
+ style={{
201
+ height,
202
+ borderRadius: 8,
203
+ backgroundColor: theme.colors.surface2,
204
+ alignItems: "center",
205
+ justifyContent: "center",
206
+ }}
207
+ >
208
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12 }}>Loading image…</RNText>
209
+ </View>
210
+ );
211
+ }
212
+ if (!state.ok) {
213
+ return (
214
+ <View
215
+ style={{
216
+ borderRadius: 8,
217
+ borderWidth: StyleSheet.hairlineWidth,
218
+ borderColor: theme.colors.border,
219
+ backgroundColor: theme.colors.surface2,
220
+ padding: 10,
221
+ }}
222
+ >
223
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12 }}>
224
+ {state.error} ({src})
225
+ </RNText>
226
+ </View>
227
+ );
228
+ }
229
+
230
+ return (
231
+ <View style={{ gap: 4 }}>
232
+ <RNImage
233
+ source={{ uri: state.url }}
234
+ accessibilityLabel={alt ?? src}
235
+ resizeMode="contain"
236
+ style={{
237
+ width: "100%",
238
+ height,
239
+ borderRadius: 8,
240
+ backgroundColor: theme.colors.surface2,
241
+ }}
242
+ />
243
+ {alt ? (
244
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 11.5 }}>{alt}</RNText>
245
+ ) : null}
246
+ </View>
247
+ );
248
+ }
249
+
250
+ export function Image({ props }: { props: Props }) {
251
+ const src = text(props.src);
252
+ const alt = optionalText(props.alt);
253
+ return <LoadedImageView src={src} alt={alt} height={220} />;
254
+ }
255
+
256
+ export function File({ props }: { props: Props }) {
257
+ const { theme, openFile } = useUiRuntime();
258
+ const filePath = text(props.path);
259
+ const line = finiteNumber(props.line);
260
+ const label = line !== null ? `${filePath}:${Math.round(line)}` : filePath;
261
+
262
+ return (
263
+ <Pressable
264
+ accessibilityRole="button"
265
+ onPress={() => {
266
+ if (openFile) void openFile(filePath, line ?? undefined);
267
+ }}
268
+ style={{
269
+ alignSelf: "flex-start",
270
+ flexDirection: "row",
271
+ alignItems: "center",
272
+ gap: 6,
273
+ paddingHorizontal: 8,
274
+ paddingVertical: 4,
275
+ borderRadius: 6,
276
+ backgroundColor: theme.colors.surface2,
277
+ borderWidth: StyleSheet.hairlineWidth,
278
+ borderColor: theme.colors.border,
279
+ }}
280
+ >
281
+ <PaseoIcon name="FileText" size={13} color={theme.colors.accent} />
282
+ <RNText selectable style={{ color: theme.colors.foreground, fontFamily: "monospace", fontSize: 12 }}>
283
+ {label}
284
+ </RNText>
285
+ </Pressable>
286
+ );
287
+ }
288
+
289
+ export function Entity({ props }: { props: Props }) {
290
+ const { theme } = useUiRuntime();
291
+ const [open, setOpen] = useState(false);
292
+ const name = text(props.name);
293
+ const kind = entityKind(props.kind);
294
+ const note = optionalText(props.note);
295
+ const url = optionalText(props.url);
296
+ const imgRef = imageRef(text(props.image));
297
+ const rawPairs = keyValuePairs(props.facts) ?? [];
298
+ const previewFacts = inlineFacts(rawPairs, 4);
299
+
300
+ return (
301
+ <View
302
+ style={{
303
+ borderRadius: 10,
304
+ borderWidth: StyleSheet.hairlineWidth,
305
+ borderColor: theme.colors.border,
306
+ backgroundColor: theme.colors.surface2,
307
+ padding: 12,
308
+ gap: 8,
309
+ }}
310
+ >
311
+ {imgRef.kind === "load" ? <LoadedImageView src={imgRef.src} alt={name} height={140} /> : null}
312
+ <View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
313
+ <RNText selectable style={{ color: theme.colors.foreground, fontSize: 14, fontWeight: "600", flex: 1 }}>
314
+ {name}
315
+ </RNText>
316
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 11, textTransform: "uppercase" }}>
317
+ {kind}
318
+ </RNText>
319
+ </View>
320
+ {note ? <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12.5 }}>{note}</RNText> : null}
321
+ {previewFacts.length > 0 ? (
322
+ <View style={{ gap: 4 }}>
323
+ {previewFacts.map((fact, idx) => (
324
+ <View key={idx} style={{ flexDirection: "row", justifyContent: "space-between", gap: 8 }}>
325
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12 }}>{fact.label}</RNText>
326
+ <RNText selectable style={{ color: theme.colors.foreground, fontSize: 12, fontWeight: "500" }}>
327
+ {fact.value}
328
+ </RNText>
329
+ </View>
330
+ ))}
331
+ {rawPairs.length > 4 ? (
332
+ <Pressable onPress={() => setOpen(true)}>
333
+ <RNText style={{ color: theme.colors.accent, fontSize: 12 }}>
334
+ View all {rawPairs.length} facts
335
+ </RNText>
336
+ </Pressable>
337
+ ) : null}
338
+ </View>
339
+ ) : null}
340
+ {url && /^https?:\/\//i.test(url) ? (
341
+ <ExternalLink href={url}>
342
+ <RNText style={{ color: theme.colors.accent, fontSize: 12 }}>{url}</RNText>
343
+ </ExternalLink>
344
+ ) : null}
345
+ {open ? (
346
+ <Modal title={name} open={open} onOpenChange={setOpen}>
347
+ <Modal.Content>
348
+ <View style={{ gap: 8 }}>
349
+ {rawPairs.map(([label, val], idx) => (
350
+ <View key={idx} style={{ flexDirection: "row", justifyContent: "space-between", gap: 12 }}>
351
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 13 }}>{label}</RNText>
352
+ <RNText selectable style={{ color: theme.colors.foreground, fontSize: 13 }}>
353
+ {text(val)}
354
+ </RNText>
355
+ </View>
356
+ ))}
357
+ </View>
358
+ </Modal.Content>
359
+ </Modal>
360
+ ) : null}
361
+ </View>
362
+ );
363
+ }
364
+
365
+ export function Sources({ props }: { props: Props }) {
366
+ const { theme } = useUiRuntime();
367
+ const links = sourceLinks(list(props.links));
368
+
369
+ return (
370
+ <View style={{ gap: 6 }}>
371
+ {links.map((item, idx) =>
372
+ item.ok ? (
373
+ <View
374
+ key={`${item.href}:${idx}`}
375
+ style={{
376
+ borderRadius: 8,
377
+ borderWidth: StyleSheet.hairlineWidth,
378
+ borderColor: theme.colors.border,
379
+ backgroundColor: theme.colors.surface2,
380
+ paddingHorizontal: 10,
381
+ paddingVertical: 8,
382
+ gap: 2,
383
+ }}
384
+ >
385
+ <ExternalLink href={item.href}>
386
+ <RNText style={{ color: theme.colors.accent, fontSize: 13, fontWeight: "500" }}>
387
+ [{item.number}] {item.title}
388
+ </RNText>
389
+ </ExternalLink>
390
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 11 }}>{item.domain}</RNText>
391
+ {item.note ? (
392
+ <RNText style={{ color: theme.colors.foreground, fontSize: 12 }}>{item.note}</RNText>
393
+ ) : null}
394
+ </View>
395
+ ) : (
396
+ <ErrorLine key={idx} message={item.error} />
397
+ ),
398
+ )}
399
+ </View>
400
+ );
401
+ }
402
+
403
+ export function List({ props }: { props: Props }) {
404
+ const { theme, run } = useUiRuntime();
405
+ const rows = parseListRows(props.items);
406
+
407
+ return (
408
+ <View
409
+ style={{
410
+ borderRadius: 8,
411
+ borderWidth: StyleSheet.hairlineWidth,
412
+ borderColor: theme.colors.border,
413
+ overflow: "hidden",
414
+ }}
415
+ >
416
+ {rows.map((item, idx) => {
417
+ const iconName = item.icon ? resolveIconName(item.icon) ?? item.icon : null;
418
+ const t = item.tone ?? "neutral";
419
+ const rowContent = (
420
+ <View
421
+ style={{
422
+ flexDirection: "row",
423
+ alignItems: "center",
424
+ justifyContent: "space-between",
425
+ gap: 10,
426
+ paddingHorizontal: 12,
427
+ paddingVertical: 9,
428
+ borderTopWidth: idx === 0 ? 0 : StyleSheet.hairlineWidth,
429
+ borderTopColor: theme.colors.border,
430
+ }}
431
+ >
432
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, flex: 1 }}>
433
+ {iconName ? (
434
+ <PaseoIcon name={iconName} size={15} color={toneColor(theme, t)} />
435
+ ) : null}
436
+ <View style={{ flex: 1, gap: 2 }}>
437
+ <RNText style={{ color: theme.colors.foreground, fontSize: 13, fontWeight: "500" }}>
438
+ {item.title}
439
+ </RNText>
440
+ {item.detail ? (
441
+ <RNText style={{ color: theme.colors.foregroundMuted, fontSize: 12 }}>
442
+ {item.detail}
443
+ </RNText>
444
+ ) : null}
445
+ </View>
446
+ </View>
447
+ {item.value !== null ? (
448
+ isNode(item.value) ? (
449
+ <RenderNode node={item.value} />
450
+ ) : (
451
+ <RNText style={{ color: toneColor(theme, t), fontSize: 12.5, fontWeight: "500" }}>
452
+ {text(item.value)}
453
+ </RNText>
454
+ )
455
+ ) : null}
456
+ </View>
457
+ );
458
+ return item.actions.length > 0 ? (
459
+ <Pressable key={idx} accessibilityRole="button" onPress={() => void run(item.actions)}>
460
+ {rowContent}
461
+ </Pressable>
462
+ ) : (
463
+ <View key={idx}>{rowContent}</View>
464
+ );
465
+ })}
466
+ </View>
467
+ );
468
+ }