@theoremjs/react 0.3.0 → 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 (258) hide show
  1. package/README.md +728 -202
  2. package/client/composer-hints.js +1 -1
  3. package/client/decision-images.d.ts +76 -0
  4. package/client/decision-images.js +139 -0
  5. package/client/decision-transport.d.ts +26 -4
  6. package/client/decision-transport.js +79 -6
  7. package/client/embedding-input.d.ts +84 -0
  8. package/client/embedding-input.js +194 -0
  9. package/client/embedding-space.d.ts +82 -0
  10. package/client/embedding-space.js +389 -0
  11. package/client/embedding-transport.d.ts +40 -0
  12. package/client/embedding-transport.js +71 -0
  13. package/client/failure.d.ts +7 -10
  14. package/client/failure.js +9 -9
  15. package/client/host-call.d.ts +1 -1
  16. package/client/host-call.js +7 -2
  17. package/client/host-transport.d.ts +2 -0
  18. package/client/host-transport.js +3 -2
  19. package/client/index.d.ts +19 -11
  20. package/client/index.js +14 -10
  21. package/client/ink-waveform.d.ts +1 -1
  22. package/client/ink-waveform.js +1 -2
  23. package/client/live/apply-live-turn-tool-event.js +1 -1
  24. package/client/live/live-captions.d.ts +30 -0
  25. package/client/live/live-captions.js +105 -1
  26. package/client/live/live-mic-forward.d.ts +1 -0
  27. package/client/live/live-mic-forward.js +11 -2
  28. package/client/live/live-page-tool.d.ts +34 -0
  29. package/client/live/live-page-tool.js +11 -0
  30. package/client/live/live-state.d.ts +4 -2
  31. package/client/live/live-state.js +2 -0
  32. package/client/live/live-video.js +1 -1
  33. package/client/live/run-live-tool-call.d.ts +7 -2
  34. package/client/live/run-live-tool-call.js +48 -10
  35. package/client/live/use-live-caption-log.d.ts +17 -0
  36. package/client/live/use-live-caption-log.js +51 -0
  37. package/client/live-client.d.ts +52 -4
  38. package/client/live-client.js +250 -59
  39. package/client/live-messages.d.ts +51 -13
  40. package/client/live-messages.js +42 -9
  41. package/client/page-dispatch.d.ts +15 -0
  42. package/client/page-dispatch.js +63 -0
  43. package/client/run-commit.d.ts +16 -43
  44. package/client/run-commit.js +26 -101
  45. package/client/run-session.d.ts +40 -41
  46. package/client/run-session.js +104 -112
  47. package/client/schema-fields.js +27 -6
  48. package/client/shaped-data.js +37 -8
  49. package/client/source-citations.js +1 -1
  50. package/client/space-geometry.d.ts +39 -0
  51. package/client/space-geometry.js +183 -0
  52. package/client/state-fields.js +1 -1
  53. package/client/tool-result.js +30 -12
  54. package/client/tool-resume.d.ts +18 -1
  55. package/client/tool-resume.js +4 -1
  56. package/client/trace-feed.d.ts +1 -1
  57. package/client/trace-story.d.ts +2 -2
  58. package/client/trace-story.js +43 -19
  59. package/client/trace-view.d.ts +2 -2
  60. package/client/trace-view.js +56 -16
  61. package/client/transcript-groups.d.ts +28 -2
  62. package/client/transcript-groups.js +63 -13
  63. package/client/transcript-schema.d.ts +10 -0
  64. package/client/transcript-schema.js +120 -0
  65. package/client/transport.d.ts +114 -26
  66. package/client/transport.js +106 -32
  67. package/client/turn-client.d.ts +17 -8
  68. package/client/turn-client.js +26 -31
  69. package/client/voice-recorder.d.ts +1 -1
  70. package/client/voice-recorder.js +2 -5
  71. package/client/wire-line.js +5 -5
  72. package/client/worklets/mic-capture.js +9 -9
  73. package/components/InkWaveform.d.ts +12 -1
  74. package/components/InkWaveform.js +65 -42
  75. package/components/live/use-live-runner-controls.d.ts +3 -4
  76. package/components/live/use-live-runner-controls.js +6 -10
  77. package/components/live/use-live-runner-model.d.ts +10 -6
  78. package/components/live/use-live-runner-model.js +29 -14
  79. package/components/live/use-live-runner-ui.d.ts +10 -8
  80. package/components/live/use-live-runner-ui.js +19 -11
  81. package/components/live/use-live-session-client.d.ts +4 -4
  82. package/components/live/use-live-session-client.js +11 -8
  83. package/components/use-composer-voice.d.ts +4 -4
  84. package/components/use-composer-voice.js +8 -8
  85. package/components/use-editor-selection.js +4 -1
  86. package/components/use-voice-playback.d.ts +2 -1
  87. package/components/use-voice-playback.js +4 -4
  88. package/hooks/use-described.d.ts +5 -1
  89. package/hooks/use-described.js +11 -9
  90. package/hooks/use-embedding-index.d.ts +27 -0
  91. package/hooks/use-embedding-index.js +43 -0
  92. package/hooks/use-one-call.d.ts +31 -0
  93. package/hooks/use-one-call.js +76 -0
  94. package/hooks/use-page-tool-warnings.d.ts +6 -0
  95. package/hooks/use-page-tool-warnings.js +25 -0
  96. package/hooks/use-second-ticker.d.ts +2 -0
  97. package/hooks/use-second-ticker.js +13 -0
  98. package/hooks/use-theorem-chat-actions.d.ts +25 -21
  99. package/hooks/use-theorem-chat-actions.js +67 -45
  100. package/hooks/use-theorem-chat-state.d.ts +6 -6
  101. package/hooks/use-theorem-chat-state.js +13 -9
  102. package/hooks/use-theorem-chat.d.ts +48 -14
  103. package/hooks/use-theorem-chat.js +329 -141
  104. package/hooks/use-theorem-decision.d.ts +5 -10
  105. package/hooks/use-theorem-decision.js +4 -67
  106. package/hooks/use-theorem-embedding.d.ts +18 -0
  107. package/hooks/use-theorem-embedding.js +12 -0
  108. package/hooks/use-theorem-host.d.ts +5 -4
  109. package/hooks/use-theorem-host.js +26 -8
  110. package/hooks/use-theorem-interface.d.ts +6 -3
  111. package/hooks/use-theorem-interface.js +12 -22
  112. package/index.d.ts +6 -3
  113. package/index.js +5 -2
  114. package/live.d.ts +1 -0
  115. package/live.js +1 -1
  116. package/package.json +6 -5
  117. package/server/checkpoint-ownership.d.ts +6 -0
  118. package/server/checkpoint-ownership.js +42 -0
  119. package/server/conversation-store.d.ts +92 -0
  120. package/server/conversation-store.js +131 -0
  121. package/server/conversation.d.ts +193 -0
  122. package/server/conversation.js +552 -0
  123. package/server/decision-handler.d.ts +12 -4
  124. package/server/decision-handler.js +29 -38
  125. package/server/embedding-handler.d.ts +33 -0
  126. package/server/embedding-handler.js +45 -0
  127. package/server/handler.d.ts +58 -20
  128. package/server/handler.js +441 -238
  129. package/server/mod.d.ts +15 -6
  130. package/server/mod.js +13 -5
  131. package/server/page-dispatch.d.ts +22 -0
  132. package/server/page-dispatch.js +127 -0
  133. package/server/request-body.d.ts +2 -0
  134. package/server/request-body.js +42 -0
  135. package/server/request-check.d.ts +6 -1
  136. package/server/request-check.js +27 -12
  137. package/server/session-authority.d.ts +8 -0
  138. package/server/session-authority.js +78 -0
  139. package/server/session-boundary.d.ts +26 -0
  140. package/server/session-boundary.js +64 -0
  141. package/server/session-identity.d.ts +16 -0
  142. package/server/session-identity.js +41 -0
  143. package/server/session-limits.d.ts +12 -0
  144. package/server/session-limits.js +59 -0
  145. package/server/session-store.d.ts +69 -20
  146. package/server/session-store.js +220 -5
  147. package/server/single-action.d.ts +21 -0
  148. package/server/single-action.js +30 -0
  149. package/server/steer-inbox.js +1 -1
  150. package/server/tool-operations.d.ts +6 -0
  151. package/server/tool-operations.js +132 -0
  152. package/server/turn-input.d.ts +10 -0
  153. package/server/turn-input.js +29 -0
  154. package/styles/ink-controls.css +78 -78
  155. package/ui/CardWall.d.ts +10 -0
  156. package/ui/CardWall.js +61 -0
  157. package/ui/ChatComposerBar.d.ts +28 -5
  158. package/ui/ChatComposerBar.js +85 -30
  159. package/ui/ChatTranscript.d.ts +14 -1
  160. package/ui/ChatTranscript.js +110 -57
  161. package/ui/ConsoleFrame.d.ts +61 -4
  162. package/ui/ConsoleFrame.js +109 -9
  163. package/ui/DecisionAnswers.d.ts +40 -2
  164. package/ui/DecisionAnswers.js +166 -35
  165. package/ui/DecisionImages.d.ts +16 -0
  166. package/ui/DecisionImages.js +46 -0
  167. package/ui/EmbeddingDocuments.d.ts +78 -0
  168. package/ui/EmbeddingDocuments.js +278 -0
  169. package/ui/EmbeddingQuery.d.ts +27 -0
  170. package/ui/EmbeddingQuery.js +95 -0
  171. package/ui/EmbeddingResults.d.ts +41 -0
  172. package/ui/EmbeddingResults.js +165 -0
  173. package/ui/EmbeddingSpace.d.ts +26 -0
  174. package/ui/EmbeddingSpace.js +106 -0
  175. package/ui/LiveCaptionsPanel.d.ts +22 -0
  176. package/ui/LiveCaptionsPanel.js +27 -0
  177. package/ui/LiveRunner.d.ts +6 -4
  178. package/ui/LiveRunner.js +42 -55
  179. package/ui/PaneState.d.ts +33 -0
  180. package/ui/PaneState.js +50 -0
  181. package/ui/SchemaFields.d.ts +1 -1
  182. package/ui/SchemaFields.js +54 -26
  183. package/ui/ShapedData.js +73 -28
  184. package/ui/SidePanel.d.ts +74 -5
  185. package/ui/SidePanel.js +82 -9
  186. package/ui/StateFields.d.ts +10 -2
  187. package/ui/StateFields.js +56 -13
  188. package/ui/TheoremChat.d.ts +65 -14
  189. package/ui/TheoremChat.js +66 -78
  190. package/ui/TheoremDecision.d.ts +12 -20
  191. package/ui/TheoremDecision.js +78 -35
  192. package/ui/TheoremEmbedding.d.ts +27 -0
  193. package/ui/TheoremEmbedding.js +122 -0
  194. package/ui/TheoremHost.d.ts +22 -5
  195. package/ui/TheoremHost.js +127 -37
  196. package/ui/ToolGateCard.d.ts +5 -1
  197. package/ui/ToolGateCard.js +30 -23
  198. package/ui/ToolResult.js +12 -10
  199. package/ui/ToolResultCharts.js +29 -12
  200. package/ui/TraceCharts.d.ts +2 -2
  201. package/ui/TraceCharts.js +48 -17
  202. package/ui/TraceGuardrails.d.ts +3 -2
  203. package/ui/TraceGuardrails.js +24 -6
  204. package/ui/TraceInspector.d.ts +11 -1
  205. package/ui/TraceInspector.js +45 -28
  206. package/ui/TraceInspectorPanel.d.ts +31 -9
  207. package/ui/TraceInspectorPanel.js +77 -39
  208. package/ui/TraceOverview.d.ts +7 -3
  209. package/ui/TraceOverview.js +44 -14
  210. package/ui/TraceSpanDetail.js +24 -22
  211. package/ui/TraceStory.js +9 -8
  212. package/ui/TraceValues.js +19 -9
  213. package/ui/TraceWaterfall.js +12 -9
  214. package/ui/VoiceNote.js +4 -4
  215. package/ui/approval-heading.d.ts +4 -0
  216. package/ui/approval-heading.js +9 -0
  217. package/ui/arrive.d.ts +18 -0
  218. package/ui/arrive.js +46 -0
  219. package/ui/built/theme.css +86 -68
  220. package/ui/built/theme.css.d.ts +9 -0
  221. package/ui/built/theorem.d.ts +3 -3
  222. package/ui/built/theorem.js +34 -5
  223. package/ui/built/theorem.variants.d.ts +8 -2
  224. package/ui/code-language.d.ts +12 -0
  225. package/ui/code-language.js +50 -0
  226. package/ui/disclosure-motion.js +53 -9
  227. package/ui/embedding-space-gl.d.ts +37 -0
  228. package/ui/embedding-space-gl.js +663 -0
  229. package/ui/embedding-space-view.d.ts +8 -0
  230. package/ui/embedding-space-view.js +8 -0
  231. package/ui/glide.d.ts +7 -0
  232. package/ui/glide.js +44 -0
  233. package/ui/icons.js +2 -1
  234. package/ui/index.d.ts +20 -9
  235. package/ui/index.js +20 -10
  236. package/ui/labels-provider.js +5 -2
  237. package/ui/labels.d.ts +374 -85
  238. package/ui/labels.js +1028 -188
  239. package/ui/markdown-plugins.d.ts +9 -0
  240. package/ui/markdown-plugins.js +13 -0
  241. package/ui/mcp-icon.d.ts +6 -0
  242. package/ui/mcp-icon.js +8 -0
  243. package/ui/row-keys.d.ts +19 -0
  244. package/ui/row-keys.js +33 -0
  245. package/ui/theme.d.ts +1 -1
  246. package/ui/theme.js +2 -2
  247. package/ui/theorem-theme.js +32 -10
  248. package/ui/trace-palette.d.ts +2 -16
  249. package/ui/trace-palette.js +2 -16
  250. package/ui/trace-patterns.d.ts +11 -3
  251. package/ui/trace-patterns.js +16 -3
  252. package/ui/transcript-copy-text.js +1 -0
  253. package/ui/wall-columns.d.ts +8 -0
  254. package/ui/wall-columns.js +19 -0
  255. package/client/walk-away.d.ts +0 -34
  256. package/client/walk-away.js +0 -48
  257. package/server/walk-away.d.ts +0 -22
  258. package/server/walk-away.js +0 -41
