@theoremjs/react 0.0.0-stage → 0.3.1

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 (320) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -2
  3. package/client/attachment-hover-preview.d.ts +11 -0
  4. package/client/attachment-hover-preview.js +36 -0
  5. package/client/audio-level.d.ts +5 -0
  6. package/client/audio-level.js +24 -0
  7. package/client/composer-attachments.d.ts +30 -0
  8. package/client/composer-attachments.js +33 -0
  9. package/client/composer-drawer.d.ts +19 -0
  10. package/client/composer-drawer.js +15 -0
  11. package/client/composer-hints.d.ts +25 -0
  12. package/client/composer-hints.js +20 -0
  13. package/client/composer-primary.d.ts +16 -0
  14. package/client/composer-primary.js +25 -0
  15. package/client/decision-images.d.ts +76 -0
  16. package/client/decision-images.js +139 -0
  17. package/client/decision-transport.d.ts +70 -0
  18. package/client/decision-transport.js +175 -0
  19. package/client/decode-composer-draft.d.ts +11 -0
  20. package/client/decode-composer-draft.js +12 -0
  21. package/client/embedding-input.d.ts +84 -0
  22. package/client/embedding-input.js +194 -0
  23. package/client/embedding-space.d.ts +82 -0
  24. package/client/embedding-space.js +389 -0
  25. package/client/embedding-transport.d.ts +40 -0
  26. package/client/embedding-transport.js +71 -0
  27. package/client/encode-composer-draft.d.ts +9 -0
  28. package/client/encode-composer-draft.js +34 -0
  29. package/client/encode-files.d.ts +18 -0
  30. package/client/encode-files.js +54 -0
  31. package/client/failure.d.ts +29 -0
  32. package/client/failure.js +34 -0
  33. package/client/generation-selection.d.ts +15 -0
  34. package/client/generation-selection.js +24 -0
  35. package/client/host-call.d.ts +51 -0
  36. package/client/host-call.js +47 -0
  37. package/client/host-transport.d.ts +70 -0
  38. package/client/host-transport.js +75 -0
  39. package/client/image-output.d.ts +6 -0
  40. package/client/image-output.js +13 -0
  41. package/client/index.d.ts +28 -0
  42. package/client/index.js +22 -0
  43. package/client/ink-waveform.d.ts +17 -0
  44. package/client/ink-waveform.js +68 -0
  45. package/client/live/apply-live-turn-tool-event.d.ts +14 -0
  46. package/client/live/apply-live-turn-tool-event.js +39 -0
  47. package/client/live/live-captions.d.ts +53 -0
  48. package/client/live/live-captions.js +174 -0
  49. package/client/live/live-mic-forward.d.ts +30 -0
  50. package/client/live/live-mic-forward.js +46 -0
  51. package/client/live/live-page-tool.d.ts +34 -0
  52. package/client/live/live-page-tool.js +11 -0
  53. package/client/live/live-state.d.ts +13 -0
  54. package/client/live/live-state.js +22 -0
  55. package/client/live/live-tool.d.ts +11 -0
  56. package/client/live/live-tool.js +1 -0
  57. package/client/live/live-video.d.ts +10 -0
  58. package/client/live/live-video.js +71 -0
  59. package/client/live/run-live-tool-call.d.ts +22 -0
  60. package/client/live/run-live-tool-call.js +70 -0
  61. package/client/live/use-live-caption-log.d.ts +17 -0
  62. package/client/live/use-live-caption-log.js +51 -0
  63. package/client/live-client.d.ts +189 -0
  64. package/client/live-client.js +829 -0
  65. package/client/live-errors.d.ts +2 -0
  66. package/client/live-errors.js +9 -0
  67. package/client/live-messages.d.ts +148 -0
  68. package/client/live-messages.js +100 -0
  69. package/client/mic-capture.d.ts +4 -0
  70. package/client/mic-capture.js +6 -0
  71. package/client/oauth-popup.d.ts +26 -0
  72. package/client/oauth-popup.js +30 -0
  73. package/client/page-dispatch.d.ts +15 -0
  74. package/client/page-dispatch.js +63 -0
  75. package/client/pcm-downsample.d.ts +4 -0
  76. package/client/pcm-downsample.js +45 -0
  77. package/client/run-commit.d.ts +34 -0
  78. package/client/run-commit.js +49 -0
  79. package/client/run-session.d.ts +79 -0
  80. package/client/run-session.js +207 -0
  81. package/client/schema-fields.d.ts +63 -0
  82. package/client/schema-fields.js +199 -0
  83. package/client/shaped-data.d.ts +118 -0
  84. package/client/shaped-data.js +343 -0
  85. package/client/source-citations.d.ts +13 -0
  86. package/client/source-citations.js +56 -0
  87. package/client/space-geometry.d.ts +39 -0
  88. package/client/space-geometry.js +183 -0
  89. package/client/state-fields.d.ts +16 -0
  90. package/client/state-fields.js +44 -0
  91. package/client/tool-result.d.ts +78 -0
  92. package/client/tool-result.js +293 -0
  93. package/client/tool-resume.d.ts +50 -0
  94. package/client/tool-resume.js +14 -0
  95. package/client/trace-feed.d.ts +13 -0
  96. package/client/trace-feed.js +18 -0
  97. package/client/trace-story.d.ts +163 -0
  98. package/client/trace-story.js +440 -0
  99. package/client/trace-view.d.ts +130 -0
  100. package/client/trace-view.js +333 -0
  101. package/client/transcript-groups.d.ts +140 -0
  102. package/client/transcript-groups.js +217 -0
  103. package/client/transcript-schema.d.ts +10 -0
  104. package/client/transcript-schema.js +120 -0
  105. package/client/transcript-scroll.d.ts +7 -0
  106. package/client/transcript-scroll.js +6 -0
  107. package/client/transport.d.ts +264 -0
  108. package/client/transport.js +330 -0
  109. package/client/turn-client.d.ts +44 -0
  110. package/client/turn-client.js +88 -0
  111. package/client/voice-label.d.ts +2 -0
  112. package/client/voice-label.js +15 -0
  113. package/client/voice-recorder.d.ts +27 -0
  114. package/client/voice-recorder.js +162 -0
  115. package/client/wire-line.d.ts +56 -0
  116. package/client/wire-line.js +90 -0
  117. package/client/worklets/mic-capture.js +16 -0
  118. package/components/InkWaveform.d.ts +21 -0
  119. package/components/InkWaveform.js +131 -0
  120. package/components/live/use-live-runner-controls.d.ts +47 -0
  121. package/components/live/use-live-runner-controls.js +96 -0
  122. package/components/live/use-live-runner-model.d.ts +49 -0
  123. package/components/live/use-live-runner-model.js +190 -0
  124. package/components/live/use-live-runner-ui.d.ts +67 -0
  125. package/components/live/use-live-runner-ui.js +167 -0
  126. package/components/live/use-live-session-client.d.ts +41 -0
  127. package/components/live/use-live-session-client.js +126 -0
  128. package/components/use-composer-voice.d.ts +28 -0
  129. package/components/use-composer-voice.js +90 -0
  130. package/components/use-editor-selection.d.ts +3 -0
  131. package/components/use-editor-selection.js +19 -0
  132. package/components/use-voice-playback.d.ts +17 -0
  133. package/components/use-voice-playback.js +68 -0
  134. package/hooks/use-described.d.ts +12 -0
  135. package/hooks/use-described.js +25 -0
  136. package/hooks/use-embedding-index.d.ts +27 -0
  137. package/hooks/use-embedding-index.js +43 -0
  138. package/hooks/use-one-call.d.ts +31 -0
  139. package/hooks/use-one-call.js +76 -0
  140. package/hooks/use-page-tool-warnings.d.ts +6 -0
  141. package/hooks/use-page-tool-warnings.js +25 -0
  142. package/hooks/use-second-ticker.d.ts +2 -0
  143. package/hooks/use-second-ticker.js +13 -0
  144. package/hooks/use-theorem-chat-actions.d.ts +79 -0
  145. package/hooks/use-theorem-chat-actions.js +287 -0
  146. package/hooks/use-theorem-chat-state.d.ts +66 -0
  147. package/hooks/use-theorem-chat-state.js +122 -0
  148. package/hooks/use-theorem-chat.d.ts +97 -0
  149. package/hooks/use-theorem-chat.js +528 -0
  150. package/hooks/use-theorem-decision.d.ts +18 -0
  151. package/hooks/use-theorem-decision.js +12 -0
  152. package/hooks/use-theorem-embedding.d.ts +18 -0
  153. package/hooks/use-theorem-embedding.js +12 -0
  154. package/hooks/use-theorem-host.d.ts +45 -0
  155. package/hooks/use-theorem-host.js +135 -0
  156. package/hooks/use-theorem-interface.d.ts +22 -0
  157. package/hooks/use-theorem-interface.js +15 -0
  158. package/hooks/use-trace-records.d.ts +4 -0
  159. package/hooks/use-trace-records.js +7 -0
  160. package/index.d.ts +17 -0
  161. package/index.js +17 -0
  162. package/live.d.ts +9 -0
  163. package/live.js +10 -0
  164. package/package.json +74 -4
  165. package/server/checkpoint-ownership.d.ts +6 -0
  166. package/server/checkpoint-ownership.js +42 -0
  167. package/server/conversation-store.d.ts +92 -0
  168. package/server/conversation-store.js +131 -0
  169. package/server/conversation.d.ts +193 -0
  170. package/server/conversation.js +552 -0
  171. package/server/credential-store.d.ts +24 -0
  172. package/server/credential-store.js +14 -0
  173. package/server/decision-handler.d.ts +42 -0
  174. package/server/decision-handler.js +46 -0
  175. package/server/embedding-handler.d.ts +33 -0
  176. package/server/embedding-handler.js +45 -0
  177. package/server/handler.d.ts +152 -0
  178. package/server/handler.js +771 -0
  179. package/server/json-response.d.ts +2 -0
  180. package/server/json-response.js +7 -0
  181. package/server/mod.d.ts +25 -0
  182. package/server/mod.js +23 -0
  183. package/server/page-dispatch.d.ts +22 -0
  184. package/server/page-dispatch.js +127 -0
  185. package/server/request-body.d.ts +2 -0
  186. package/server/request-body.js +42 -0
  187. package/server/request-check.d.ts +21 -0
  188. package/server/request-check.js +49 -0
  189. package/server/session-authority.d.ts +8 -0
  190. package/server/session-authority.js +78 -0
  191. package/server/session-boundary.d.ts +26 -0
  192. package/server/session-boundary.js +64 -0
  193. package/server/session-identity.d.ts +16 -0
  194. package/server/session-identity.js +41 -0
  195. package/server/session-limits.d.ts +12 -0
  196. package/server/session-limits.js +59 -0
  197. package/server/session-store.d.ts +123 -0
  198. package/server/session-store.js +264 -0
  199. package/server/single-action.d.ts +21 -0
  200. package/server/single-action.js +30 -0
  201. package/server/steer-inbox.d.ts +35 -0
  202. package/server/steer-inbox.js +52 -0
  203. package/server/tool-operations.d.ts +6 -0
  204. package/server/tool-operations.js +132 -0
  205. package/server/turn-input.d.ts +10 -0
  206. package/server/turn-input.js +29 -0
  207. package/styles/ink-controls.css +134 -0
  208. package/ui/CardWall.d.ts +10 -0
  209. package/ui/CardWall.js +61 -0
  210. package/ui/ChatComposerBar.d.ts +62 -0
  211. package/ui/ChatComposerBar.js +306 -0
  212. package/ui/ChatTranscript.d.ts +43 -0
  213. package/ui/ChatTranscript.js +527 -0
  214. package/ui/ConsoleFrame.d.ts +104 -0
  215. package/ui/ConsoleFrame.js +146 -0
  216. package/ui/DecisionAnswers.d.ts +53 -0
  217. package/ui/DecisionAnswers.js +233 -0
  218. package/ui/DecisionImages.d.ts +16 -0
  219. package/ui/DecisionImages.js +46 -0
  220. package/ui/EmbeddingDocuments.d.ts +78 -0
  221. package/ui/EmbeddingDocuments.js +278 -0
  222. package/ui/EmbeddingQuery.d.ts +27 -0
  223. package/ui/EmbeddingQuery.js +95 -0
  224. package/ui/EmbeddingResults.d.ts +41 -0
  225. package/ui/EmbeddingResults.js +165 -0
  226. package/ui/EmbeddingSpace.d.ts +26 -0
  227. package/ui/EmbeddingSpace.js +106 -0
  228. package/ui/LiveCaptionsPanel.d.ts +22 -0
  229. package/ui/LiveCaptionsPanel.js +27 -0
  230. package/ui/LiveRunner.d.ts +30 -0
  231. package/ui/LiveRunner.js +180 -0
  232. package/ui/PaneState.d.ts +33 -0
  233. package/ui/PaneState.js +50 -0
  234. package/ui/SchemaFields.d.ts +10 -0
  235. package/ui/SchemaFields.js +150 -0
  236. package/ui/ShapedData.d.ts +11 -0
  237. package/ui/ShapedData.js +196 -0
  238. package/ui/SidePanel.d.ts +126 -0
  239. package/ui/SidePanel.js +123 -0
  240. package/ui/StateFields.d.ts +44 -0
  241. package/ui/StateFields.js +139 -0
  242. package/ui/TheoremChat.d.ts +122 -0
  243. package/ui/TheoremChat.js +134 -0
  244. package/ui/TheoremDecision.d.ts +30 -0
  245. package/ui/TheoremDecision.js +130 -0
  246. package/ui/TheoremEmbedding.d.ts +27 -0
  247. package/ui/TheoremEmbedding.js +122 -0
  248. package/ui/TheoremHost.d.ts +55 -0
  249. package/ui/TheoremHost.js +240 -0
  250. package/ui/ToolGateCard.d.ts +32 -0
  251. package/ui/ToolGateCard.js +107 -0
  252. package/ui/ToolResult.d.ts +10 -0
  253. package/ui/ToolResult.js +62 -0
  254. package/ui/ToolResultCharts.d.ts +5 -0
  255. package/ui/ToolResultCharts.js +103 -0
  256. package/ui/TraceCharts.d.ts +13 -0
  257. package/ui/TraceCharts.js +266 -0
  258. package/ui/TraceGuardrails.d.ts +11 -0
  259. package/ui/TraceGuardrails.js +74 -0
  260. package/ui/TraceInspector.d.ts +16 -0
  261. package/ui/TraceInspector.js +206 -0
  262. package/ui/TraceInspectorPanel.d.ts +46 -0
  263. package/ui/TraceInspectorPanel.js +95 -0
  264. package/ui/TraceOverview.d.ts +38 -0
  265. package/ui/TraceOverview.js +126 -0
  266. package/ui/TraceSpanDetail.d.ts +10 -0
  267. package/ui/TraceSpanDetail.js +190 -0
  268. package/ui/TraceStory.d.ts +20 -0
  269. package/ui/TraceStory.js +108 -0
  270. package/ui/TraceValues.d.ts +36 -0
  271. package/ui/TraceValues.js +123 -0
  272. package/ui/TraceWaterfall.d.ts +13 -0
  273. package/ui/TraceWaterfall.js +135 -0
  274. package/ui/VoiceNote.d.ts +12 -0
  275. package/ui/VoiceNote.js +28 -0
  276. package/ui/approval-heading.d.ts +4 -0
  277. package/ui/approval-heading.js +9 -0
  278. package/ui/arrive.d.ts +18 -0
  279. package/ui/arrive.js +46 -0
  280. package/ui/built/theme.css +792 -0
  281. package/ui/built/theme.css.d.ts +9 -0
  282. package/ui/built/theorem.d.ts +13 -0
  283. package/ui/built/theorem.js +660 -0
  284. package/ui/built/theorem.variants.d.ts +22 -0
  285. package/ui/code-language.d.ts +12 -0
  286. package/ui/code-language.js +50 -0
  287. package/ui/disclosure-motion.d.ts +6 -0
  288. package/ui/disclosure-motion.js +207 -0
  289. package/ui/embedding-space-gl.d.ts +37 -0
  290. package/ui/embedding-space-gl.js +663 -0
  291. package/ui/embedding-space-view.d.ts +8 -0
  292. package/ui/embedding-space-view.js +8 -0
  293. package/ui/glide.d.ts +7 -0
  294. package/ui/glide.js +44 -0
  295. package/ui/icons.d.ts +4 -0
  296. package/ui/icons.js +38 -0
  297. package/ui/index.d.ts +36 -0
  298. package/ui/index.js +39 -0
  299. package/ui/labels-provider.d.ts +16 -0
  300. package/ui/labels-provider.js +64 -0
  301. package/ui/labels.d.ts +1442 -0
  302. package/ui/labels.js +1443 -0
  303. package/ui/markdown-plugins.d.ts +9 -0
  304. package/ui/markdown-plugins.js +13 -0
  305. package/ui/mcp-icon.d.ts +6 -0
  306. package/ui/mcp-icon.js +8 -0
  307. package/ui/row-keys.d.ts +19 -0
  308. package/ui/row-keys.js +33 -0
  309. package/ui/theme.d.ts +10 -0
  310. package/ui/theme.js +13 -0
  311. package/ui/theorem-theme.d.ts +10 -0
  312. package/ui/theorem-theme.js +105 -0
  313. package/ui/trace-palette.d.ts +10 -0
  314. package/ui/trace-palette.js +10 -0
  315. package/ui/trace-patterns.d.ts +40 -0
  316. package/ui/trace-patterns.js +57 -0
  317. package/ui/transcript-copy-text.d.ts +4 -0
  318. package/ui/transcript-copy-text.js +40 -0
  319. package/ui/wall-columns.d.ts +8 -0
  320. package/ui/wall-columns.js +19 -0
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CodeBlock } from '@astryxdesign/core/CodeBlock';
3
+ import { Collapsible, CollapsibleGroup } from '@astryxdesign/core/Collapsible';
4
+ import { HStack } from '@astryxdesign/core/HStack';
5
+ import { useLocale } from '@astryxdesign/core/i18n';
6
+ import { Link } from '@astryxdesign/core/Link';
7
+ import { List, ListItem } from '@astryxdesign/core/List';
8
+ import { Markdown } from '@astryxdesign/core/Markdown';
9
+ import { MetadataList, MetadataListItem } from '@astryxdesign/core/MetadataList';
10
+ import { SegmentedControl, SegmentedControlItem } from '@astryxdesign/core/SegmentedControl';
11
+ import { Table } from '@astryxdesign/core/Table';
12
+ import { Text } from '@astryxdesign/core/Text';
13
+ import { Token } from '@astryxdesign/core/Token';
14
+ import { VStack } from '@astryxdesign/core/VStack';
15
+ import { Component, useMemo, useState } from 'react';
16
+ import { fieldLabel, fieldReading, isPlain, isProse, isRow, json, MAX_ROWS, plainReading, rowHeading, shapeOf, splitFields, unpacked, withUnit, } from "../client/shaped-data.js";
17
+ import { Arrive, useArrive } from "./arrive.js";
18
+ import { useLabels } from "./labels-provider.js";
19
+ import { useMarkdownPlugins } from "./markdown-plugins.js";
20
+ import { keyedByContent } from "./row-keys.js";
21
+ /** Each way a plain value reads, drawn. */
22
+ function ProseReading({ text }) {
23
+ return (_jsx(Markdown, { density: "compact", headingLevelStart: 4, plugins: useMarkdownPlugins(), children: text }));
24
+ }
25
+ const READINGS = {
26
+ none: () => _jsx(Text, { color: "secondary", children: "\u2014" }),
27
+ boolean: ({ value }, _, t) => t(value ? '@theorem.data.yes' : '@theorem.data.no'),
28
+ number: ({ value, unit }, formats) => withUnit((Number.isInteger(value) ? formats.integer : formats.decimal).format(value), unit),
29
+ link: ({ href }) => (_jsx(Link, { href: href, target: "_blank", rel: "noreferrer", children: href })),
30
+ date: ({ date }, formats) => formats.date.format(date),
31
+ 'date-time': ({ date }, formats) => formats.dateTime.format(date),
32
+ // why: Prose (an answer, a summary) is usually Markdown; headings start small, under the section's own.
33
+ text: ({ text }) => isProse(text) ? (_jsx(ProseReading, { text: text })) : (_jsx("span", { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: text })),
34
+ };
35
+ function Plain({ value, unit }) {
36
+ const t = useLabels();
37
+ const locale = useLocale();
38
+ const formats = useMemo(() => ({
39
+ // why: No grouping: ids and years read as written. Six significant digits: a timing reads 1.40369.
40
+ integer: new Intl.NumberFormat(locale, { useGrouping: false }),
41
+ decimal: new Intl.NumberFormat(locale, { useGrouping: false, maximumSignificantDigits: 6 }),
42
+ dateTime: new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }),
43
+ // why: A bare date is a calendar day, parsed at UTC midnight: read it in UTC, or it slips a day west of Greenwich.
44
+ date: new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone: 'UTC' }),
45
+ }), [locale]);
46
+ const reading = plainReading(value, unit);
47
+ return READINGS[reading.kind](reading, formats, t);
48
+ }
49
+ /** JSON past this many characters isn't laid out or highlighted in full: the page stays responsive however big the payload. */
50
+ const LARGE_JSON_CHARS = 200_000;
51
+ function Json({ text }) {
52
+ const code = text.length > LARGE_JSON_CHARS ? `${text.slice(0, LARGE_JSON_CHARS)}\n…` : text;
53
+ return _jsx(CodeBlock, { code: code, language: "json", hasLanguageLabel: false, size: "sm", width: "100%" });
54
+ }
55
+ /** Past MAX_ROWS, a note that the rest are in the JSON view. */
56
+ function More({ total }) {
57
+ const t = useLabels();
58
+ if (total <= MAX_ROWS)
59
+ return null;
60
+ return (_jsx(Text, { type: "supporting", color: "secondary", children: t('@theorem.data.more', { count: String(total - MAX_ROWS) }) }));
61
+ }
62
+ function DataTable({ columns, rows, units, }) {
63
+ const tableColumns = columns.map((key, index) => {
64
+ const { label, unit } = fieldLabel(key, units);
65
+ return {
66
+ key: `c${String(index)}`,
67
+ header: unit ? `${label} (${unit})` : label,
68
+ renderCell: (row) => _jsx(Plain, { value: row.cells[key] }),
69
+ };
70
+ });
71
+ const data = rows.slice(0, MAX_ROWS).map((cells, index) => ({ id: String(index), cells }));
72
+ return (_jsxs(VStack, { gap: 1, children: [_jsx(Table, { data: data, columns: tableColumns, idKey: "id", density: "compact", textOverflow: "truncate" }), _jsx(More, { total: rows.length })] }));
73
+ }
74
+ /** A list row's extra fields and tags, at its end. */
75
+ function ListRowEnd({ row }) {
76
+ if (row.tags.length + row.extras.length === 0)
77
+ return undefined;
78
+ return (_jsxs(HStack, { gap: 2, vAlign: "center", children: [row.extras.map(([key, value]) => {
79
+ const { label, unit } = fieldReading(key, value);
80
+ return (_jsxs(Text, { type: "supporting", color: "secondary", children: [label, " ", _jsx(Plain, { value: value, unit: unit })] }, key));
81
+ }), row.tags.map((tag) => (_jsx(Token, { label: tag, size: "sm" }, tag)))] }));
82
+ }
83
+ function dataListItem(row) {
84
+ return {
85
+ label: row.title,
86
+ description: row.description,
87
+ href: row.href,
88
+ target: row.href ? '_blank' : undefined,
89
+ rel: row.href ? 'noreferrer' : undefined,
90
+ endContent: _jsx(ListRowEnd, { row: row }),
91
+ };
92
+ }
93
+ /** A top-level row eases in with the rest of a returned result. Nested rows stay put. */
94
+ function ArrivingDataRow({ row }) {
95
+ const arrive = useArrive();
96
+ return _jsx(ListItem, { className: arrive?.className, style: arrive?.style, ...dataListItem(row) });
97
+ }
98
+ function DataList({ rows, depth }) {
99
+ return (_jsxs(VStack, { gap: 1, children: [_jsx(List, { hasDividers: true, density: "compact", children: keyedByContent(rows.slice(0, MAX_ROWS), (row) => row.title).map(({ item: row, key }) => depth === 0 ? (_jsx(ArrivingDataRow, { row: row }, key)) : (_jsx(ListItem, { ...dataListItem(row) }, key))) }), _jsx(More, { total: rows.length })] }));
100
+ }
101
+ function ObjectRow({ item, index, depth, className, style, }) {
102
+ const t = useLabels();
103
+ const heading = rowHeading(item);
104
+ return (_jsx(Collapsible, { className: className, style: style, value: String(index), trigger: _jsxs(HStack, { gap: 2, vAlign: "center", wrap: "wrap", children: [_jsx(Text, { type: "body", children: heading.title ?? t('@theorem.data.item', { index: String(index + 1) }) }), heading.tags.map((tag) => (_jsx(Token, { label: tag, size: "sm" }, tag)))] }), children: _jsx(Node, { value: heading.rest, depth: depth + 1 }) }));
105
+ }
106
+ /** A top-level object row eases in with the result. One nested under it does not. */
107
+ function ArrivingObjectRow({ item, index, depth, }) {
108
+ const arrive = useArrive();
109
+ return (_jsx(ObjectRow, { item: item, index: index, depth: depth, className: arrive?.className, style: arrive?.style }));
110
+ }
111
+ function Rows({ items, depth }) {
112
+ return (_jsxs(VStack, { gap: 1, children: [_jsx(CollapsibleGroup, { type: "multiple", hasDividers: true, density: "compact", children: keyedByContent(items.slice(0, MAX_ROWS), (item) => JSON.stringify(item) ?? '').map(({ item, index, key }) => depth === 0 ? (_jsx(ArrivingObjectRow, { item: item, index: index, depth: depth }, key)) : (_jsx(ObjectRow, { item: item, index: index, depth: depth }, key))) }), _jsx(More, { total: items.length })] }));
113
+ }
114
+ function FieldSection({ shown, depth, className, style, }) {
115
+ return (_jsx(Collapsible, { className: className, style: style, value: shown.key, trigger: _jsx(Text, { type: "label", children: shown.title }), children: _jsx(VStack, { paddingInlineStart: 3, children: _jsx(Node, { value: shown.value, depth: depth + 1, units: shown.units }) }) }));
116
+ }
117
+ function ArrivingFieldSection({ shown, depth }) {
118
+ const arrive = useArrive();
119
+ return (_jsx(FieldSection, { shown: shown, depth: depth, className: arrive?.className, style: arrive?.style }));
120
+ }
121
+ function Fields({ row, depth, units }) {
122
+ const { plain, sections } = splitFields(row);
123
+ const fields = plain.length > 0 && (_jsx(MetadataList, { label: { position: 'start', width: '40%' }, children: plain.map(([key, value]) => {
124
+ const { label, unit } = fieldReading(key, value, units);
125
+ return (_jsx(MetadataListItem, { label: label, children: Array.isArray(unpacked(value)) ? (_jsx(Node, { value: value, depth: depth + 1 })) : (_jsx(Plain, { value: value, unit: unit })) }, key));
126
+ }) }));
127
+ return (_jsxs(VStack, { gap: 2, children: [depth > 0 && fields, sections.length > 0 && (_jsx(CollapsibleGroup, { type: "multiple", hasDividers: true, density: "compact",
128
+ // why: The top level's first section starts open: it's usually the payload. The rest wait to be asked for.
129
+ defaultValue: depth === 0 ? sections.slice(0, 1).map((shown) => shown.key) : [], children: sections.map((shown) => depth === 0 ? (_jsx(ArrivingFieldSection, { shown: shown, depth: depth }, shown.key)) : (_jsx(FieldSection, { shown: shown, depth: depth }, shown.key))) })), depth === 0 && fields ? _jsx(Arrive, { children: fields }) : null] }));
130
+ }
131
+ /** Each shape a value takes, drawn. */
132
+ const SHAPES = {
133
+ json: (_, { value }) => _jsx(Json, { text: json(value) }),
134
+ none: (_, { t }) => _jsx(Text, { color: "secondary", children: t('@theorem.data.none') }),
135
+ plain: (_, { value }) => (_jsx(Text, { type: "body", children: _jsx(Plain, { value: value }) })),
136
+ tokens: ({ items }) => (_jsxs(VStack, { gap: 1, children: [_jsx(HStack, { gap: 1, wrap: "wrap", children: keyedByContent(items.slice(0, MAX_ROWS), (item) => String(item)).map(({ item, key }) => (_jsx(Token, { label: item === null ? '—' : String(item), size: "sm" }, key))) }), _jsx(More, { total: items.length })] })),
137
+ list: ({ rows }, { depth }) => _jsx(DataList, { rows: rows, depth: depth }),
138
+ table: ({ columns, rows }, { units }) => (_jsx(DataTable, { columns: columns, rows: rows, units: units })),
139
+ rows: ({ items }, { depth }) => _jsx(Rows, { items: items, depth: depth }),
140
+ fields: ({ row }, { depth, units }) => _jsx(Fields, { row: row, depth: depth, units: units }),
141
+ };
142
+ function Node({ value: raw, depth, units, }) {
143
+ const t = useLabels();
144
+ const value = unpacked(raw);
145
+ const shape = shapeOf(value, depth);
146
+ const body = SHAPES[shape.kind](shape, {
147
+ value,
148
+ depth,
149
+ units,
150
+ t,
151
+ });
152
+ // why: A list, a set of rows, or an object's sections stagger their own children. Anything else at the top of a result is one arrival.
153
+ if (depth !== 0 || shape.kind === 'list' || shape.kind === 'rows' || shape.kind === 'fields') {
154
+ return body;
155
+ }
156
+ return _jsx(Arrive, { children: body });
157
+ }
158
+ /** A render the rules didn't foresee throws into the JSON, never into the transcript. */
159
+ class ShapeBoundary extends Component {
160
+ state = { failed: false };
161
+ static getDerivedStateFromError() {
162
+ return { failed: true };
163
+ }
164
+ componentDidCatch() {
165
+ this.props.onFallback();
166
+ }
167
+ render() {
168
+ return this.state.failed ? this.props.fallback : this.props.children;
169
+ }
170
+ }
171
+ /** A tool call's input, output or failure, or a structured reply, with a switch to its JSON. */
172
+ export function ShapedData({ value: raw, title, lead, ledKeys = [] }) {
173
+ const t = useLabels();
174
+ const locale = useLocale();
175
+ const [view, setView] = useState('data');
176
+ const value = unpacked(raw);
177
+ const structured = !isPlain(value);
178
+ const text = useMemo(() => (structured ? json(value) : ''), [structured, value]);
179
+ const rest = useMemo(() => {
180
+ if (!ledKeys.length || !isRow(value))
181
+ return value;
182
+ const left = Object.entries(value).filter(([key]) => !ledKeys.includes(key));
183
+ return left.length ? Object.fromEntries(left) : undefined;
184
+ }, [value, ledKeys]);
185
+ if (text.length > LARGE_JSON_CHARS) {
186
+ const size = new Intl.NumberFormat(locale, {
187
+ style: 'unit',
188
+ unit: 'kilobyte',
189
+ maximumFractionDigits: 0,
190
+ }).format(LARGE_JSON_CHARS / 1000);
191
+ return (_jsxs(VStack, { gap: 2, children: [_jsx(Text, { type: "supporting", color: "secondary", children: title }), _jsx(Text, { type: "supporting", color: "secondary", children: t('@theorem.data.large', { size }) }), _jsx(Json, { text: text })] }));
192
+ }
193
+ return (_jsxs(VStack, { gap: 2, children: [_jsxs(HStack, { gap: 2, vAlign: "center", hAlign: "between", children: [_jsx(Text, { type: "supporting", color: "secondary", children: title }), structured && (_jsxs(SegmentedControl, { label: t('@theorem.data.view'), size: "sm", value: view, onChange: (next) => {
194
+ setView(next === 'json' ? 'json' : 'data');
195
+ }, children: [_jsx(SegmentedControlItem, { value: "data", label: t('@theorem.data.shaped') }), _jsx(SegmentedControlItem, { value: "json", label: t('@theorem.data.json') })] }))] }), view === 'json' && structured ? (_jsx(Json, { text: text })) : (_jsx(ShapeBoundary, { fallback: _jsx(Json, { text: text }), onFallback: () => setView('json'), children: _jsxs(VStack, { gap: 3, children: [lead, rest === undefined ? null : _jsx(Node, { value: rest, depth: 0 })] }) }))] }));
196
+ }
@@ -0,0 +1,126 @@
1
+ import { type ResizableRegion } from '@astryxdesign/core/Resizable';
2
+ import { type ReactNode, type RefObject } from 'react';
3
+ /** Whether a side panel is wide enough for two readable columns: twice its minimum. */
4
+ export declare function isSidePanelWide(size: number): boolean;
5
+ /** A side panel's open state and size. `containerRef` is the Layout its percentages resolve against. */
6
+ export declare function useSidePanel(containerRef: RefObject<HTMLDivElement | null>, initiallyOpen: boolean): {
7
+ id: string;
8
+ open: boolean;
9
+ toggle: () => void;
10
+ resizable: ResizableRegion;
11
+ };
12
+ /** The Layout header row: side panel toggles, at the end. */
13
+ export declare function SidePanelHeader({ children, floating, }: {
14
+ children?: ReactNode;
15
+ floating?: boolean;
16
+ }): import("react").JSX.Element;
17
+ /** A side panel's words: its name, and its toggle and drag handle actions. */
18
+ export type SidePanelLabels = {
19
+ name: string;
20
+ show: string;
21
+ hide: string;
22
+ resize: string;
23
+ };
24
+ /** Shows or hides one side panel (`panelId` from useSidePanel). */
25
+ export declare function SidePanelToggle({ labels, icon, panelId, open, onToggle, }: {
26
+ labels: SidePanelLabels;
27
+ icon: ReactNode;
28
+ panelId: string;
29
+ open: boolean;
30
+ onToggle: () => void;
31
+ }): import("react").JSX.Element;
32
+ export type SidePanelProps = {
33
+ id?: string;
34
+ labels: SidePanelLabels;
35
+ /** From `useResizable`: the panel's width and its handle's drag state. */
36
+ resizable: ResizableRegion;
37
+ /**
38
+ * Closed panels stay mounted at zero width and inert, so the theme can ease
39
+ * their width; their content renders only while open, so it never reflows
40
+ * into the collapsing width.
41
+ */
42
+ open?: boolean;
43
+ /** Card padding; 0 for content that brings its own (e.g. ChatLayout). */
44
+ padding?: 0;
45
+ /**
46
+ * `end` docks on the trailing edge: the handle, then the panel. `start`
47
+ * docks on the leading edge: the panel, then the handle.
48
+ */
49
+ side?: 'start' | 'end';
50
+ className?: string;
51
+ children?: ReactNode;
52
+ };
53
+ /** Page corners like the host's raised panels, no border or shadow; the card fill, so surface-filled cards read on it. */
54
+ export declare const RAISED: {
55
+ background: string;
56
+ borderRadius: string;
57
+ overflow: string;
58
+ };
59
+ /** A docked panel's inset: 12px off the outer edge, 16px off the bottom (the page inset). */
60
+ export declare const PANEL_EDGE = 3;
61
+ export declare const PANEL_BOTTOM = 4;
62
+ /** The raised card every docked panel and the content beside one share. */
63
+ export declare function PanelBody({ padding, inset, className, children, }: {
64
+ padding?: 0;
65
+ /**
66
+ * `end` insets the trailing edge (a panel docked there, or the content
67
+ * filling beside a start panel). `start` insets the leading edge. `beside`
68
+ * insets both.
69
+ */
70
+ inset?: 'end' | 'start' | 'beside';
71
+ className?: string;
72
+ children?: ReactNode;
73
+ }): import("react").JSX.Element;
74
+ /** The scroll inside a panel card. The same area the trace uses, so a short body sizes to its rows. */
75
+ export declare function PanelScroll({ label, padding, children, }: {
76
+ label: string;
77
+ padding?: 4;
78
+ children?: ReactNode;
79
+ }): import("react").JSX.Element;
80
+ /**
81
+ * A Layout side panel as Astryx's IDE template builds one (a `ResizeHandle`
82
+ * beside a `LayoutPanel` sized by `useResizable`), holding a raised section
83
+ * inset from the layout's outer edge and bottom instead of a flat pane.
84
+ * `end` puts the handle first and reverses it; `start` puts the panel first.
85
+ * For more than one panel, nest Layouts, one panel each, as the template does.
86
+ */
87
+ export declare function SidePanel({ id, labels, resizable, open, padding, side, className, children, }: SidePanelProps): import("react").JSX.Element;
88
+ /** The element's width, kept current. */
89
+ export declare function useWidth(): {
90
+ ref: (node: HTMLDivElement | null) => (() => void) | undefined;
91
+ width: number;
92
+ };
93
+ /**
94
+ * A raised pane that fills its place, inset from the sides. Its content is
95
+ * told whether the pane is wide enough for two readable columns.
96
+ */
97
+ export declare function RaisedPane({ id, header, children, }: {
98
+ id?: string;
99
+ header?: ReactNode;
100
+ children: (isWide: boolean) => ReactNode;
101
+ }): import("react").JSX.Element;
102
+ /** A raised pane's content. Narrow, the detail takes the overview's place; wide, it sits beside it. */
103
+ export declare function PaneLayout({ isWide, label, detailLabel, lead, overview, detail, }: {
104
+ isWide: boolean;
105
+ label: string;
106
+ detailLabel: string;
107
+ /** Rendered once, ahead of the content. */
108
+ lead?: ReactNode;
109
+ overview: ReactNode;
110
+ detail: ReactNode;
111
+ }): import("react").JSX.Element;
112
+ /** One titled block of a pane's detail; `doc` says what the title means, on hover. */
113
+ export declare function PanePanel({ title, doc, children, }: {
114
+ title: string;
115
+ doc?: string;
116
+ children: ReactNode;
117
+ }): import("react").JSX.Element;
118
+ /** Text as it was written: its line breaks kept, long words wrapped. */
119
+ export declare function Prose({ text }: {
120
+ text: string;
121
+ }): import("react").JSX.Element;
122
+ /** The surface, or the view in its place; the surface stays mounted underneath, so nothing it holds is lost. */
123
+ export declare function InPlace({ view, children }: {
124
+ view: ReactNode;
125
+ children: ReactNode;
126
+ }): import("react").JSX.Element;
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Card } from '@astryxdesign/core/Card';
3
+ import { HStack } from '@astryxdesign/core/HStack';
4
+ import { IconButton } from '@astryxdesign/core/IconButton';
5
+ import { Layout, LayoutContent, LayoutHeader, LayoutPanel } from '@astryxdesign/core/Layout';
6
+ import { ResizeHandle, useResizable, } from '@astryxdesign/core/Resizable';
7
+ import { ScrollableArea } from '@astryxdesign/core/ScrollableArea';
8
+ import { StackItem } from '@astryxdesign/core/Stack';
9
+ import { Text } from '@astryxdesign/core/Text';
10
+ import { Tooltip as HoverTip } from '@astryxdesign/core/Tooltip';
11
+ import { VStack } from '@astryxdesign/core/VStack';
12
+ import { useCallback, useId, useState } from 'react';
13
+ /**
14
+ * `useResizable` sizing for side panels: a third of the layout (pass the
15
+ * Layout as `containerRef` so the percentages resolve), dragged between a
16
+ * readable minimum and half.
17
+ */
18
+ const SIDE_PANEL_SIZING = {
19
+ defaultSize: '33%',
20
+ minSize: 280,
21
+ maxSize: '50%',
22
+ };
23
+ /** Whether a side panel is wide enough for two readable columns: twice its minimum. */
24
+ export function isSidePanelWide(size) {
25
+ return size >= SIDE_PANEL_SIZING.minSize * 2;
26
+ }
27
+ /** A side panel's open state and size. `containerRef` is the Layout its percentages resolve against. */
28
+ export function useSidePanel(containerRef, initiallyOpen) {
29
+ const id = useId();
30
+ const [open, setOpen] = useState(initiallyOpen);
31
+ const toggle = useCallback(() => setOpen((value) => !value), []);
32
+ const resizable = useResizable({ ...SIDE_PANEL_SIZING, containerRef });
33
+ return { id, open, toggle, resizable };
34
+ }
35
+ /** The Layout header row: side panel toggles, at the end. */
36
+ export function SidePanelHeader({ children, floating = false, }) {
37
+ if (floating)
38
+ return (_jsx("div", { style: {
39
+ position: 'absolute',
40
+ insetBlockStart: 'var(--spacing-4)',
41
+ insetInlineEnd: 'var(--spacing-4)',
42
+ zIndex: 3,
43
+ }, children: _jsx(HStack, { hAlign: "end", gap: 1, children: children }) }));
44
+ return (_jsx(LayoutHeader, { hasDivider: false, children: _jsx(HStack, { hAlign: "end", gap: 1, children: children }) }));
45
+ }
46
+ /** Shows or hides one side panel (`panelId` from useSidePanel). */
47
+ export function SidePanelToggle({ labels, icon, panelId, open, onToggle, }) {
48
+ const action = open ? labels.hide : labels.show;
49
+ return (_jsx(IconButton, { label: action, tooltip: action, variant: open ? 'secondary' : 'ghost', "aria-expanded": open, "aria-controls": panelId, icon: icon, onClick: onToggle }));
50
+ }
51
+ /** Page corners like the host's raised panels, no border or shadow; the card fill, so surface-filled cards read on it. */
52
+ export const RAISED = {
53
+ background: 'var(--theorem-pane-background, var(--color-background-card))',
54
+ borderRadius: 'var(--radius-page)',
55
+ overflow: 'hidden',
56
+ };
57
+ /** A docked panel's inset: 12px off the outer edge, 16px off the bottom (the page inset). */
58
+ export const PANEL_EDGE = 3;
59
+ export const PANEL_BOTTOM = 4;
60
+ /** The raised card every docked panel and the content beside one share. */
61
+ export function PanelBody({ padding, inset = 'end', className, children, }) {
62
+ return (_jsx(VStack, { height: "100%", className: className, paddingInlineStart: inset === 'end' ? undefined : PANEL_EDGE, paddingInlineEnd: inset === 'start' ? undefined : PANEL_EDGE, paddingBlockEnd: PANEL_BOTTOM, children: _jsx(Card, { variant: "transparent", height: "100%", padding: padding, style: RAISED, children: children }) }));
63
+ }
64
+ /** The scroll inside a panel card. The same area the trace uses, so a short body sizes to its rows. */
65
+ export function PanelScroll({ label, padding, children, }) {
66
+ return (_jsx(ScrollableArea, { className: "theorem-panel-scroll", label: label, height: "100%", padding: padding, children: children }));
67
+ }
68
+ /**
69
+ * A Layout side panel as Astryx's IDE template builds one (a `ResizeHandle`
70
+ * beside a `LayoutPanel` sized by `useResizable`), holding a raised section
71
+ * inset from the layout's outer edge and bottom instead of a flat pane.
72
+ * `end` puts the handle first and reverses it; `start` puts the panel first.
73
+ * For more than one panel, nest Layouts, one panel each, as the template does.
74
+ */
75
+ export function SidePanel({ id, labels, resizable, open = true, padding, side = 'end', className, children, }) {
76
+ const handle = open ? (_jsx(ResizeHandle, { direction: "horizontal", isReversed: side === 'end', hasDivider: false, isAlwaysVisible: false, resizable: resizable.props, label: labels.resize })) : null;
77
+ const panel = (_jsx(LayoutPanel, { id: id, className: className, label: labels.name, role: "complementary", width: open ? resizable.size : 0, hasDivider: false, padding: 0, isScrollable: false, inert: !open, children: _jsx(PanelBody, { padding: padding, inset: side, children: open ? children : null }) }));
78
+ return side === 'start' ? (_jsxs(_Fragment, { children: [panel, handle] })) : (_jsxs(_Fragment, { children: [handle, panel] }));
79
+ }
80
+ /** The element's width, kept current. */
81
+ export function useWidth() {
82
+ const [width, setWidth] = useState(0);
83
+ const ref = useCallback((node) => {
84
+ if (!node)
85
+ return;
86
+ const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));
87
+ observer.observe(node);
88
+ return () => observer.disconnect();
89
+ }, []);
90
+ return { ref, width };
91
+ }
92
+ /**
93
+ * A raised pane that fills its place, inset from the sides. Its content is
94
+ * told whether the pane is wide enough for two readable columns.
95
+ */
96
+ export function RaisedPane({ id, header, children, }) {
97
+ const { ref, width } = useWidth();
98
+ return (_jsx(Layout, { height: "fill", style: { position: 'relative' }, header: header, content: _jsx(LayoutContent, { padding: 0, isScrollable: false, children: _jsx(VStack, { height: "100%", padding: 3, children: _jsx(Card, { variant: "transparent", height: "100%", padding: 0, style: RAISED, children: _jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: children(isSidePanelWide(width)) }) }) }) }) }));
99
+ }
100
+ /** A raised pane's content. Narrow, the detail takes the overview's place; wide, it sits beside it. */
101
+ export function PaneLayout({ isWide, label, detailLabel, lead, overview, detail, }) {
102
+ if (!isWide) {
103
+ return (_jsxs(PanelScroll, { label: label, children: [lead, detail || overview] }));
104
+ }
105
+ return (_jsxs(HStack, { height: "100%", gap: 0, children: [_jsx(StackItem, { size: "fill", children: _jsxs(PanelScroll, { label: label, children: [lead, overview] }) }), detail ? (_jsx("div", { style: {
106
+ width: '48%',
107
+ flexShrink: 0,
108
+ height: '100%',
109
+ borderInlineStart: '1px solid var(--color-border)',
110
+ }, children: _jsx(PanelScroll, { label: detailLabel, children: detail }) })) : null] }));
111
+ }
112
+ /** One titled block of a pane's detail; `doc` says what the title means, on hover. */
113
+ export function PanePanel({ title, doc, children, }) {
114
+ return (_jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsxs(VStack, { gap: 2, children: [_jsx(HoverTip, { content: doc ?? title, children: _jsx(Text, { type: "supporting", color: "secondary", children: title }) }), children] }) }));
115
+ }
116
+ /** Text as it was written: its line breaks kept, long words wrapped. */
117
+ export function Prose({ text }) {
118
+ return _jsx("span", { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: text });
119
+ }
120
+ /** The surface, or the view in its place; the surface stays mounted underneath, so nothing it holds is lost. */
121
+ export function InPlace({ view, children }) {
122
+ return (_jsxs(_Fragment, { children: [view, _jsx("div", { style: { display: view ? 'none' : 'contents' }, children: children })] }));
123
+ }
@@ -0,0 +1,44 @@
1
+ import type { DecisionJson } from '@theoremjs/agents';
2
+ import { type DecisionImages } from '../client/decision-images.ts';
3
+ export type Edit<T = DecisionJson> = {
4
+ value: T;
5
+ onChange: (next: T) => void;
6
+ };
7
+ /** A text area's height: one row per line of its text, within `min` and `max`. Wrapped lines do not count. */
8
+ export declare function textRows(text: string, min: number, max: number): number;
9
+ /** The images the state names, and how to change them; a field whose whole text names one shows it. */
10
+ export declare const StateImages: import("react").Context<{
11
+ images: DecisionImages;
12
+ onImages: (next: DecisionImages) => void;
13
+ }>;
14
+ export type GroupLabel = {
15
+ label: string;
16
+ isRequired?: boolean;
17
+ description?: React.ReactNode;
18
+ };
19
+ /** Several fields under one name, in the same label a single field has. */
20
+ export declare function Labelled({ label, isRequired, description, children, }: GroupLabel & {
21
+ children: React.ReactNode;
22
+ }): import("react").JSX.Element;
23
+ /** Nested fields sit under their name, set in by a rule. */
24
+ export declare function Group({ children, ...label }: GroupLabel & {
25
+ children: React.ReactNode;
26
+ }): import("react").JSX.Element;
27
+ /** One more row at the end of a list. */
28
+ export declare function AddRow({ field, onAdd }: {
29
+ field: string;
30
+ onAdd: () => void;
31
+ }): import("react").JSX.Element;
32
+ /** Removes a list's row, named by its place. */
33
+ export declare function RemoveRow({ index, onRemove }: {
34
+ index: number;
35
+ onRemove: () => void;
36
+ }): import("react").JSX.Element;
37
+ /**
38
+ * A decision's state edited field by field: objects as labelled fields, lists
39
+ * of words as tokens, other lists as rows. The shape itself (new keys, other
40
+ * types) is edited as JSON.
41
+ */
42
+ export declare function StateFields({ value, onChange, label }: Edit & {
43
+ label: string;
44
+ }): import("react").JSX.Element;
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@astryxdesign/core/Button';
3
+ import { FieldLabel } from '@astryxdesign/core/Field';
4
+ import { FileInput } from '@astryxdesign/core/FileInput';
5
+ import { HStack } from '@astryxdesign/core/HStack';
6
+ import { IconButton } from '@astryxdesign/core/IconButton';
7
+ import { Lightbox } from '@astryxdesign/core/Lightbox';
8
+ import { NumberInput } from '@astryxdesign/core/NumberInput';
9
+ import { StackItem } from '@astryxdesign/core/Stack';
10
+ import { Switch } from '@astryxdesign/core/Switch';
11
+ import { Text } from '@astryxdesign/core/Text';
12
+ import { TextArea } from '@astryxdesign/core/TextArea';
13
+ import { TextInput } from '@astryxdesign/core/TextInput';
14
+ import { Thumbnail } from '@astryxdesign/core/Thumbnail';
15
+ import { Tokenizer } from '@astryxdesign/core/Tokenizer';
16
+ import { VStack } from '@astryxdesign/core/VStack';
17
+ import { IconPlus, IconX } from '@tabler/icons-react';
18
+ import { createContext, useContext, useId, useState } from 'react';
19
+ import { IMAGE_ACCEPT, imageSrc } from "../client/decision-images.js";
20
+ import { humanize, isPlain } from "../client/shaped-data.js";
21
+ import { blankLike, isWordList, tableColumns } from "../client/state-fields.js";
22
+ import { useImageReader } from "./DecisionImages.js";
23
+ import { useLabels } from "./labels-provider.js";
24
+ import { useKeyedRows } from "./row-keys.js";
25
+ const NO_SUGGESTIONS = { search: () => [], bootstrap: () => [] };
26
+ /** Text this long, or on several lines, gets a text area. */
27
+ const LONG_TEXT = 48;
28
+ /** A text area's height: one row per line of its text, within `min` and `max`. Wrapped lines do not count. */
29
+ export function textRows(text, min, max) {
30
+ return Math.min(Math.max(text.split('\n').length, min), max);
31
+ }
32
+ /** The images the state names, and how to change them; a field whose whole text names one shows it. */
33
+ export const StateImages = createContext({ images: {}, onImages: () => { } });
34
+ /** A field that holds an image: the image, and a file input that replaces it. */
35
+ function ImageField({ value, label, isLabelHidden, }) {
36
+ const t = useLabels();
37
+ const { images, onImages } = useContext(StateImages);
38
+ const { read, status } = useImageReader();
39
+ const [isOpen, setOpen] = useState(false);
40
+ const src = Object.hasOwn(images, value) ? imageSrc(images[value]) : undefined;
41
+ return (_jsxs(HStack, { gap: 2, vAlign: "center", children: [_jsx(Thumbnail, { src: src, alt: label, label: value, onClick: src ? () => setOpen(true) : undefined }), src ? (_jsx(Lightbox, { isOpen: isOpen, onOpenChange: setOpen, media: { src, alt: label, caption: value }, hasZoom: true })) : null, _jsx(StackItem, { size: "fill", children: _jsx(FileInput, { label: label, isLabelHidden: isLabelHidden, accept: IMAGE_ACCEPT, value: null, placeholder: t('@theorem.decision.replace_image'), status: status, statusVariant: "detached", onChange: (files) => {
42
+ void read(files).then(([first]) => {
43
+ if (first)
44
+ onImages({ ...images, [value]: first[1] });
45
+ });
46
+ } }) })] }));
47
+ }
48
+ /** A string, number, true/false, or null, as its input. */
49
+ function PlainField({ value, onChange, label, isLabelHidden }) {
50
+ const { images } = useContext(StateImages);
51
+ // why: Decided once, so the field doesn't swap inputs (and lose focus) as the text grows or changes.
52
+ const [isLong] = useState(() => typeof value === 'string' && (value.length > LONG_TEXT || value.includes('\n')));
53
+ const [isImage] = useState(() => typeof value === 'string' && Object.hasOwn(images, value));
54
+ if (isImage && typeof value === 'string')
55
+ return _jsx(ImageField, { value: value, label: label, isLabelHidden: isLabelHidden });
56
+ if (typeof value === 'boolean')
57
+ return _jsx(Switch, { label: label, value: value, onChange: onChange });
58
+ if (typeof value === 'number') {
59
+ return (_jsx(NumberInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: value, isWheelEnabled: false, onChange: onChange }));
60
+ }
61
+ if (value === null)
62
+ return (_jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: "", placeholder: "null", isDisabled: true, onChange: () => { } }));
63
+ const text = String(value);
64
+ return isLong ? (_jsx(TextArea, { label: label, isLabelHidden: isLabelHidden, size: "sm", rows: textRows(text, 2, 12), value: text, onChange: onChange })) : (_jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: text, onChange: onChange }));
65
+ }
66
+ /** Several fields under one name, in the same label a single field has. */
67
+ export function Labelled({ label, isRequired, description, children, }) {
68
+ const id = useId();
69
+ return (_jsxs(VStack, { gap: 2, role: "group", "aria-labelledby": id, children: [_jsx(FieldLabel, { label: label, inputID: "", labelID: id, isGroupLabel: true, isRequired: isRequired, description: description }), children] }));
70
+ }
71
+ /** Nested fields sit under their name, set in by a rule. */
72
+ export function Group({ children, ...label }) {
73
+ return (_jsx(Labelled, { ...label, children: _jsx("div", { style: { paddingInlineStart: 12, borderInlineStart: '1px solid var(--color-border)' }, children: children }) }));
74
+ }
75
+ /** One more row at the end of a list. */
76
+ export function AddRow({ field, onAdd }) {
77
+ const t = useLabels();
78
+ return (_jsx(HStack, { children: _jsx(Button, { label: t('@theorem.decision.add_item', { field }), size: "sm", variant: "ghost", icon: _jsx(IconPlus, { size: 14 }), onClick: onAdd }) }));
79
+ }
80
+ /** Removes a list's row, named by its place. */
81
+ export function RemoveRow({ index, onRemove }) {
82
+ const t = useLabels();
83
+ return (_jsx(IconButton, { label: t('@theorem.decision.remove_item', {
84
+ item: t('@theorem.data.item', { index: String(index + 1) }),
85
+ }), size: "sm", variant: "ghost", icon: _jsx(IconX, { size: 14 }), onClick: onRemove }));
86
+ }
87
+ function ObjectFields({ value, onChange }) {
88
+ return (_jsx(VStack, { gap: 3, children: Object.entries(value).map(([key, entry]) => (_jsx(Field, { label: humanize(key), value: entry, onChange: (next) => onChange({ ...value, [key]: next }) }, key))) }));
89
+ }
90
+ /** A list of words as tokens; any other list as rows, each removable, with one more shaped like the last. */
91
+ function ListField({ value, onChange, label }) {
92
+ const t = useLabels();
93
+ const keyed = useKeyedRows(value);
94
+ if (isWordList(value)) {
95
+ return (_jsx(Tokenizer, { label: label, size: "sm", value: value.map((word, index) => ({ id: `${String(index)}:${word}`, label: word })), searchSource: NO_SUGGESTIONS, hasCreate: true, debounceMs: 0, placeholder: t('@theorem.decision.add_value'), onChange: (items) => onChange(items.map((item) => item.label)) }));
96
+ }
97
+ const columns = tableColumns(value);
98
+ const add = _jsx(AddRow, { field: label, onAdd: () => onChange([...value, blankLike(value.at(-1))]) });
99
+ const remove = (index) => (_jsx(RemoveRow, { index: index, onRemove: () => {
100
+ keyed.drop(index);
101
+ onChange(value.filter((_, at) => at !== index));
102
+ } }));
103
+ if (columns) {
104
+ const grid = {
105
+ display: 'grid',
106
+ gap: 8,
107
+ alignItems: 'start',
108
+ gridTemplateColumns: `${columns.map((column) => `minmax(0, ${String(column.weight)}fr)`).join(' ')} auto`,
109
+ };
110
+ const rows = value;
111
+ return (_jsxs(Labelled, { label: label, children: [_jsxs("div", { style: grid, "aria-hidden": true, children: [columns.map((column) => (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: humanize(column.key) }, column.key))), _jsx("span", { style: { width: 28 } })] }), keyed.rows.map(({ index, key }) => {
112
+ const row = rows[index];
113
+ return (_jsxs("div", { style: grid, children: [columns.map((column) => (_jsx(PlainField, { label: `${humanize(column.key)} ${String(index + 1)}`, isLabelHidden: true, value: row[column.key] ?? null, onChange: (next) => onChange(rows.map((old, at) => (at === index ? { ...old, [column.key]: next } : old))) }, column.key))), remove(index)] }, key));
114
+ }), add] }));
115
+ }
116
+ return (_jsx(Group, { label: label, children: _jsxs(VStack, { gap: 3, children: [keyed.rows.map(({ item: entry, index, key }) => {
117
+ const item = t('@theorem.data.item', { index: String(index + 1) });
118
+ const set = (next) => onChange(value.map((old, at) => (at === index ? next : old)));
119
+ return (_jsxs(HStack, { gap: 2, vAlign: "start", children: [_jsx(StackItem, { size: "fill", children: isPlain(entry) ? (_jsx(PlainField, { label: item, isLabelHidden: true, value: entry, onChange: set })) : (_jsx(Field, { label: item, value: entry, onChange: set })) }), remove(index)] }, key));
120
+ }), add] }) }));
121
+ }
122
+ function Field({ value, onChange, label }) {
123
+ if (Array.isArray(value))
124
+ return _jsx(ListField, { label: label, value: value, onChange: onChange });
125
+ if (value !== null && typeof value === 'object') {
126
+ return (_jsx(Group, { label: label, children: _jsx(ObjectFields, { value: value, onChange: onChange }) }));
127
+ }
128
+ return _jsx(PlainField, { label: label, value: value, onChange: onChange });
129
+ }
130
+ /**
131
+ * A decision's state edited field by field: objects as labelled fields, lists
132
+ * of words as tokens, other lists as rows. The shape itself (new keys, other
133
+ * types) is edited as JSON.
134
+ */
135
+ export function StateFields({ value, onChange, label }) {
136
+ if (value !== null && typeof value === 'object' && !Array.isArray(value))
137
+ return _jsx(ObjectFields, { value: value, onChange: onChange });
138
+ return _jsx(Field, { label: label, value: value, onChange: onChange });
139
+ }