@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,343 @@
1
+ /**
2
+ * The rules `ShapedData` draws a payload by, as plain decisions: which shape a
3
+ * value takes, what a row is called, what a field's label and unit are. The
4
+ * component only maps each decision to its Astryx part.
5
+ */
6
+ /** Fields that name a row, in the order they're tried. */
7
+ const TITLE_KEYS = ['name', 'title', 'label', 'headline', 'subject', 'id'];
8
+ /** Fields that place or sort a row, shown as tokens beside its title. */
9
+ const TAG_KEYS = [
10
+ 'country',
11
+ 'region',
12
+ 'state',
13
+ 'admin1',
14
+ 'city',
15
+ 'category',
16
+ 'type',
17
+ 'kind',
18
+ 'status',
19
+ ];
20
+ const MAX_TAGS = 2;
21
+ const MAX_TAG_LENGTH = 24;
22
+ /** Fields that say, in a line, what a row is. */
23
+ const DESCRIPTION_KEYS = [
24
+ 'description',
25
+ 'summary',
26
+ 'snippet',
27
+ 'extract',
28
+ 'subtitle',
29
+ 'address',
30
+ 'display_name',
31
+ 'text',
32
+ ];
33
+ /** Fields that link a row to its page. */
34
+ const LINK_KEYS = ['url', 'link', 'href'];
35
+ /** Fields a list row shows beyond its name, line, link, and tags, at its end. */
36
+ const MAX_LIST_EXTRAS = 2;
37
+ /** A list of flat objects with at most this many fields reads as a table. */
38
+ const MAX_TABLE_COLUMNS = 6;
39
+ /** Rows shown before the rest are left to the JSON view. */
40
+ export const MAX_ROWS = 50;
41
+ /** A list of plain values this short sits inline as tokens beside the other fields. */
42
+ const MAX_INLINE_TOKENS = 8;
43
+ /** Levels drawn before a subtree shows as JSON. */
44
+ const MAX_DEPTH = 8;
45
+ export const ISO_DATE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
46
+ export function isRow(value) {
47
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
48
+ }
49
+ export function isPlain(value) {
50
+ return value === null || typeof value !== 'object';
51
+ }
52
+ const UNREADABLE = '[Unreadable]';
53
+ const NO_TO_JSON = Symbol('no toJSON');
54
+ /** A read that throws reads as a marker. */
55
+ function readSafe(read) {
56
+ try {
57
+ return read();
58
+ }
59
+ catch {
60
+ return UNREADABLE;
61
+ }
62
+ }
63
+ /** What the value's own toJSON gives, made safe; NO_TO_JSON when it has none. */
64
+ function viaToJSON(value, inside) {
65
+ const own = value.toJSON;
66
+ return typeof own === 'function' ? jsonSafe(own.call(value), inside) : NO_TO_JSON;
67
+ }
68
+ /**
69
+ * What JSON.stringify can't take, made plain: a cycle, a BigInt, a property
70
+ * that throws when read. Each becomes a marker in place, so the rest still shows.
71
+ */
72
+ function jsonSafe(value, ancestors = []) {
73
+ if (typeof value === 'bigint')
74
+ return String(value);
75
+ if (value === null || typeof value !== 'object')
76
+ return value;
77
+ if (ancestors.includes(value))
78
+ return '[Circular]';
79
+ const inside = [...ancestors, value];
80
+ const converted = readSafe(() => viaToJSON(value, inside));
81
+ if (converted !== NO_TO_JSON)
82
+ return converted;
83
+ if (Array.isArray(value))
84
+ return value.map((item) => jsonSafe(item, inside));
85
+ return Object.fromEntries(Object.keys(value).map((key) => [key, readSafe(() => jsonSafe(value[key], inside))]));
86
+ }
87
+ /** The value as indented JSON, whatever it holds. */
88
+ export function json(value) {
89
+ try {
90
+ return JSON.stringify(value, null, 2) ?? String(value);
91
+ }
92
+ catch {
93
+ return JSON.stringify(jsonSafe(value), null, 2) ?? String(value);
94
+ }
95
+ }
96
+ /** A string that holds a JSON object or list, parsed; anything else as it came. */
97
+ export function unpacked(value) {
98
+ if (typeof value !== 'string')
99
+ return value;
100
+ const text = value.trim();
101
+ if (!/^[[{]/.test(text))
102
+ return value;
103
+ try {
104
+ return JSON.parse(text);
105
+ }
106
+ catch {
107
+ return value;
108
+ }
109
+ }
110
+ /** `country_code`, `countryCode` → "Country code". */
111
+ export function humanize(key) {
112
+ const words = key
113
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
114
+ .replace(/([a-zA-Z])(\d)/g, '$1 $2')
115
+ .replace(/[_-]+/g, ' ')
116
+ .trim()
117
+ .toLowerCase();
118
+ return words.charAt(0).toUpperCase() + words.slice(1);
119
+ }
120
+ /** A unit worth printing: a symbol or abbreviation, not a format name like `iso8601` or `wmo code`. */
121
+ export function printableUnit(unit) {
122
+ if (typeof unit !== 'string' || unit === '' || /\s/.test(unit) || unit === 'iso8601')
123
+ return undefined;
124
+ return unit;
125
+ }
126
+ /** Unit suffixes a key can end in (`distance_m`, `duration_ms`), and the unit each prints as. */
127
+ const SUFFIX_UNITS = {
128
+ m: 'm',
129
+ km: 'km',
130
+ mi: 'mi',
131
+ ms: 'ms',
132
+ s: 's',
133
+ sec: 's',
134
+ min: 'min',
135
+ h: 'h',
136
+ kg: 'kg',
137
+ g: 'g',
138
+ pct: '%',
139
+ percent: '%',
140
+ c: '°C',
141
+ f: '°F',
142
+ usd: 'USD',
143
+ eur: 'EUR',
144
+ gbp: 'GBP',
145
+ jpy: 'JPY',
146
+ };
147
+ /** The unit a key's suffix names, and the key without it: `distance_m` → Distance, m. */
148
+ function suffixUnit(key) {
149
+ const match = /^(.+?)[_-]([a-z]+)$/i.exec(key);
150
+ const unit = match ? SUFFIX_UNITS[(match[2] ?? '').toLowerCase()] : undefined;
151
+ return match?.[1] && unit ? { label: humanize(match[1]), unit } : undefined;
152
+ }
153
+ /** A column's label and unit: from its `<key>_units` entry, else its key's suffix. */
154
+ export function fieldLabel(key, units) {
155
+ const listed = printableUnit(units?.[key]);
156
+ if (listed)
157
+ return { label: humanize(key), unit: listed };
158
+ return suffixUnit(key) ?? { label: humanize(key) };
159
+ }
160
+ /** A field's label and the unit its value reads with. A unit only reads onto a number: `country_code` stays "Country code". */
161
+ export function fieldReading(key, value, units) {
162
+ if (typeof value !== 'number')
163
+ return { label: humanize(key) };
164
+ return fieldLabel(key, units);
165
+ }
166
+ /** The `<key>_units` object beside `key`, if there is one. */
167
+ export function unitsOf(row, key) {
168
+ const units = row[`${key}_units`];
169
+ return isRow(units) ? units : undefined;
170
+ }
171
+ /** A row's fields, less the units objects its other fields read. */
172
+ export function shownEntries(row) {
173
+ return Object.entries(row).filter(([key, value]) => !(key.endsWith('_units') && isRow(value) && key.slice(0, -'_units'.length) in row));
174
+ }
175
+ /** A string that's a date: a bare day or a moment. */
176
+ function dateReading(text) {
177
+ if (!ISO_DATE.test(text))
178
+ return undefined;
179
+ const date = new Date(text);
180
+ if (Number.isNaN(date.getTime()))
181
+ return undefined;
182
+ return text.includes('T') ? { kind: 'date-time', date } : { kind: 'date', date };
183
+ }
184
+ export function plainReading(value, unit) {
185
+ if (value === null || value === undefined)
186
+ return { kind: 'none' };
187
+ if (typeof value === 'boolean')
188
+ return { kind: 'boolean', value };
189
+ if (typeof value === 'number')
190
+ return unit ? { kind: 'number', value, unit } : { kind: 'number', value };
191
+ const text = String(value);
192
+ if (/^https?:\/\/\S+$/.test(text))
193
+ return { kind: 'link', href: text };
194
+ return dateReading(text) ?? { kind: 'text', text };
195
+ }
196
+ /** A formatted number with its unit: symbols sit close (21°C, 35%), words stand apart (320 m). */
197
+ export function withUnit(number, unit) {
198
+ if (!unit)
199
+ return number;
200
+ return /^[°%]/.test(unit) ? `${number}${unit}` : `${number} ${unit}`;
201
+ }
202
+ /** The field that names a row, if it has one. */
203
+ export function titleKey(row) {
204
+ return TITLE_KEYS.find((key) => typeof row[key] === 'string' || typeof row[key] === 'number');
205
+ }
206
+ /** Up to two short strings that place or sort a row. */
207
+ function tags(row, skip) {
208
+ return TAG_KEYS.filter((key) => {
209
+ const value = row[key];
210
+ return (key !== skip &&
211
+ typeof value === 'string' &&
212
+ value.length > 0 &&
213
+ value.length <= MAX_TAG_LENGTH);
214
+ })
215
+ .slice(0, MAX_TAGS)
216
+ .map((key) => row[key]);
217
+ }
218
+ /** A flat row's parts, if a name and a line say what it is. */
219
+ function listRow(item) {
220
+ if (!isRow(item) || !Object.values(item).every(isPlain))
221
+ return undefined;
222
+ const title = titleKey(item);
223
+ const description = DESCRIPTION_KEYS.find((key) => typeof item[key] === 'string' && item[key] !== '');
224
+ if (!title || !description)
225
+ return undefined;
226
+ const link = LINK_KEYS.find((key) => typeof item[key] === 'string' && /^https?:\/\//.test(item[key]));
227
+ const rowTags = tags(item, title);
228
+ const shown = new Set([title, description, link]);
229
+ const extras = Object.entries(item).filter(([key, value]) => !shown.has(key) && !(TAG_KEYS.includes(key) && rowTags.includes(value)));
230
+ if (extras.length > MAX_LIST_EXTRAS)
231
+ return undefined;
232
+ return {
233
+ title: String(item[title]),
234
+ description: item[description],
235
+ href: link ? item[link] : undefined,
236
+ tags: rowTags,
237
+ extras,
238
+ };
239
+ }
240
+ /** A collapsible row's heading and what it opens to: a titled row loses its title field; else it's named by its place. */
241
+ export function rowHeading(item) {
242
+ if (!isRow(item))
243
+ return { tags: [], rest: item };
244
+ const key = titleKey(item);
245
+ if (!key)
246
+ return { tags: tags(item, undefined), rest: item };
247
+ return {
248
+ title: String(item[key]),
249
+ tags: tags(item, key),
250
+ rest: Object.fromEntries(Object.entries(item).filter(([k]) => k !== key)),
251
+ };
252
+ }
253
+ /** Small flat objects sharing their fields: a table's rows. */
254
+ function tableColumns(items) {
255
+ if (items.length < 2 || !items.every((item) => isRow(item) && Object.values(item).every(isPlain)))
256
+ return undefined;
257
+ const columns = [...new Set(items.flatMap((item) => Object.keys(item)))];
258
+ return columns.length <= MAX_TABLE_COLUMNS ? columns : undefined;
259
+ }
260
+ /** An object of equal-length lists of plain values (a column store): a table's columns. */
261
+ export function columnStore(row) {
262
+ const entries = shownEntries(row);
263
+ if (entries.length < 2)
264
+ return undefined;
265
+ const lengths = new Set(entries.map(([, value]) => (Array.isArray(value) && value.every(isPlain) ? value.length : -1)));
266
+ const [length] = lengths;
267
+ return lengths.size === 1 && length !== undefined && length >= 2
268
+ ? entries.map(([key]) => key)
269
+ : undefined;
270
+ }
271
+ /** A column store's lists, turned into rows. */
272
+ export function storeRows(row, columns) {
273
+ const lists = columns.map((key) => row[key]);
274
+ return (lists[0] ?? []).map((_, index) => Object.fromEntries(columns.map((key, k) => [key, lists[k]?.[index]])));
275
+ }
276
+ function listShape(items) {
277
+ if (items.length === 0)
278
+ return { kind: 'none' };
279
+ if (items.every(isPlain))
280
+ return { kind: 'tokens', items };
281
+ const listed = items.map(listRow);
282
+ if (listed.every((row) => row !== undefined))
283
+ return { kind: 'list', rows: listed };
284
+ const columns = tableColumns(items);
285
+ return columns ? { kind: 'table', columns, rows: items } : { kind: 'rows', items };
286
+ }
287
+ function rowShape(row) {
288
+ if (Object.keys(row).length === 0)
289
+ return { kind: 'none' };
290
+ const store = columnStore(row);
291
+ return store
292
+ ? { kind: 'table', columns: store, rows: storeRows(row, store) }
293
+ : { kind: 'fields', row };
294
+ }
295
+ /** How a value (already unpacked) at `depth` is drawn. */
296
+ export function shapeOf(value, depth) {
297
+ if (depth > MAX_DEPTH)
298
+ return { kind: 'json' };
299
+ if (Array.isArray(value))
300
+ return listShape(value);
301
+ return isRow(value) ? rowShape(value) : { kind: 'plain' };
302
+ }
303
+ /** Text this long, or on several lines, reads as prose: full width, as Markdown. */
304
+ const PROSE_CHARS = 160;
305
+ export function isProse(value) {
306
+ return typeof value === 'string' && (value.length > PROSE_CHARS || value.includes('\n'));
307
+ }
308
+ /** A field that sits in the field list: a plain value (prose aside), or a short list of them. */
309
+ function isInline(value) {
310
+ const inner = unpacked(value);
311
+ if (isProse(inner))
312
+ return false;
313
+ if (isPlain(inner))
314
+ return true;
315
+ return (Array.isArray(inner) &&
316
+ inner.length > 0 &&
317
+ inner.length <= MAX_INLINE_TOKENS &&
318
+ inner.every(isPlain));
319
+ }
320
+ function section(row, key) {
321
+ const path = [key];
322
+ let inner = unpacked(row[key]);
323
+ let units = unitsOf(row, key);
324
+ while (isRow(inner)) {
325
+ // why: A units object beside the one field doesn't count: `{ hourly, hourly_units }` is still a wrapper.
326
+ const entries = shownEntries(inner);
327
+ const only = entries.length === 1 ? entries[0] : undefined;
328
+ if (!only || isPlain(unpacked(only[1])))
329
+ break;
330
+ path.push(only[0]);
331
+ units = unitsOf(inner, only[0]);
332
+ inner = unpacked(only[1]);
333
+ }
334
+ return { key, title: path.map(humanize).join(' › '), value: inner, units };
335
+ }
336
+ /** An object's fields split: plain ones for the field list, nested ones as sections. */
337
+ export function splitFields(row) {
338
+ const entries = shownEntries(row);
339
+ return {
340
+ plain: entries.filter(([, value]) => isInline(value)),
341
+ sections: entries.filter(([, value]) => !isInline(value)).map(([key]) => section(row, key)),
342
+ };
343
+ }
@@ -0,0 +1,13 @@
1
+ import type { TranscriptBlock } from '@theoremjs/agents/interface';
2
+ /** One source as a citation: its title, and when it's on the web, its link and favicon. */
3
+ export type SourceCitation = {
4
+ key: string;
5
+ title: string;
6
+ href?: string;
7
+ icon?: string;
8
+ };
9
+ export type SourceCitationBlock = Extract<TranscriptBlock, {
10
+ kind: 'citation';
11
+ }>;
12
+ /** A citation's sources, in the order they were cited. */
13
+ export declare function citationsFromBlock(block: SourceCitationBlock): SourceCitation[];
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Gemini grounding links through this host rather than to the page, and names
3
+ * the page's own site in the source title instead.
4
+ */
5
+ const GROUNDING_REDIRECT_HOST = 'vertexaisearch.cloud.google.com';
6
+ /** A citation's sources, in the order they were cited. */
7
+ export function citationsFromBlock(block) {
8
+ return block.sources.map((source, i) => citationFromSource(source, `c-${String(i)}`));
9
+ }
10
+ function citationFromSource(source, key) {
11
+ const url = webUrl(source.uri);
12
+ const site = url && siteOf(url, source.title);
13
+ return {
14
+ key,
15
+ title: source.title.trim() || hostLabel(source.uri) || source.type,
16
+ ...(url ? { href: url.href } : {}),
17
+ ...(site ? { icon: faviconUrl(site) } : {}),
18
+ };
19
+ }
20
+ /**
21
+ * A source is linked only at an http(s) URL. Sources come from the model and
22
+ * from tool output, so a `javascript:`, `data:`, or other scheme never becomes
23
+ * a clickable link.
24
+ */
25
+ function webUrl(uri) {
26
+ if (!URL.canParse(uri))
27
+ return undefined;
28
+ const url = new URL(uri);
29
+ return url.protocol === 'https:' || url.protocol === 'http:' ? url : undefined;
30
+ }
31
+ /** The site a source is on: the link's host, or for a grounding redirect, the site its title names. */
32
+ function siteOf(url, title) {
33
+ if (url.hostname !== GROUNDING_REDIRECT_HOST)
34
+ return url.hostname;
35
+ const named = title.trim().toLowerCase();
36
+ if (!named.includes('.') || !URL.canParse(`https://${named}`))
37
+ return undefined;
38
+ return new URL(`https://${named}`).hostname === named ? named : undefined;
39
+ }
40
+ /** Google's favicon service; the visitor's browser sends it the site's host, never the page. */
41
+ function faviconUrl(site) {
42
+ return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(site)}&sz=32`;
43
+ }
44
+ function hostLabel(uri) {
45
+ try {
46
+ return new URL(uri).hostname.replace(/^www\./, '');
47
+ }
48
+ catch {
49
+ return truncate(uri, 40);
50
+ }
51
+ }
52
+ function truncate(value, max) {
53
+ if (value.length <= max)
54
+ return value;
55
+ return `${value.slice(0, max - 1)}…`;
56
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * What the embedding space draws, as flat arrays a GPU takes as they are:
3
+ * the axes' three circles, each document's dot, a line for each link, heavier
4
+ * the further its cosine similarity passes the threshold, and a query's white
5
+ * dot with a line to each match that passes its own.
6
+ * Every other line joins two real points and stands for their cosine
7
+ * similarity; nothing is drawn for show.
8
+ *
9
+ * @module
10
+ */
11
+ import type { EmbeddingSpace, SpacePoint } from './embedding-space.ts';
12
+ /**
13
+ * Floats per line segment: from (3), to (3), colour (3), alpha, width in CSS
14
+ * pixels, the two slots it reads its state from, where it sits along its line
15
+ * (0 at the start, 1 at the end), and the gap in CSS pixels it leaves before
16
+ * its start and after its end, where a dot sits.
17
+ */
18
+ export declare const SEGMENT_FLOATS = 16;
19
+ /** Floats per dot: position (3), colour (3), alpha, radius in CSS pixels, its slot, and where it sits along the line that reaches it. */
20
+ export declare const DOT_FLOATS = 10;
21
+ /** Stands for the query where a slot would go. */
22
+ export declare const QUERY_SLOT = -1;
23
+ /** Stands for the axes' circles, which no document's state reaches. */
24
+ export declare const FRAME_SLOT = -2;
25
+ /** Line segments and dots, packed as {@link SEGMENT_FLOATS} and {@link DOT_FLOATS} describe. */
26
+ export type SpaceGeometry = {
27
+ segments: Float32Array;
28
+ dots: Float32Array;
29
+ };
30
+ /** Everything a space draws before any query: the axes' circles, a line for each link, then every document's dot. */
31
+ export declare function spaceGeometry(space: EmbeddingSpace): SpaceGeometry;
32
+ /** A match the query is joined to. */
33
+ export type QueryMatch = {
34
+ index: number;
35
+ /** How far its similarity passes the threshold it stood out against, from 0 at it to 1 at identical. */
36
+ closeness: number;
37
+ };
38
+ /** A query's white dot where it landed, and a line from it to each match, weighted as a link is. */
39
+ export declare function queryGeometry(space: EmbeddingSpace, at: SpacePoint, matches: readonly QueryMatch[]): SpaceGeometry;
@@ -0,0 +1,183 @@
1
+ /**
2
+ * What the embedding space draws, as flat arrays a GPU takes as they are:
3
+ * the axes' three circles, each document's dot, a line for each link, heavier
4
+ * the further its cosine similarity passes the threshold, and a query's white
5
+ * dot with a line to each match that passes its own.
6
+ * Every other line joins two real points and stands for their cosine
7
+ * similarity; nothing is drawn for show.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Floats per line segment: from (3), to (3), colour (3), alpha, width in CSS
13
+ * pixels, the two slots it reads its state from, where it sits along its line
14
+ * (0 at the start, 1 at the end), and the gap in CSS pixels it leaves before
15
+ * its start and after its end, where a dot sits.
16
+ */
17
+ export const SEGMENT_FLOATS = 16;
18
+ /** Floats per dot: position (3), colour (3), alpha, radius in CSS pixels, its slot, and where it sits along the line that reaches it. */
19
+ export const DOT_FLOATS = 10;
20
+ /** Stands for the query where a slot would go. */
21
+ export const QUERY_SLOT = -1;
22
+ /** Stands for the axes' circles, which no document's state reaches. */
23
+ export const FRAME_SLOT = -2;
24
+ /** Every cluster's colour has this lightness and chroma, so none reads dirtier or louder than another. */
25
+ const LIGHTNESS = 0.72;
26
+ const CHROMA = 0.085;
27
+ const QUERY_COLOUR = [0.97, 0.98, 1.0];
28
+ /**
29
+ * Every line is a faint hairline in its documents' colour: it hints at a
30
+ * connection, and the dots carry the picture. Its width in CSS pixels and its
31
+ * opacity, at the threshold and at identical.
32
+ */
33
+ const LINE_WIDTH = [0.3, 0.5];
34
+ const LINE_ALPHA = [0.18, 0.4];
35
+ function weighted([at, to], strength) {
36
+ return at + (to - at) * strength;
37
+ }
38
+ function gamma(linear) {
39
+ return linear <= 0.0031308 ? 12.92 * linear : 1.055 * linear ** (1 / 2.4) - 0.055;
40
+ }
41
+ /** An OKLCH colour as sRGB from 0 to 1, or `null` when the screen cannot show it. */
42
+ function oklch(l, c, hue) {
43
+ const a = c * Math.cos(hue);
44
+ const b = c * Math.sin(hue);
45
+ const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
46
+ const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
47
+ const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
48
+ const rgb = [
49
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
50
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
51
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_,
52
+ ];
53
+ if (rgb.some((v) => v < -1e-4 || v > 1 + 1e-4))
54
+ return null;
55
+ return rgb.map((v) => gamma(Math.min(1, Math.max(0, v))));
56
+ }
57
+ /** A cluster's colour from its tone, a hue from 0 to 1, at the palette's one lightness; chroma gives way only where the screen runs out. */
58
+ function colourOf(tone) {
59
+ const hue = tone * 2 * Math.PI;
60
+ for (let c = CHROMA; c > 0; c -= 0.005) {
61
+ const colour = oklch(LIGHTNESS, c, hue);
62
+ if (colour)
63
+ return colour;
64
+ }
65
+ return oklch(LIGHTNESS, 0, hue) ?? [0.8, 0.8, 0.8];
66
+ }
67
+ function mixed(a, b, u) {
68
+ return [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u, a[2] + (b[2] - a[2]) * u];
69
+ }
70
+ /** The longest piece a line is cut into, so depth fades along it, not only at its ends. */
71
+ const PIECE = 0.1;
72
+ /** Points along the straight line from one point to another, through the ball. */
73
+ function pieces(a, b) {
74
+ const steps = Math.max(1, Math.ceil(Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]) / PIECE));
75
+ return Array.from({ length: steps + 1 }, (_, s) => mixed(a, b, s / steps));
76
+ }
77
+ class Strokes {
78
+ segments = [];
79
+ dots = [];
80
+ line({ from, to, colour, alpha, width, slots, gaps }) {
81
+ const points = pieces(from, to);
82
+ const last = points.length - 1;
83
+ for (let i = 1; i <= last; i += 1) {
84
+ const u = i / last;
85
+ this.segments.push(...points[i - 1], ...points[i], ...colour((i - 0.5) / last), alpha, width, slots[0], slots[1], u, i === 1 ? gaps[0] : 0, i === last ? gaps[1] : 0);
86
+ }
87
+ }
88
+ /** One segment no document's state reaches, always shown, with no gaps. */
89
+ segment(from, to, colour, alpha, width) {
90
+ this.segments.push(...from, ...to, ...colour, alpha, width, FRAME_SLOT, FRAME_SLOT, 0, 0, 0);
91
+ }
92
+ dot({ at, colour, alpha, radius, slot, along }) {
93
+ this.dots.push(...at, ...colour, alpha, radius, slot, along);
94
+ }
95
+ done() {
96
+ return { segments: new Float32Array(this.segments), dots: new Float32Array(this.dots) };
97
+ }
98
+ }
99
+ /** A document's dot, and the query's, a little larger, in CSS pixels. */
100
+ const DOCUMENT_RADIUS = 2.6;
101
+ const QUERY_RADIUS = 3.6;
102
+ /** The clear space between a line's end and the dot it reaches. */
103
+ const GAP = 1.75;
104
+ /** How far a link passes the threshold, from 0 at it to 1 at identical. */
105
+ function strengthOf(space, similarity) {
106
+ if (space.threshold >= 1)
107
+ return 1;
108
+ return Math.min(Math.max((similarity - space.threshold) / (1 - space.threshold), 0), 1);
109
+ }
110
+ /** The axes' circles: a hairline round the rim in each plane two of the three axes span. */
111
+ const FRAME_COLOUR = [0.88, 0.9, 0.94];
112
+ /** At the ball's front; the renderer fades a circle with the square of its depth, toward a quarter of this at the back. */
113
+ const FRAME_ALPHA = 0.29;
114
+ const FRAME_WIDTH = 0.7;
115
+ const FRAME_STEPS = 96;
116
+ function frame(strokes) {
117
+ const planes = [
118
+ (a) => [Math.cos(a), Math.sin(a), 0],
119
+ (a) => [Math.cos(a), 0, Math.sin(a)],
120
+ (a) => [0, Math.cos(a), Math.sin(a)],
121
+ ];
122
+ for (const at of planes)
123
+ for (let i = 0; i < FRAME_STEPS; i += 1)
124
+ strokes.segment(at((i / FRAME_STEPS) * 2 * Math.PI), at(((i + 1) / FRAME_STEPS) * 2 * Math.PI), FRAME_COLOUR, FRAME_ALPHA, FRAME_WIDTH);
125
+ }
126
+ /** Everything a space draws before any query: the axes' circles, a line for each link, then every document's dot. */
127
+ export function spaceGeometry(space) {
128
+ const strokes = new Strokes();
129
+ frame(strokes);
130
+ const colourOfDocument = (index) => colourOf(space.clusters[space.documents[index].cluster].tone);
131
+ for (const link of space.links) {
132
+ const strength = strengthOf(space, link.similarity);
133
+ // invariant: a link's two documents are in one cluster, so it takes their colour.
134
+ const colour = colourOfDocument(link.a);
135
+ strokes.line({
136
+ from: space.documents[link.a].position,
137
+ to: space.documents[link.b].position,
138
+ colour: () => colour,
139
+ alpha: weighted(LINE_ALPHA, strength),
140
+ width: weighted(LINE_WIDTH, strength),
141
+ slots: [link.a, link.b],
142
+ gaps: [DOCUMENT_RADIUS + GAP, DOCUMENT_RADIUS + GAP],
143
+ });
144
+ }
145
+ for (const document of space.documents)
146
+ strokes.dot({
147
+ at: document.position,
148
+ colour: colourOfDocument(document.index),
149
+ alpha: 1,
150
+ radius: DOCUMENT_RADIUS,
151
+ slot: document.index,
152
+ along: 0,
153
+ });
154
+ return strokes.done();
155
+ }
156
+ /** A query's white dot where it landed, and a line from it to each match, weighted as a link is. */
157
+ export function queryGeometry(space, at, matches) {
158
+ const strokes = new Strokes();
159
+ for (const match of matches) {
160
+ const document = space.documents[match.index];
161
+ if (!document)
162
+ continue;
163
+ const colour = colourOf(space.clusters[document.cluster].tone);
164
+ strokes.line({
165
+ from: at,
166
+ to: document.position,
167
+ colour: (u) => mixed(QUERY_COLOUR, colour, u),
168
+ alpha: weighted(LINE_ALPHA, match.closeness),
169
+ width: weighted(LINE_WIDTH, match.closeness),
170
+ slots: [QUERY_SLOT, match.index],
171
+ gaps: [QUERY_RADIUS + GAP, DOCUMENT_RADIUS + GAP],
172
+ });
173
+ }
174
+ strokes.dot({
175
+ at,
176
+ colour: QUERY_COLOUR,
177
+ alpha: 1,
178
+ radius: QUERY_RADIUS,
179
+ slot: QUERY_SLOT,
180
+ along: 0,
181
+ });
182
+ return strokes.done();
183
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The rules behind editing a decision's state as fields: which lists read as
3
+ * words, and what a new row in a list starts as.
4
+ *
5
+ * @module
6
+ */
7
+ import type { DecisionJson } from '@theoremjs/agents';
8
+ /** A list edited as tokens: empty, or every entry a string. */
9
+ export declare function isWordList(value: DecisionJson[]): value is string[];
10
+ /** A new row shaped like `like`: its fields kept, its values emptied. */
11
+ export declare function blankLike(like: DecisionJson | undefined): DecisionJson;
12
+ /** Columns a list of rows edits as a table in, or `null` when its rows are not all small, flat, and alike. */
13
+ export declare function tableColumns(value: DecisionJson[]): {
14
+ key: string;
15
+ weight: number;
16
+ }[] | null;