@@ -1,4 +1,6 @@
1
- import type { DecisionResult } from '@theoremjs/agents';
1
+ import type { DecisionJson, DecisionResult } from '@theoremjs/agents';
2
+ import { type ReactNode } from 'react';
3
+ import { type DecisionImages } from '../client/decision-images.ts';
2
4
  import type { DecisionInterface } from '../client/decision-transport.ts';
3
5
  export type TheoremDecisionAnswersProps = {
4
6
  iface: DecisionInterface;
@@ -7,9 +9,45 @@ export type TheoremDecisionAnswersProps = {
7
9
  elapsedMs?: number | null;
8
10
  /** A newer decision is running: the answers dim until it lands. */
9
11
  isStale?: boolean;
12
+ /** The images and state the answers were asked about, which box answers are cut from. Without an image, a box is drawn in a plain frame. */
13
+ asked?: {
14
+ images: DecisionImages;
15
+ state: DecisionJson;
16
+ };
17
+ /** Opens a question where it is set up. Each answer then has a button that calls it with the question's id. */
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?: Readonly<Record<string, string>>;
10
21
  };
22
+ /** The reader's locale's percent, decimal and dollar formats. */
23
+ export declare function useNumbers(): {
24
+ percent: Intl.NumberFormat;
25
+ decimal: Intl.NumberFormat;
26
+ usd: Intl.NumberFormat;
27
+ };
28
+ /** The option's share as a bar, drawn like the trace's. */
29
+ export declare function Share({ value, isLead }: {
30
+ value: number;
31
+ isLead: boolean;
32
+ }): import("react").JSX.Element;
33
+ /**
34
+ * A console's result as it streams in: dimmed while the next one runs, then
35
+ * one quiet line for the model, time, tokens, and cost.
36
+ */
37
+ export declare function ResultStack({ model, usage, elapsedMs, isStale, tokens, children, }: {
38
+ model: string;
39
+ usage?: {
40
+ inputTokens?: number;
41
+ costUsd?: number;
42
+ };
43
+ elapsedMs?: number | null;
44
+ isStale?: boolean;
45
+ /** The tokens read, as the console words them. */
46
+ tokens: (count: number) => string;
47
+ children: ReactNode;
48
+ }): import("react").JSX.Element;
11
49
  /**
12
50
  * A decision's answers, one card per question in the order the profile asks
13
51
  * them, and one quiet line for the model, time, tokens, and cost.
14
52
  */
15
- export declare function TheoremDecisionAnswers({ iface, result, elapsedMs, isStale }: TheoremDecisionAnswersProps): import("react").JSX.Element;
53
+ export declare function TheoremDecisionAnswers({ iface, result, elapsedMs, isStale, asked, onQuestion, questionText, }: TheoremDecisionAnswersProps): import("react").JSX.Element;
@@ -1,22 +1,35 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Grid } from '@astryxdesign/core/Grid';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { ClickableCard } from '@astryxdesign/core/ClickableCard';
3
3
  import { HStack } from '@astryxdesign/core/HStack';
