@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,122 @@
1
+ import type { DefinedTheme } from '@astryxdesign/core/theme';
2
+ import { type ReactNode, type Ref } from 'react';
3
+ import type { PageTools } from '../client/live/live-page-tool.ts';
4
+ import { type ConversationView, type HttpTransportOptions, type TheoremTransport } from '../client/transport.ts';
5
+ import { type SentTurn, type UseTheoremChatOptions } from '../hooks/use-theorem-chat.ts';
6
+ import type { TheoremLabels } from './labels.ts';
7
+ export type TheoremChatProps = {
8
+ /** Where `createTheoremHandler` is mounted. Default `/api/theorem`. Ignored when `transport` is set. */
9
+ endpoint?: string;
10
+ /** Extra fetch options for the default HTTP transport (auth headers, custom fetch). */
11
+ http?: Omit<HttpTransportOptions, 'endpoint'>;
12
+ /** Bring your own transport (tests, studios, non-HTTP hosts). */
13
+ transport?: TheoremTransport;
14
+ /** Astryx theme. Omit to inherit the host's `<Theme>` or use `theoremTheme`. */
15
+ theme?: DefinedTheme;
16
+ mode?: 'system' | 'light' | 'dark';
17
+ /**
18
+ * Replacement lines by locale, for any `@theorem.*` key in
19
+ * `THEOREM_UI_CATALOG` or any of Astryx's `@astryx.*` keys. The locale is
20
+ * the host's Astryx `InternationalizationProvider` locale (default `en`).
21
+ */
22
+ labels?: TheoremLabels;
23
+ /**
24
+ * Colour code the reply left untagged: a fence with no language is read and, when it is plainly
25
+ * one of JavaScript, TypeScript, Python, shell, JSON, CSS, HTML, XML, YAML, or Markdown, shown as it.
26
+ * A fence with a language is never changed. Off by default.
27
+ */
28
+ detectCodeLanguage?: boolean;
29
+ /**
30
+ * The value chosen for each of the profile's `inputs.slots`, sent with every turn. A slot you
31
+ * name is fixed to that value; one you leave out gets a selector in the composer bar, beside
32
+ * the model's, starting at its first allowed value.
33
+ */
34
+ slots?: Record<string, string>;
35
+ /** Told each time the visitor chooses a slot in the composer, with every slot's value. */
36
+ onSlotsChange?: (slots: Record<string, string>) => void;
37
+ /**
38
+ * What the page wants the agent to know: any JSON. The latest value goes with every
39
+ * turn, and the profile's `inputs.context.from` must list `client`.
40
+ */
41
+ context?: unknown;
42
+ /**
43
+ * The page's tools, by name: each answers a tool the profile declares with
44
+ * `answeredBy: 'page'`. The reply waits while the page answers; no one is asked.
45
+ */
46
+ pageTools?: PageTools;
47
+ /** Every event of every turn, as it arrives: also the ones the chat shows nothing for. */
48
+ onTurnEvent?: UseTheoremChatOptions['onTurnEvent'];
49
+ placeholder?: string;
50
+ /** Shown above the centred composer before the first message. Default: the agent's handle and a prompt. */
51
+ emptyState?: ReactNode;
52
+ density?: 'compact' | 'balanced' | 'spacious';
53
+ /**
54
+ * Widest the transcript and composer may grow, as a CSS length. Default
55
+ * {@link DEFAULT_CHAT_MAX_WIDTH}: 50% of the chat, or full width on narrow screens.
56
+ */
57
+ maxWidth?: string;
58
+ /** Page scroller when the chat is not its own scroll container (e.g. `document.documentElement`). */
59
+ scrollRef?: React.RefObject<HTMLElement | null>;
60
+ /**
61
+ * Drive the trace from the host's own control. The built-in toggle hides.
62
+ * The open trace takes the chat's place, unless this page is inside
63
+ * `TracePlacement value="panel"`, where it docks. Omit to keep the toggle.
64
+ * Needs a profile that records traces.
65
+ */
66
+ trace?: boolean;
67
+ /**
68
+ * Show what was used, as tokens and cost: each reply's total under it, and
69
+ * on an agent tool's call row what the agent it ran used. Off by default.
70
+ */
71
+ usage?: boolean;
72
+ className?: string;
73
+ style?: React.CSSProperties;
74
+ /**
75
+ * The ref of a conversation to resume, as `onConversationChange` reported it. Read once, when
76
+ * the chat mounts: the chat draws the conversation as the host keeps it.
77
+ */
78
+ conversation?: string;
79
+ /**
80
+ * The conversation under `conversation` as the host showed it with the ref, as
81
+ * `startConversation` returns it. Read once, when the chat mounts: the chat draws it at once,
82
+ * and does not read it from the host again.
83
+ */
84
+ conversationView?: ConversationView;
85
+ /** The text the composer starts with, for the user to send or change. Read once, when the chat mounts. */
86
+ initialText?: string;
87
+ /**
88
+ * Called with each new ref the host issues for the conversation: once the message posts, and
89
+ * again as the reply's runs are saved. Keep the latest to resume the chat later.
90
+ */
91
+ onConversationChange?: (ref: string) => void;
92
+ /** Lets the host send a message as the composer would; see {@link TheoremChatHandle}. */
93
+ chatRef?: Ref<TheoremChatHandle>;
94
+ /**
95
+ * `false` leaves the composer out: the chat is the transcript alone, and your own control sends
96
+ * through `chatRef`. Gates are still answered in the transcript. Default `true`.
97
+ */
98
+ composer?: boolean;
99
+ };
100
+ /** What a host can do to a mounted {@link TheoremChat}. */
101
+ export type TheoremChatHandle = {
102
+ /**
103
+ * Sends `text` and optional `files` as the user, and resolves with the blocks the turn added once the reply is done;
104
+ * `null` when the chat can't take a message now (a reply is streaming, or waits on a gate).
105
+ */
106
+ send: (text: string, files?: readonly File[]) => Promise<SentTurn | null>;
107
+ /** Stops the reply that is streaming, as the composer's stop button does. */
108
+ stop: () => void;
109
+ };
110
+ /**
111
+ * 50% of the chat's width, but never narrower than 640px (or the full width,
112
+ * whichever is smaller) — so phones and narrow panes get 100%.
113
+ */
114
+ export declare const DEFAULT_CHAT_MAX_WIDTH = "max(50%, min(100%, 640px))";
115
+ /**
116
+ * Drop-in chat for a profile served by `createTheoremHandler`:
117
+ *
118
+ * ```tsx
119
+ * <TheoremChat endpoint="/api/theorem" />
120
+ * ```
121
+ */
122
+ export declare function TheoremChat({ endpoint, http, transport, theme, mode, labels, detectCodeLanguage, ...rest }: TheoremChatProps): import("react").JSX.Element;
@@ -0,0 +1,134 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Banner } from '@astryxdesign/core/Banner';
3
+ import { Center } from '@astryxdesign/core/Center';
4
+ import { ChatLayout } from '@astryxdesign/core/Chat';
5
+ import { Layout, LayoutContent } from '@astryxdesign/core/Layout';
6
+ import { Text } from '@astryxdesign/core/Text';
7
+ import { VStack } from '@astryxdesign/core/VStack';
8
+ import { moveComposerPendingWithinKind, removeComposerPendingMessage, } from '@theoremjs/agents/interface';
9
+ import { useImperativeHandle, useMemo, useRef, } from 'react';
10
+ import { parseAspectRatio } from "../client/image-output.js";
11
+ import { createHttpTransport, } from "../client/transport.js";
12
+ import { useTheoremChat, } from "../hooks/use-theorem-chat.js";
13
+ import { useTheoremInterface } from "../hooks/use-theorem-interface.js";
14
+ import { ChatComposerBar } from "./ChatComposerBar.js";
15
+ import { ChatTranscript } from "./ChatTranscript.js";
16
+ import { useGlide } from "./glide.js";
17
+ import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
18
+ import { MarkdownPluginsProvider } from "./markdown-plugins.js";
19
+ import { PaneFailure, PaneLoading } from "./PaneState.js";
20
+ import { SidePanelHeader } from "./SidePanel.js";
21
+ import { useTraceInspector, WithTrace } from "./TraceInspectorPanel.js";
22
+ import { TheoremThemeProvider } from "./theme.js";
23
+ /**
24
+ * 50% of the chat's width, but never narrower than 640px (or the full width,
25
+ * whichever is smaller) — so phones and narrow panes get 100%.
26
+ */
27
+ export const DEFAULT_CHAT_MAX_WIDTH = 'max(50%, min(100%, 640px))';
28
+ /** Centres content in a column no wider than `maxWidth`, using Astryx layout props. */
29
+ function ChatColumn({ maxWidth, ref, children, }) {
30
+ return (_jsx(Center, { axis: "horizontal", width: "100%", children: _jsx(VStack, { ref: ref, width: "100%", maxWidth: maxWidth, children: children }) }));
31
+ }
32
+ /** The composer bar wired to a `useTheoremChat` model. */
33
+ function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
34
+ return (_jsx(ChatComposerBar, { iface: iface, draftText: chat.draftText, pendingFiles: chat.pendingFiles, pendingVoice: chat.pendingVoice, pendingMessages: chat.pendingMessages, issues: chat.issues, phase: chat.phase, failure: chat.failure, selectedModel: chat.session.selectedModel, selectedEffort: chat.session.selectedEffort, placeholder: placeholder, inputRef: inputRef, onDraftTextChange: chat.setDraftText, onFilesSelected: (files) => {
35
+ chat.setPendingFiles((prev) => [...prev, ...files]);
36
+ chat.setIssues([]);
37
+ }, onAttachmentRemove: (index) => {
38
+ chat.setPendingFiles((prev) => prev.filter((_, i) => i !== index));
39
+ }, onVoiceStaged: (file) => {
40
+ chat.setPendingVoice([file]);
41
+ chat.setIssues([]);
42
+ }, onVoiceClear: () => chat.setPendingVoice([]), onSubmit: () => {
43
+ void chat.handleSubmit();
44
+ }, onStop: chat.handleStop, onMenuAction: chat.handleMenuAction, onGenerationChange: chat.handleGenerationChange, slotChoices: chat.slotChoices, onPendingMove: (id, direction) => {
45
+ chat.setPendingMessages((prev) => moveComposerPendingWithinKind(prev, id, direction));
46
+ }, onPendingRemove: (id) => {
47
+ chat.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
48
+ }, onPendingQueue: chat.handlePendingQueue, onPendingRestore: (id) => {
49
+ void chat.handlePendingRestore(id);
50
+ }, onPendingSendNow: (id) => {
51
+ const message = chat.pendingMessages.find((m) => m.id === id);
52
+ if (message)
53
+ void chat.handleSendNow(message);
54
+ } }));
55
+ }
56
+ function ChatBody({ transport, iface, placeholder, emptyState, density, maxWidth = DEFAULT_CHAT_MAX_WIDTH, scrollRef, trace, usage, className, style, conversation, conversationView, initialText, onConversationChange, chatRef, composer: hasComposer = true, slots, onSlotsChange, context, pageTools, onTurnEvent, }) {
57
+ const t = useLabels();
58
+ const chat = useTheoremChat({
59
+ transport,
60
+ iface,
61
+ conversation,
62
+ conversationView,
63
+ initialText,
64
+ onConversationChange,
65
+ slots,
66
+ onSlotsChange,
67
+ context,
68
+ pageTools,
69
+ onTurnEvent,
70
+ });
71
+ const { sendText, handleStop } = chat;
72
+ useImperativeHandle(chatRef, () => ({ send: sendText, stop: handleStop }), [
73
+ sendText,
74
+ handleStop,
75
+ ]);
76
+ const blocks = useMemo(() => [...chat.blocks, ...chat.streamBlocks], [chat.blocks, chat.streamBlocks]);
77
+ const handle = t('@theorem.agent.handle', { handle: iface.identity.handle });
78
+ const landing = blocks.length === 0;
79
+ const inputRef = useRef(null);
80
+ const composerRef = useGlide(landing, () => inputRef.current?.focus());
81
+ const inspector = useTraceInspector(iface, transport.traces, trace);
82
+ const header = _jsx(SidePanelHeader, { floating: true, children: inspector.toggle });
83
+ // why: the composer says when a turn failed. Without one, the chat says it where the composer was.
84
+ const composer = hasComposer ? (_jsx(ChatComposerForChat, { chat: chat, iface: iface, placeholder: placeholder, inputRef: inputRef })) : (chat.failure && _jsx(Banner, { status: "error", role: "alert", title: chat.failure.error }));
85
+ const docked = { composer, composerRef, maxWidth };
86
+ return (_jsx(WithTrace, { inspector: inspector, children: _jsx(Layout, { height: "fill", className: className, style: { position: 'relative', ...style }, header: header, content: _jsx(LayoutContent, { padding: 0, children: landing ? (_jsx(ChatLanding, { ...docked, handle: handle, emptyState: emptyState })) : (_jsx(ChatThread, { ...docked, chat: chat, iface: iface, blocks: blocks, handle: handle, density: density, scrollRef: scrollRef, usage: usage })) }) }) }));
87
+ }
88
+ /**
89
+ * The chat before its first message: the greeting, and the composer under it.
90
+ * why: paddingInline={3} matches the dock's inset so the composer keeps its width when it moves.
91
+ */
92
+ function ChatLanding({ composer, composerRef, maxWidth, handle, emptyState, }) {
93
+ const t = useLabels();
94
+ return (_jsxs(VStack, { minHeight: "100%", vAlign: "center", gap: 8, paddingInline: 3, children: [_jsx(ChatColumn, { maxWidth: maxWidth, children: emptyState ?? (_jsxs(VStack, { gap: 1, children: [_jsx(Text, { type: "supporting", as: "h2", children: handle }), _jsx(Text, { type: "display-2", as: "h1", children: t('@theorem.chat.greeting') })] })) }), composer && (_jsx(ChatColumn, { ref: composerRef, maxWidth: maxWidth, children: composer }))] }));
95
+ }
96
+ /**
97
+ * The chat once it has messages: the transcript, with the composer in ChatLayout's own dock.
98
+ * why: the dock leaves 12px under the composer; paddingBlockEnd={3} adds 12px more for a 24px
99
+ * bottom margin.
100
+ */
101
+ function ChatThread({ composer, composerRef, maxWidth, chat, iface, blocks, handle, density, scrollRef, usage, }) {
102
+ return (_jsx(VStack, { height: "100%", children: _jsx(ChatLayout, { density: density, scrollRef: scrollRef, composer: composer && (_jsx(VStack, { paddingBlockEnd: 3, children: _jsx(ChatColumn, { ref: composerRef, maxWidth: maxWidth, children: composer }) })), children: _jsx(ChatColumn, { maxWidth: maxWidth, children: _jsx(ChatTranscript, { blocks: blocks, handle: handle, streaming: chat.streaming, delivery: chat.delivery, answering: chat.answering, usage: usage, labels: iface.labels, imageOutput: iface.type === 'image'
103
+ ? { ratio: parseAspectRatio(iface.image.aspectRatio) }
104
+ : undefined, onToolDecision: (index, action) => {
105
+ void chat.handleToolDecision(index, action);
106
+ }, onAuthenticated: (index, secret) => {
107
+ void chat.handleAuthenticated(index, secret);
108
+ } }) }) }) }));
109
+ }
110
+ function ChatForTransport(props) {
111
+ const t = useLabels();
112
+ const described = useTheoremInterface(props.transport);
113
+ if (described.status === 'loading')
114
+ return _jsx(PaneLoading, { label: t('@theorem.chat.loading') });
115
+ if (described.status === 'error')
116
+ return _jsx(PaneFailure, { title: described.failure.error });
117
+ if (described.iface.type === 'live') {
118
+ return (_jsx(PaneFailure, { title: t('@theorem.chat.live_unsupported.title'), description: t('@theorem.chat.live_unsupported.description') }));
119
+ }
120
+ return _jsx(ChatBody, { ...props, transport: props.transport, iface: described.iface });
121
+ }
122
+ /**
123
+ * Drop-in chat for a profile served by `createTheoremHandler`:
124
+ *
125
+ * ```tsx
126
+ * <TheoremChat endpoint="/api/theorem" />
127
+ * ```
128
+ */
129
+ export function TheoremChat({ endpoint, http, transport, theme, mode, labels, detectCodeLanguage, ...rest }) {
130
+ const httpRef = useRef(http);
131
+ httpRef.current = http;
132
+ const resolved = useMemo(() => transport ?? createHttpTransport({ ...httpRef.current, endpoint }), [transport, endpoint]);
133
+ return (_jsx(TheoremThemeProvider, { theme: theme, mode: mode, children: _jsx(TheoremLabelsProvider, { labels: labels, children: _jsx(MarkdownPluginsProvider, { detectCodeLanguage: detectCodeLanguage, children: _jsx(ChatForTransport, { ...rest, transport: resolved }) }) }) }));
134
+ }
@@ -0,0 +1,30 @@
1
+ import { type DecisionImages } from '../client/decision-images.ts';
2
+ import { type DecisionTransport } from '../client/decision-transport.ts';
3
+ import { type ConsolePageProps } from './ConsoleFrame.tsx';
4
+ import { type TheoremDecisionAnswersProps } from './DecisionAnswers.tsx';
5
+ export type TheoremDecisionProps = ConsolePageProps & {
6
+ /** Where `createTheoremDecisionHandler` is mounted. Default `/api/decision`. Ignored when `transport` is set. */
7
+ endpoint?: string;
8
+ /** Bring your own transport (tests, studios, non-HTTP hosts). */
9
+ transport?: DecisionTransport;
10
+ /** The JSON the state field starts with. Default `{}`. */
11
+ defaultState?: string;
12
+ /**
13
+ * Images attached when the page opens, by the name the state refers to them by. The visitor
14
+ * can add and remove more; they travel with each decision.
15
+ */
16
+ defaultImages?: DecisionImages;
17
+ /** Opens a question where it is set up, such as an editor. Each answer then has a button for it. */
18
+ onQuestion?: (id: string) => void;
19
+ /** Each question's text by its id, shown under its answer's title. The page is never sent a question's instructions, so it shows only what it is given here. */
20
+ questionText?: TheoremDecisionAnswersProps['questionText'];
21
+ };
22
+ /**
23
+ * Drop-in surface for a decision profile served by `createTheoremDecisionHandler`:
24
+ * the state as fields or JSON, a Decide button (⌘/Ctrl+Enter), and the answers.
25
+ *
26
+ * ```tsx
27
+ * <TheoremDecision endpoint="/api/decision" />
28
+ * ```
29
+ */
30
+ export declare function TheoremDecision({ endpoint, http, transport, theme, mode, labels, ...rest }: TheoremDecisionProps): import("react").JSX.Element;
@@ -0,0 +1,130 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Banner } from '@astryxdesign/core/Banner';
3
+ import { useMemo, useRef, useState } from 'react';
4
+ import { droppedImages, namedImages, stateFields, } from "../client/decision-images.js";
5
+ import { createDecisionTransport, } from "../client/decision-transport.js";
6
+ import { useTheoremDecision } from "../hooks/use-theorem-decision.js";
7
+ import { ConsoleFrame, JsonRequest, RequestCard, ResponseColumn, RunBar, useBytes, } from "./ConsoleFrame.js";
8
+ import { TheoremDecisionAnswers } from "./DecisionAnswers.js";
9
+ import { ImageDrop } from "./DecisionImages.js";
10
+ import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
11
+ import { PaneFailure, PaneLoading } from "./PaneState.js";
12
+ import { StateFields, StateImages } from "./StateFields.js";
13
+ import { useTraceInspector } from "./TraceInspectorPanel.js";
14
+ import { TheoremThemeProvider } from "./theme.js";
15
+ /** The field's text as the state a decision sends, measured the way the kernel measures it. */
16
+ function checkState(text, maxBytes) {
17
+ let state;
18
+ try {
19
+ state = JSON.parse(text);
20
+ }
21
+ catch {
22
+ return { ok: false, reason: 'invalid_json' };
23
+ }
24
+ if (state === null)
25
+ return { ok: false, reason: 'null_state', state };
26
+ const bytes = new TextEncoder().encode(JSON.stringify(state)).byteLength;
27
+ if (maxBytes !== undefined && bytes > maxBytes)
28
+ return { ok: false, reason: 'too_large', bytes, state };
29
+ return { ok: true, state, bytes };
30
+ }
31
+ function stateNote(t, check, maxBytes, bytes) {
32
+ if (!check.ok) {
33
+ if (check.reason === 'too_large')
34
+ return t('@theorem.decision.too_large', { limit: bytes(maxBytes ?? 0) });
35
+ return t(check.reason === 'invalid_json'
36
+ ? '@theorem.decision.invalid_json'
37
+ : '@theorem.decision.null_state');
38
+ }
39
+ return maxBytes === undefined
40
+ ? null
41
+ : t('@theorem.decision.state_size', { size: bytes(check.bytes), limit: bytes(maxBytes) });
42
+ }
43
+ /** Where a decision stands: nothing asked, asked with nothing back yet, or answered or failed. */
44
+ function decisionPhase(decision) {
45
+ if (decision.failure || decision.result)
46
+ return 'settled';
47
+ return decision.status === 'deciding' ? 'waiting' : 'idle';
48
+ }
49
+ /** The state a decision is asked about, held as text, and the view it is edited in. */
50
+ function useDecisionState(defaultState, maxStateBytes) {
51
+ const [text, setText] = useState(defaultState);
52
+ const check = checkState(text, maxStateBytes);
53
+ const [view, setView] = useState(() => check.state === undefined ? 'json' : 'fields');
54
+ const shown = check.state === undefined ? 'json' : view;
55
+ return { text, setText, check, shown, setView };
56
+ }
57
+ /** The state, as fields or JSON, and Decide. */
58
+ function DecisionRequest({ form, iface, images, onImages, note, isRunning, onRun, onStop, }) {
59
+ const t = useLabels();
60
+ const { text, setText, check, shown, setView } = form;
61
+ const withImages = iface.questions.some((question) => question.type === 'box') || Object.keys(images).length > 0;
62
+ const fields = shown === 'fields' ? check.state : undefined;
63
+ const named = stateFields(fields);
64
+ return (_jsxs(RequestCard, { title: t('@theorem.decision.state'), view: shown, hasFields: check.state !== undefined, onView: setView, onRun: onRun, children: [fields !== undefined ? (_jsx(StateImages, { value: { images, onImages }, children: _jsx(StateFields, { label: t('@theorem.decision.state'), value: fields, onChange: (next) => {
65
+ setText(JSON.stringify(next, null, 2));
66
+ } }) })) : (_jsx(JsonRequest, { label: t('@theorem.decision.state'), text: text, isInvalid: !check.ok, onChange: setText })), withImages && named ? (_jsx(ImageDrop, { onImages: (dropped) => {
67
+ const next = droppedImages(dropped, { images, host: iface.images, state: named });
68
+ onImages(next.images);
69
+ setText(JSON.stringify(next.state, null, 2));
70
+ } })) : null, _jsx(RunBar, { note: note, isReady: check.ok, isRunning: isRunning, runLabel: t('@theorem.decision.decide'), runningLabel: t('@theorem.decision.deciding'), stopLabel: t('@theorem.decision.stop'), onRun: onRun, onStop: onStop })] }));
71
+ }
72
+ /** What came back: the failure, the answers, or a spinner until the first of either. */
73
+ function DecisionAnswered({ iface, decision, asked, onQuestion, questionText, }) {
74
+ const t = useLabels();
75
+ const deciding = decision.status === 'deciding';
76
+ return (_jsxs(_Fragment, { children: [decision.failure ? (_jsx(Banner, { status: "error", role: "alert", title: decision.failure.error })) : null, deciding && !decision.result ? (_jsx(PaneLoading, { label: t('@theorem.decision.deciding'), since: decision.startedAt })) : null, decision.result ? (_jsx(TheoremDecisionAnswers, { iface: iface, result: decision.result, asked: asked, onQuestion: onQuestion, questionText: questionText, elapsedMs: decision.elapsedMs, isStale: deciding })) : null] }));
77
+ }
78
+ /**
79
+ * The frame's second child. A call, not a component: the frame counts its
80
+ * children to choose a layout, and no response yet has to count as none.
81
+ */
82
+ function decisionResponse(columns, phase, labels, since, answers) {
83
+ if (phase === 'idle')
84
+ return null;
85
+ if (!columns)
86
+ return answers;
87
+ const waiting = phase === 'waiting';
88
+ return (_jsx(ResponseColumn, { label: labels.answers, fill: waiting, children: waiting ? _jsx(PaneLoading, { label: labels.deciding, since: since }) : answers }));
89
+ }
90
+ function DecisionBody({ transport, iface, decision, defaultState = '{}', defaultImages, onQuestion, questionText, maxWidth = '960px', trace, flush, columns = false, className, style, }) {
91
+ const t = useLabels();
92
+ const bytes = useBytes();
93
+ const form = useDecisionState(defaultState, iface.maxStateBytes);
94
+ const { check } = form;
95
+ const [images, setImages] = useState(defaultImages ?? {});
96
+ /** The images and state the answers were asked about, kept with them: later edits don't redraw them. */
97
+ const [asked, setAsked] = useState();
98
+ const inspector = useTraceInspector(iface, transport.traces, trace);
99
+ const decide = () => {
100
+ if (!check.ok)
101
+ return;
102
+ const sent = namedImages(images, check.state);
103
+ setAsked({ images: { ...iface.images, ...sent }, state: check.state });
104
+ void decision.decide(check.state, Object.keys(sent).length > 0 ? sent : undefined);
105
+ };
106
+ return (_jsxs(ConsoleFrame, { inspector: inspector, maxWidth: maxWidth, flush: flush, columns: columns, requestLabel: t('@theorem.decision.state'), className: className, style: style, children: [_jsx(DecisionRequest, { form: form, iface: iface, images: images, onImages: setImages, note: stateNote(t, check, iface.maxStateBytes, bytes), isRunning: decision.status === 'deciding', onRun: decide, onStop: decision.cancel }), decisionResponse(columns, decisionPhase(decision), { answers: t('@theorem.decision.answers'), deciding: t('@theorem.decision.deciding') }, decision.startedAt, _jsx(DecisionAnswered, { iface: iface, decision: decision, asked: asked, onQuestion: onQuestion, questionText: questionText }))] }));
107
+ }
108
+ function DecisionForTransport(props) {
109
+ const t = useLabels();
110
+ const decision = useTheoremDecision(props.transport);
111
+ if (decision.describeFailure)
112
+ return _jsx(PaneFailure, { title: decision.describeFailure.error });
113
+ if (!decision.iface)
114
+ return _jsx(PaneLoading, { label: t('@theorem.chat.loading') });
115
+ return _jsx(DecisionBody, { ...props, iface: decision.iface, decision: decision });
116
+ }
117
+ /**
118
+ * Drop-in surface for a decision profile served by `createTheoremDecisionHandler`:
119
+ * the state as fields or JSON, a Decide button (⌘/Ctrl+Enter), and the answers.
120
+ *
121
+ * ```tsx
122
+ * <TheoremDecision endpoint="/api/decision" />
123
+ * ```
124
+ */
125
+ export function TheoremDecision({ endpoint, http, transport, theme, mode, labels, ...rest }) {
126
+ const httpRef = useRef(http);
127
+ httpRef.current = http;
128
+ const resolved = useMemo(() => transport ?? createDecisionTransport({ ...httpRef.current, endpoint }), [transport, endpoint]);
129
+ return (_jsx(TheoremThemeProvider, { theme: theme, mode: mode, children: _jsx(TheoremLabelsProvider, { labels: labels, children: _jsx(DecisionForTransport, { ...rest, transport: resolved }) }) }));
130
+ }
@@ -0,0 +1,27 @@
1
+ import { type EmbeddingTransport } from '../client/embedding-transport.ts';
2
+ import { type ConsolePageProps } from './ConsoleFrame.tsx';
3
+ import { type EmbeddingDocument } from './EmbeddingDocuments.tsx';
4
+ export type TheoremEmbeddingProps = ConsolePageProps & {
5
+ /** Where `createTheoremEmbeddingHandler` is mounted. Default `/api/embedding`. Ignored when `transport` is set. */
6
+ endpoint?: string;
7
+ /** Bring your own transport (tests, studios, non-HTTP hosts). */
8
+ transport?: EmbeddingTransport;
9
+ /** The text the documents card starts with, split into paragraphs. Default none. */
10
+ defaultText?: string;
11
+ /** Documents the card starts with beside the text, each with the file it came from. Default none. */
12
+ defaultDocuments?: readonly EmbeddingDocument[];
13
+ /** The query the search starts with. Default none. */
14
+ defaultQuery?: string;
15
+ };
16
+ /**
17
+ * Drop-in search over an embedding profile served by `createTheoremEmbeddingHandler`:
18
+ * paste text and split it, or drop files, and Index embeds them as documents
19
+ * in as many requests as the profile's limits need. Then Search embeds a query
20
+ * and ranks the documents by cosine similarity. Every request is shown as
21
+ * JSON, and in the trace.
22
+ *
23
+ * ```tsx
24
+ * <TheoremEmbedding endpoint="/api/embedding" />
25
+ * ```
26
+ */
27
+ export declare function TheoremEmbedding({ endpoint, http, transport, theme, mode, labels, ...rest }: TheoremEmbeddingProps): import("react").JSX.Element;
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { VStack } from '@astryxdesign/core/VStack';
3
+ import { useMemo, useRef, useState } from 'react';
4
+ import { rankMatches } from "../client/embedding-input.js";
5
+ import { createEmbeddingTransport, } from "../client/embedding-transport.js";
6
+ import { useEmbeddingIndex } from "../hooks/use-embedding-index.js";
7
+ import { useTheoremEmbedding } from "../hooks/use-theorem-embedding.js";
8
+ import { ConsoleFrame, ConsoleSection, ResponseColumn, } from "./ConsoleFrame.js";
9
+ import { EmbeddingDocuments, useEmbeddingDocuments, } from "./EmbeddingDocuments.js";
10
+ import { EmbeddingQuery, useEmbeddingQuery } from "./EmbeddingQuery.js";
11
+ import { EmbeddingResults, IndexCard } from "./EmbeddingResults.js";
12
+ import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
13
+ import { PaneFailure, PaneLoading } from "./PaneState.js";
14
+ import { useTraceInspector } from "./TraceInspectorPanel.js";
15
+ import { TheoremThemeProvider } from "./theme.js";
16
+ /** The same documents: the same texts, and the very same files. */
17
+ function sameInputs(a, b) {
18
+ return a.length === b.length && a.every((input, at) => input === b[at]);
19
+ }
20
+ /** Where the documents stand for the last query: embedded, changed since, or embedded by another model. */
21
+ function indexStateOf(indexed, queried, inputs) {
22
+ if (!indexed)
23
+ return 'fresh';
24
+ // why: A query from another model than the index's is in another space; no score between them means anything.
25
+ if (queried && queried.model !== indexed.model)
26
+ return 'model';
27
+ return sameInputs(indexed.inputs, inputs) ? 'fresh' : 'stale';
28
+ }
29
+ function indexPhase(index) {
30
+ if (index.status === 'indexing')
31
+ return 'indexing';
32
+ return index.result !== null || index.failure !== null ? 'indexed' : 'idle';
33
+ }
34
+ /**
35
+ * The frame's second child. A call, not a component: the frame counts its
36
+ * children to choose a layout, and nothing indexed yet has to count as none.
37
+ */
38
+ function embeddingResponse(columns, label, phase, results) {
39
+ if (phase === 'idle')
40
+ return null;
41
+ return columns ? (_jsx(ResponseColumn, { label: label, fill: phase === 'indexing', children: results })) : (results);
42
+ }
43
+ /** The spinner while the documents embed, counting the requests once there is more than one. */
44
+ function IndexingPane({ index }) {
45
+ const t = useLabels();
46
+ const { progress } = index;
47
+ return (_jsx(PaneLoading, { label: progress
48
+ ? t('@theorem.embedding.indexing_progress', {
49
+ done: Math.min(progress.done + 1, progress.total),
50
+ total: progress.total,
51
+ })
52
+ : t('@theorem.embedding.indexing'), since: index.startedAt }));
53
+ }
54
+ /** Where the last query stands against the index: whether it searched, and its vector while both share a model. */
55
+ function searchOf(index, search, inputs) {
56
+ const state = indexStateOf(index.result, search.result, inputs);
57
+ if (state === 'model')
58
+ return { state, searched: false, queried: null };
59
+ return {
60
+ state,
61
+ searched: search.status !== 'idle' || search.result !== null,
62
+ queried: search.result?.embeddings[0]?.values ?? null,
63
+ };
64
+ }
65
+ /** The query against the index, and once it ran, what it found. */
66
+ function IndexSearch({ iface, index, search, query, indexed, state, searched, matches, pointing, }) {
67
+ return (_jsxs(ConsoleSection, { children: [_jsx(EmbeddingQuery, { form: query, rules: iface.inputs, index: state, isRunning: search.status === 'embedding', onRun: (input) => {
68
+ void search.embed([input], { task: 'query' });
69
+ }, onStop: search.cancel }), searched ? (_jsx(VStack, { padding: 4, children: _jsx(EmbeddingResults, { index: index, search: search, documents: indexed, matches: matches, pointing: pointing }) })) : null] }));
70
+ }
71
+ /** The index, and once it holds vectors, the search over them. */
72
+ function EmbeddingIndexed({ inputs, ...props }) {
73
+ const { index, indexed } = props;
74
+ const { state, searched, queried } = searchOf(index, props.search, inputs);
75
+ const vectors = index.result?.vectors;
76
+ const matches = useMemo(() => (queried && vectors ? rankMatches(queried, vectors) : []), [queried, vectors]);
77
+ const [hovered, setHovered] = useState(null);
78
+ const pointing = useMemo(() => ({ hovered, onHover: setHovered }), [hovered]);
79
+ return (_jsxs(VStack, { gap: 3, children: [_jsx(IndexCard, { run: index, documents: indexed, query: queried, matches: matches, pointing: pointing }), index.result ? (_jsx(IndexSearch, { ...props, state: state, searched: searched, matches: matches, pointing: pointing })) : null] }));
80
+ }
81
+ function EmbeddingBody({ transport, iface, search, defaultText = '', defaultDocuments, defaultQuery = '', maxWidth = '960px', trace, flush, columns = false, className, style, }) {
82
+ const t = useLabels();
83
+ const documents = useEmbeddingDocuments(defaultText, iface.inputs, defaultDocuments);
84
+ const query = useEmbeddingQuery(defaultQuery, iface.inputs);
85
+ const index = useEmbeddingIndex(transport);
86
+ const [indexed, setIndexed] = useState([]);
87
+ const inspector = useTraceInspector(iface, transport.traces, trace);
88
+ const runIndex = () => {
89
+ if (documents.jsonInvalid || documents.issue || documents.inputs.length === 0)
90
+ return;
91
+ setIndexed(documents.documents);
92
+ void index.index(documents.inputs, iface.inputs);
93
+ };
94
+ const phase = indexPhase(index);
95
+ return (_jsxs(ConsoleFrame, { inspector: inspector, maxWidth: maxWidth, flush: flush, columns: columns, requestLabel: t('@theorem.embedding.documents'), className: className, style: style, children: [_jsx(EmbeddingDocuments, { form: documents, rules: iface.inputs, isRunning: phase === 'indexing', onRun: runIndex, onStop: index.cancel }), embeddingResponse(columns, t('@theorem.embedding.results'), phase, phase === 'indexing' ? (_jsx(IndexingPane, { index: index })) : (_jsx(EmbeddingIndexed, { iface: iface, index: index, search: search, query: query, indexed: indexed, inputs: documents.inputs })))] }));
96
+ }
97
+ function EmbeddingForTransport(props) {
98
+ const t = useLabels();
99
+ const search = useTheoremEmbedding(props.transport);
100
+ if (search.describeFailure)
101
+ return _jsx(PaneFailure, { title: search.describeFailure.error });
102
+ if (!search.iface)
103
+ return _jsx(PaneLoading, { label: t('@theorem.chat.loading') });
104
+ return _jsx(EmbeddingBody, { ...props, iface: search.iface, search: search });
105
+ }
106
+ /**
107
+ * Drop-in search over an embedding profile served by `createTheoremEmbeddingHandler`:
108
+ * paste text and split it, or drop files, and Index embeds them as documents
109
+ * in as many requests as the profile's limits need. Then Search embeds a query
110
+ * and ranks the documents by cosine similarity. Every request is shown as
111
+ * JSON, and in the trace.
112
+ *
113
+ * ```tsx
114
+ * <TheoremEmbedding endpoint="/api/embedding" />
115
+ * ```
116
+ */
117
+ export function TheoremEmbedding({ endpoint, http, transport, theme, mode, labels, ...rest }) {
118
+ const httpRef = useRef(http);
119
+ httpRef.current = http;
120
+ const resolved = useMemo(() => transport ?? createEmbeddingTransport({ ...httpRef.current, endpoint }), [transport, endpoint]);
121
+ return (_jsx(TheoremThemeProvider, { theme: theme, mode: mode, children: _jsx(TheoremLabelsProvider, { labels: labels, children: _jsx(EmbeddingForTransport, { ...rest, transport: resolved }) }) }));
122
+ }
@@ -0,0 +1,55 @@
1
+ import type { DefinedTheme } from '@astryxdesign/core/theme';
2
+ import { type CSSProperties } from 'react';
3
+ import { type HostTransport } from '../client/host-transport.ts';
4
+ import type { PageTools } from '../client/live/live-page-tool.ts';
5
+ import type { HttpOptions } from '../client/transport.ts';
6
+ import { type TheoremLabels } from './labels.ts';
7
+ export type TheoremHostProps = {
8
+ /** Where `createTheoremHostHandler` is mounted. Default `/api/host`. Ignored when `transport` is set. */
9
+ endpoint?: string;
10
+ /** Extra fetch options for the default HTTP transport (auth headers, custom fetch). */
11
+ http?: HttpOptions;
12
+ /** Bring your own transport (tests, studios, non-HTTP hosts). */
13
+ transport?: HostTransport;
14
+ /** why: Page callbacks run only after the server saves a dispatch grant. */
15
+ pageTools?: PageTools;
16
+ theme?: DefinedTheme;
17
+ mode?: 'system' | 'light' | 'dark';
18
+ /** Replacement lines by locale, for any `@theorem.*` or `@astryx.*` key. */
19
+ labels?: TheoremLabels;
20
+ /**
21
+ * Colour code the reply left untagged: a fence with no language is read and, when it is plainly
22
+ * one of JavaScript, TypeScript, Python, shell, JSON, CSS, HTML, XML, YAML, or Markdown, shown as it.
23
+ * A fence with a language is never changed. Off by default.
24
+ */
25
+ detectCodeLanguage?: boolean;
26
+ /** Widest the column may grow, as a CSS length. Default `960px`. */
27
+ maxWidth?: string;
28
+ /**
29
+ * Drive the trace from the host's own control. The built-in toggle hides.
30
+ * The open trace takes the console's place, unless this page is inside
31
+ * `TracePlacement value="panel"`, where it docks. Omit to keep the toggle.
32
+ * Needs a profile that records traces.
33
+ */
34
+ trace?: boolean;
35
+ /**
36
+ * The page is already a raised panel. The console sits in it instead of
37
+ * painting the ground it uses on a flat page.
38
+ */
39
+ flush?: boolean;
40
+ /** The request on the left and the response on the right. */
41
+ columns?: boolean;
42
+ className?: string;
43
+ style?: CSSProperties;
44
+ };
45
+ /**
46
+ * Drop-in console for a host profile served by `createTheoremHostHandler`:
47
+ * pick a tool, fill in the request its input schema draws, Run (⌘/Ctrl+Enter),
48
+ * and read the response laid out from what it returned — figures, charts,
49
+ * images, and the data itself. Gates are answered in place.
50
+ *
51
+ * ```tsx
52
+ * <TheoremHost endpoint="/api/host" />
53
+ * ```
54
+ */
55
+ export declare function TheoremHost({ endpoint, http, transport, theme, mode, labels, detectCodeLanguage, ...rest }: TheoremHostProps): import("react").JSX.Element;