4
+ import { Icon } from '@astryxdesign/core/Icon';
5
+ import { IconButton } from '@astryxdesign/core/IconButton';
6
+ import { useLocale } from '@astryxdesign/core/i18n';
7
+ import { Lightbox } from '@astryxdesign/core/Lightbox';
4
8
  import { Text } from '@astryxdesign/core/Text';
9
+ import { Tooltip } from '@astryxdesign/core/Tooltip';
5
10
  import { VStack } from '@astryxdesign/core/VStack';
6
- import { useLocale } from '@astryxdesign/core/i18n';
7
- import { useMemo } from 'react';
8
- import { workDuration } from "./labels.js";
9
- import { ChartCard, TRACE_RADIUS, traceFill } from "./trace-patterns.js";
11
+ import { IconPencil } from '@tabler/icons-react';
12
+ import { useEffect, useMemo, useState } from 'react';
13
+ import { boxSource, cropOf, imageSrc, markedBox, } from "../client/decision-images.js";
14
+ import { Arrive, Stream } from "./arrive.js";
15
+ import { CardWall } from "./CardWall.js";
16
+ import { resultMeta } from "./labels.js";
10
17
  import { useLabels } from "./labels-provider.js";
18
+ import { ChartCard, TRACE_RADIUS, traceFill } from "./trace-patterns.js";
11
19
  const EASE = 'var(--duration-medium) var(--ease-standard)';
12
20
  /** Height of a share bar, as the trace draws one. */
13
21
  const SHARE_PX = 12;
14
- function useNumbers() {
22
+ /** The reader's locale's percent, decimal and dollar formats. */
23
+ export function useNumbers() {
15
24
  const locale = useLocale();
16
25
  return useMemo(() => ({
17
26
  percent: new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 0 }),
18
27
  decimal: new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),
19
- usd: new Intl.NumberFormat(locale, { style: 'currency', currency: 'USD', maximumSignificantDigits: 2 }),
28
+ usd: new Intl.NumberFormat(locale, {
29
+ style: 'currency',
30
+ currency: 'USD',
31
+ maximumSignificantDigits: 2,
32
+ }),
20
33
  }), [locale]);
21
34
  }
22
35
  /** The pick in the trace's clear series, the rest in its shade. */
@@ -24,7 +37,7 @@ function answerFill(isLead) {
24
37
  return traceFill(isLead ? 'clear' : 'shade2');
25
38
  }
26
39
  /** The option's share as a bar, drawn like the trace's. */
27
- function Share({ value, isLead }) {
40
+ export function Share({ value, isLead }) {
28
41
  return (_jsx("div", { style: {
29
42
  height: SHARE_PX,
30
43
  width: `${String(Math.max(value, 0.01) * 100)}%`,
@@ -33,11 +46,11 @@ function Share({ value, isLead }) {
33
46
  transition: `width ${EASE}`,
34
47
  } }));
35
48
  }
36
- function ChoiceAnswer({ title, answer }) {
49
+ function ChoiceAnswer({ title, aside, detail, answer, }) {
37
50
  const t = useLabels();
38
51
  const n = useNumbers();
39
52
  const options = Object.entries(answer.probabilities).sort(([, a], [, b]) => b - a);
40
- return (_jsx(ChartCard, { title: title, value: answer.choice, note: t('@theorem.decision.confidence', { percent: n.percent.format(answer.confidence) }), children: _jsx(VStack, { gap: 2, role: "list", children: options.map(([label, p]) => (_jsxs(VStack, { gap: 1, role: "listitem", "aria-label": t('@theorem.decision.option', { label, percent: n.percent.format(p) }), children: [_jsxs(HStack, { gap: 2, hAlign: "between", "aria-hidden": true, children: [_jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: label }), _jsx(Text, { type: "supporting", hasTabularNumbers: true, children: n.percent.format(p) })] }), _jsx(Share, { value: p, isLead: label === answer.choice })] }, label))) }) }));
53
+ return (_jsx(ChartCard, { title: title, aside: aside, detail: detail, value: answer.choice, note: t('@theorem.decision.confidence', { percent: n.percent.format(answer.confidence) }), children: _jsx(VStack, { gap: 2, role: "list", children: options.map(([label, p]) => (_jsxs(VStack, { gap: 1, role: "listitem", "aria-label": t('@theorem.decision.option', { label, percent: n.percent.format(p) }), children: [_jsxs(HStack, { gap: 2, hAlign: "between", "aria-hidden": true, children: [_jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: label }), _jsx(Text, { type: "supporting", hasTabularNumbers: true, children: n.percent.format(p) })] }), _jsx(Share, { value: p, isLead: label === answer.choice })] }, label))) }) }));
41
54
  }
42
55
  const SCALE_HEIGHT = 56;
43
56
  /** A level's short name: the words before its colon or dash ("Low: reversible…" → "Low"), when there are few. */
@@ -46,15 +59,24 @@ function levelName(label) {
46
59
  return head && head.length <= 32 && head !== label ? head : label;
47
60
  }
48
61
  /** Each level's probability as a column, with a mark where the score falls between them. */
49
- function ScoreAnswer({ title, answer, labels }) {
62
+ function ScoreAnswer({ title, aside, detail, answer, labels, }) {
50
63
  const t = useLabels();
51
64
  const n = useNumbers();
52
65
  const levels = labels.length;
53
66
  const nearest = Math.min(levels - 1, Math.max(0, Math.round(answer.score)));
54
- return (_jsx(ChartCard, { title: title, value: labels[nearest] === undefined ? n.decimal.format(answer.score) : levelName(labels[nearest]), note: [
55
- t('@theorem.decision.score_of', { score: n.decimal.format(answer.score), max: n.decimal.format(levels - 1) }),
67
+ return (_jsx(ChartCard, { title: title, aside: aside, detail: detail, value: labels[nearest] === undefined ? n.decimal.format(answer.score) : levelName(labels[nearest]), note: [
68
+ t('@theorem.decision.score_of', {
69
+ score: n.decimal.format(answer.score),
70
+ max: n.decimal.format(levels - 1),
71
+ }),
56
72
  t('@theorem.decision.confidence', { percent: n.percent.format(answer.confidence) }),
57
- ].join(' · '), children: _jsxs(VStack, { gap: 1, children: [_jsxs("div", { "aria-hidden": true, style: { position: 'relative', display: 'flex', alignItems: 'flex-end', gap: 4, height: SCALE_HEIGHT }, children: [labels.map((label, level) => (_jsx("div", { style: {
73
+ ].join(' · '), children: _jsxs(VStack, { gap: 1, children: [_jsxs("div", { "aria-hidden": true, style: {
74
+ position: 'relative',
75
+ display: 'flex',
76
+ alignItems: 'flex-end',
77
+ gap: 4,
78
+ height: SCALE_HEIGHT,
79
+ }, children: [labels.map((label, level) => (_jsx("div", { style: {
58
80
  flex: 1,
59
81
  height: `${String(Math.max(0.04, answer.probabilities[String(level)] ?? 0) * 100)}%`,
60
82
  borderRadius: TRACE_RADIUS,
@@ -67,36 +89,145 @@ function ScoreAnswer({ title, answer, labels }) {
67
89
  width: 0,
68
90
  borderInlineStart: '1.5px dashed var(--color-text-secondary)',
69
91
  transition: `left ${EASE}`,
70
- } })] }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: labels.map((label, level) => (_jsx("div", { style: { flex: 1, minWidth: 0 }, title: label, children: _jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: levelName(label) }) }, `${String(level)}:${label}`))) }), _jsx("span", { style: { position: 'absolute', width: 1, height: 1, overflow: 'hidden', clipPath: 'inset(50%)' }, children: labels.map((label, level) => t('@theorem.decision.option', { label, percent: n.percent.format(answer.probabilities[String(level)] ?? 0) })).join('; ') })] }) }));
92
+ } })] }), _jsx("div", { style: { display: 'flex', gap: 4 }, children: labels.map((label, level) => (_jsx("div", { style: { flex: 1, minWidth: 0 }, title: label, children: _jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: levelName(label) }) }, `${String(level)}:${label}`))) }), _jsx("span", { style: {
93
+ position: 'absolute',
94
+ width: 1,
95
+ height: 1,
96
+ overflow: 'hidden',
97
+ clipPath: 'inset(50%)',
98
+ }, children: labels
99
+ .map((label, level) => t('@theorem.decision.option', {
100
+ label,
101
+ percent: n.percent.format(answer.probabilities[String(level)] ?? 0),
102
+ }))
103
+ .join('; ') })] }) }));
104
+ }
105
+ function NumberAnswer({ title, aside, detail, answer, }) {
106
+ const n = useNumbers();
107
+ return (_jsx(ChartCard, { title: title, aside: aside, detail: detail, value: n.decimal.format(answer.noul) }));
108
+ }
109
+ const BOX_FRAME_HEIGHT = 72;
110
+ const CROP_HEIGHT = 168;
111
+ /** An image's natural size, once the page has loaded it. */
112
+ function useImageSize(src) {
113
+ const [size, setSize] = useState(null);
114
+ useEffect(() => {
115
+ const image = new Image();
116
+ image.onload = () => {
117
+ setSize({ width: image.naturalWidth, height: image.naturalHeight });
118
+ };
119
+ image.src = src;
120
+ }, [src]);
121
+ return size;
122
+ }
123
+ /** A theme colour as the page draws it, for a canvas that cannot read the theme's tokens. */
124
+ function themeColor(element, token) {
125
+ const shown = element.style.color;
126
+ element.style.color = `var(${token})`;
127
+ const color = getComputedStyle(element).color;
128
+ element.style.color = shown;
129
+ return color;
130
+ }
131
+ /** The part of the image the box covers, cut out at its own proportions. It opens the whole image with the box outlined. */
132
+ function BoxCrop({ source, box, caption, }) {
133
+ const t = useLabels();
134
+ const src = imageSrc(source);
135
+ const size = useImageSize(src);
136
+ const [marked, setMarked] = useState(null);
137
+ if (!size)
138
+ return null;
139
+ const crop = cropOf(box);
140
+ const ratio = (size.width * (box.right - box.left)) / (size.height * (box.bottom - box.top));
141
+ const open = t('@theorem.decision.open_box');
142
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { content: open, children: _jsx(ClickableCard, { label: open, padding: 0, onClick: (event) => {
143
+ const marks = {
144
+ outline: themeColor(event.currentTarget, '--color-accent'),
145
+ shade: themeColor(event.currentTarget, '--color-overlay'),
146
+ };
147
+ void markedBox(src, box, marks).then(setMarked);
148
+ }, children: _jsx("div", { role: "img", "aria-label": t('@theorem.decision.box_in'), style: {
149
+ position: 'relative',
150
+ overflow: 'hidden',
151
+ aspectRatio: String(ratio),
152
+ height: CROP_HEIGHT,
153
+ maxWidth: '100%',
154
+ borderRadius: TRACE_RADIUS,
155
+ }, children: _jsx("img", { src: src, alt: "", style: {
156
+ position: 'absolute',
157
+ width: `${String(crop.width)}%`,
158
+ height: `${String(crop.height)}%`,
159
+ left: `${String(crop.left)}%`,
160
+ top: `${String(crop.top)}%`,
161
+ maxWidth: 'none',
162
+ } }) }) }) }), _jsx(Lightbox, { isOpen: marked !== null, onOpenChange: (isOpen) => {
163
+ if (!isOpen)
164
+ setMarked(null);
165
+ }, media: { src: marked ?? src, alt: t('@theorem.decision.box_marked'), caption }, hasZoom: true })] }));
71
166
  }
72
- function NumberAnswer({ title, answer }) {
167
+ /** The box cut from the image its question names, or drawn in a frame that stands for the whole image. */
168
+ function BoxAnswer({ title, aside, detail, answer, field, asked, }) {
169
+ const t = useLabels();
170
+ const source = asked && boxSource(asked.images, asked.state, field);
73
171
  const n = useNumbers();
74
- return _jsx(ChartCard, { title: title, value: n.decimal.format(answer.noul) });
172
+ const { top, bottom, left, right } = answer.box;
173
+ const edges = t('@theorem.decision.box_edges', {
174
+ left: n.percent.format(left),
175
+ top: n.percent.format(top),
176
+ right: n.percent.format(right),
177
+ bottom: n.percent.format(bottom),
178
+ });
179
+ return (_jsx(ChartCard, { title: title, aside: aside, detail: detail, value: edges, note: t('@theorem.decision.confidence', { percent: n.percent.format(answer.confidence) }), children: source ? (_jsx(BoxCrop, { source: source, box: answer.box, caption: `${title}: ${edges}` })) : (_jsx("div", { "aria-hidden": true, style: {
180
+ position: 'relative',
181
+ height: BOX_FRAME_HEIGHT,
182
+ borderRadius: TRACE_RADIUS,
183
+ border: '1px solid var(--color-border)',
184
+ }, children: _jsx("div", { style: {
185
+ position: 'absolute',
186
+ insetInlineStart: `${String(left * 100)}%`,
187
+ insetBlockStart: `${String(top * 100)}%`,
188
+ width: `${String((right - left) * 100)}%`,
189
+ height: `${String((bottom - top) * 100)}%`,
190
+ borderRadius: 2,
191
+ background: answerFill(true),
192
+ transition: `all ${EASE}`,
193
+ } }) })) }));
75
194
  }
76
195
  /** One question's answer in the trace's chart card, titled by the question and its kind. */
77
- function AnswerCard({ question, answer }) {
196
+ function AnswerCard({ question, answer, asked, onQuestion, detail, }) {
78
197
  const t = useLabels();
79
198
  const title = `${question.id} · ${t(`@theorem.decision.type.${question.type}`)}`;
80
- if (answer?.type === 'choice')
81
- return _jsx(ChoiceAnswer, { title: title, answer: answer });
82
- if (answer?.type === 'score')
83
- return _jsx(ScoreAnswer, { title: title, answer: answer, labels: question.labels });
84
- if (answer?.type === 'noul')
85
- return _jsx(NumberAnswer, { title: title, answer: answer });
86
- return null;
199
+ if (!answer)
200
+ return null;
201
+ const edit = t('@theorem.decision.edit_question', { question: question.id });
202
+ const head = {
203
+ title,
204
+ detail,
205
+ aside: onQuestion ? (_jsx(IconButton, { label: edit, tooltip: edit, size: "sm", variant: "ghost", icon: _jsx(Icon, { icon: IconPencil, size: "xsm", color: "secondary" }), onClick: () => {
206
+ onQuestion(question.id);
207
+ } })) : undefined,
208
+ };
209
+ if (answer.type === 'choice')
210
+ return _jsx(ChoiceAnswer, { ...head, answer: answer });
211
+ if (answer.type === 'score')
212
+ return _jsx(ScoreAnswer, { ...head, answer: answer, labels: question.labels });
213
+ if (answer.type === 'noul')
214
+ return _jsx(NumberAnswer, { ...head, answer: answer });
215
+ return _jsx(BoxAnswer, { ...head, answer: answer, field: question.field, asked: asked });
216
+ }
217
+ /**
218
+ * A console's result as it streams in: dimmed while the next one runs, then
219
+ * one quiet line for the model, time, tokens, and cost.
220
+ */
221
+ export function ResultStack({ model, usage, elapsedMs, isStale, tokens, children, }) {
222
+ const t = useLabels();
223
+ const n = useNumbers();
224
+ return (_jsx(Stream, { children: _jsxs(VStack, { gap: 3, style: { opacity: isStale ? 0.5 : 1, transition: `opacity ${EASE}` }, "aria-busy": isStale, children: [children, _jsx(Arrive, { children: _jsx(Text, { type: "supporting", color: "secondary", hasTabularNumbers: true, children: resultMeta(t, n.usd, { model, usage, elapsedMs, tokens }) }) })] }, elapsedMs ?? 0) }));
87
225
  }
88
226
  /**
89
227
  * A decision's answers, one card per question in the order the profile asks
90
228
  * them, and one quiet line for the model, time, tokens, and cost.
91
229
  */
92
- export function TheoremDecisionAnswers({ iface, result, elapsedMs, isStale }) {
230
+ export function TheoremDecisionAnswers({ iface, result, elapsedMs, isStale, asked, onQuestion, questionText, }) {
93
231
  const t = useLabels();
94
- const n = useNumbers();
95
- const meta = [
96
- result.model,
97
- elapsedMs == null ? null : workDuration(t, elapsedMs),
98
- result.usage ? t('@theorem.decision.tokens', { count: result.usage.inputTokens }) : null,
99
- result.usage?.costUsd === undefined ? null : n.usd.format(result.usage.costUsd),
100
- ].filter(Boolean);
101
- return (_jsxs(VStack, { gap: 3, style: { opacity: isStale ? 0.5 : 1, transition: `opacity ${EASE}` }, "aria-busy": isStale, children: [_jsx(Grid, { columns: { minWidth: 260, repeat: 'fill' }, gap: 3, children: iface.questions.map((question) => (_jsx(AnswerCard, { question: question, answer: result.answers[question.id] }, question.id))) }), _jsx(Text, { type: "supporting", color: "secondary", hasTabularNumbers: true, children: meta.join(' · ') })] }));
232
+ return (_jsx(ResultStack, { model: result.model, usage: result.usage, elapsedMs: elapsedMs, isStale: isStale, tokens: (count) => t('@theorem.decision.tokens', { count }), children: _jsx(CardWall, { minWidth: 260, children: iface.questions.map((question) => (_jsx(Arrive, { children: _jsx(AnswerCard, { question: question, answer: result.answers[question.id], asked: asked, onQuestion: onQuestion, detail: questionText?.[question.id] }) }, question.id))) }) }));
102
233
  }
@@ -0,0 +1,16 @@
1
+ import { type DecisionImages } from '../client/decision-images.ts';
2
+ /**
3
+ * Reads each file as an image and hands over the ones that are, with the name of the first that
4
+ * is not as the refusal to show.
5
+ */
6
+ export declare function useImageReader(): {
7
+ read: (files: File | File[] | null) => Promise<[File, import("../../../mod.ts").DecisionAsset][]>;
8
+ status: {
9
+ type: "error";
10
+ message: string;
11
+ } | undefined;
12
+ };
13
+ /** A drop area under the state: each PNG or JPEG dropped or chosen becomes a field that holds it. */
14
+ export declare function ImageDrop({ onImages, }: {
15
+ onImages: (files: [File, DecisionImages[string]][]) => void;
16
+ }): import("react").JSX.Element;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { FileInput } from '@astryxdesign/core/FileInput';
3
+ import { useState } from 'react';
4
+ import { IMAGE_ACCEPT, readImage } from "../client/decision-images.js";
5
+ import { useLabels } from "./labels-provider.js";
6
+ /** The files a file input reports, as a list. */
7
+ function asList(files) {
8
+ if (files === null)
9
+ return [];
10
+ return Array.isArray(files) ? files : [files];
11
+ }
12
+ /**
13
+ * Reads each file as an image and hands over the ones that are, with the name of the first that
14
+ * is not as the refusal to show.
15
+ */
16
+ export function useImageReader() {
17
+ const t = useLabels();
18
+ const [refused, setRefused] = useState(null);
19
+ const read = async (files) => {
20
+ setRefused(null);
21
+ const held = [];
22
+ for (const file of asList(files)) {
23
+ const image = await readImage(file);
24
+ if (image)
25
+ held.push([file, image]);
26
+ else
27
+ setRefused(file.name);
28
+ }
29
+ return held;
30
+ };
31
+ const status = refused === null
32
+ ? undefined
33
+ : { type: 'error', message: t('@theorem.decision.image_bad', { file: refused }) };
34
+ return { read, status };
35
+ }
36
+ /** A drop area under the state: each PNG or JPEG dropped or chosen becomes a field that holds it. */
37
+ export function ImageDrop({ onImages, }) {
38
+ const t = useLabels();
39
+ const { read, status } = useImageReader();
40
+ return (_jsx(FileInput, { label: t('@theorem.decision.add_image'), isLabelHidden: true, mode: "dropzone", isMultiple: true, accept: IMAGE_ACCEPT, value: null, placeholder: t('@theorem.decision.add_image_hint'), status: status, statusVariant: "detached", onChange: (files) => {
41
+ void read(files).then((images) => {
42
+ if (images.length > 0)
43
+ onImages(images);
44
+ });
45
+ } }));
46
+ }
@@ -0,0 +1,78 @@
1
+ import { type EmbeddingInput } from '@theoremjs/agents';
2
+ import { type ReactNode } from 'react';
3
+ import { type EmbeddingInputCheck, type EmbeddingMediaPart } from '../client/embedding-input.ts';
4
+ import type { EmbeddingInterface } from '../client/embedding-transport.ts';
5
+ import { type ConsoleView } from './ConsoleFrame.tsx';
6
+ import type { LabelText } from './labels.ts';
7
+ /** The profile's `inputs`, which every request is held to. */
8
+ export type Rules = EmbeddingInterface['inputs'];
9
+ /** A document as the list shows it: what it sends, and the file it came from. */
10
+ export type EmbeddingDocument = {
11
+ input: EmbeddingInput;
12
+ from?: string;
13
+ };
14
+ type SplitChoice = 'whole' | 'lines' | 'paragraphs' | 'commas' | 'custom';
15
+ /** What the check says is wrong, about the input it names. */
16
+ export declare function inputIssue(t: LabelText, check: EmbeddingInputCheck, name: string): string | null;
17
+ /**
18
+ * The documents: pasted text split as chosen, then files and JSON, in that
19
+ * order. Text files are split as the text is; any other file is one document.
20
+ */
21
+ export declare function useEmbeddingDocuments(defaultText: string, rules: Rules, defaultDocuments?: readonly EmbeddingDocument[]): {
22
+ text: string;
23
+ setText: import("react").Dispatch<import("react").SetStateAction<string>>;
24
+ choice: SplitChoice;
25
+ setChoice: import("react").Dispatch<import("react").SetStateAction<SplitChoice>>;
26
+ separator: string;
27
+ setSeparator: import("react").Dispatch<import("react").SetStateAction<string>>;
28
+ documents: EmbeddingDocument[];
29
+ inputs: EmbeddingInput[];
30
+ issue: string | null;
31
+ requests: number;
32
+ view: ConsoleView;
33
+ setView: (next: ConsoleView) => void;
34
+ json: string;
35
+ jsonInvalid: boolean;
36
+ setJson: (next: string) => void;
37
+ addFiles: (files: readonly File[]) => Promise<void>;
38
+ remove: (at: readonly number[]) => void;
39
+ /** Where the pasted text's documents end and the removable ones begin. */
40
+ fromText: number;
41
+ };
42
+ export type EmbeddingDocumentsForm = ReturnType<typeof useEmbeddingDocuments>;
43
+ /** What a document reads as in a list: its text, or its file's name, type, and size. */
44
+ export declare function documentLabel(t: LabelText, bytes: (count: number) => string, document: EmbeddingDocument, number: number): {
45
+ label: string;
46
+ detail: string | null;
47
+ };
48
+ /** Where the text is cut, and the separator when it is one of the user's own. */
49
+ /** A file field that reads what is dropped or chosen, and says which files it could not read. */
50
+ export declare function EmbeddingFileDrop({ label, placeholder, accept, isMultiple, onFiles, }: {
51
+ label: string;
52
+ placeholder: string;
53
+ accept: readonly string[];
54
+ isMultiple?: boolean;
55
+ onFiles: (files: File[]) => Promise<void>;
56
+ }): import("react").JSX.Element;
57
+ /** One file: its picture above its type, with its name and size on hover and Remove. */
58
+ export declare function FileTile({ part, name, label, onRemove, }: {
59
+ part: EmbeddingMediaPart;
60
+ /** The file's name; a query's file keeps none, so its type stands in. */
61
+ name?: string;
62
+ label: string;
63
+ onRemove: () => void;
64
+ }): import("react").JSX.Element;
65
+ /** Text with its file beside it at two thirds and one third, or whichever of them there is. */
66
+ export declare function TextBesideFile({ text, file }: {
67
+ text: ReactNode;
68
+ file: ReactNode;
69
+ }): import("react").JSX.Element;
70
+ /** The documents card: text and files as fields, or the inputs as JSON, and Index. */
71
+ export declare function EmbeddingDocuments({ form, rules, isRunning, onRun, onStop, }: {
72
+ form: EmbeddingDocumentsForm;
73
+ rules: Rules;
74
+ isRunning: boolean;
75
+ onRun: () => void;
76
+ onStop: () => void;
77
+ }): import("react").JSX.Element;
78
+ export {};