@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.
- package/README.md +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
package/ui/StateFields.js
CHANGED
|
@@ -1,40 +1,70 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from '@astryxdesign/core/Button';
|
|
3
3
|
import { FieldLabel } from '@astryxdesign/core/Field';
|
|
4
|
+
import { FileInput } from '@astryxdesign/core/FileInput';
|
|
4
5
|
import { HStack } from '@astryxdesign/core/HStack';
|
|
5
6
|
import { IconButton } from '@astryxdesign/core/IconButton';
|
|
7
|
+
import { Lightbox } from '@astryxdesign/core/Lightbox';
|
|
6
8
|
import { NumberInput } from '@astryxdesign/core/NumberInput';
|
|
7
9
|
import { StackItem } from '@astryxdesign/core/Stack';
|
|
8
10
|
import { Switch } from '@astryxdesign/core/Switch';
|
|
9
11
|
import { Text } from '@astryxdesign/core/Text';
|
|
10
12
|
import { TextArea } from '@astryxdesign/core/TextArea';
|
|
11
13
|
import { TextInput } from '@astryxdesign/core/TextInput';
|
|
14
|
+
import { Thumbnail } from '@astryxdesign/core/Thumbnail';
|
|
12
15
|
import { Tokenizer } from '@astryxdesign/core/Tokenizer';
|
|
13
16
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
14
17
|
import { IconPlus, IconX } from '@tabler/icons-react';
|
|
15
|
-
import { useId, useState } from 'react';
|
|
18
|
+
import { createContext, useContext, useId, useState } from 'react';
|
|
19
|
+
import { IMAGE_ACCEPT, imageSrc } from "../client/decision-images.js";
|
|
16
20
|
import { humanize, isPlain } from "../client/shaped-data.js";
|
|
17
21
|
import { blankLike, isWordList, tableColumns } from "../client/state-fields.js";
|
|
22
|
+
import { useImageReader } from "./DecisionImages.js";
|
|
18
23
|
import { useLabels } from "./labels-provider.js";
|
|
24
|
+
import { useKeyedRows } from "./row-keys.js";
|
|
19
25
|
const NO_SUGGESTIONS = { search: () => [], bootstrap: () => [] };
|
|
20
26
|
/** Text this long, or on several lines, gets a text area. */
|
|
21
27
|
const LONG_TEXT = 48;
|
|
28
|
+
/** A text area's height: one row per line of its text, within `min` and `max`. Wrapped lines do not count. */
|
|
29
|
+
export function textRows(text, min, max) {
|
|
30
|
+
return Math.min(Math.max(text.split('\n').length, min), max);
|
|
31
|
+
}
|
|
32
|
+
/** The images the state names, and how to change them; a field whose whole text names one shows it. */
|
|
33
|
+
export const StateImages = createContext({ images: {}, onImages: () => { } });
|
|
34
|
+
/** A field that holds an image: the image, and a file input that replaces it. */
|
|
35
|
+
function ImageField({ value, label, isLabelHidden, }) {
|
|
36
|
+
const t = useLabels();
|
|
37
|
+
const { images, onImages } = useContext(StateImages);
|
|
38
|
+
const { read, status } = useImageReader();
|
|
39
|
+
const [isOpen, setOpen] = useState(false);
|
|
40
|
+
const src = Object.hasOwn(images, value) ? imageSrc(images[value]) : undefined;
|
|
41
|
+
return (_jsxs(HStack, { gap: 2, vAlign: "center", children: [_jsx(Thumbnail, { src: src, alt: label, label: value, onClick: src ? () => setOpen(true) : undefined }), src ? (_jsx(Lightbox, { isOpen: isOpen, onOpenChange: setOpen, media: { src, alt: label, caption: value }, hasZoom: true })) : null, _jsx(StackItem, { size: "fill", children: _jsx(FileInput, { label: label, isLabelHidden: isLabelHidden, accept: IMAGE_ACCEPT, value: null, placeholder: t('@theorem.decision.replace_image'), status: status, statusVariant: "detached", onChange: (files) => {
|
|
42
|
+
void read(files).then(([first]) => {
|
|
43
|
+
if (first)
|
|
44
|
+
onImages({ ...images, [value]: first[1] });
|
|
45
|
+
});
|
|
46
|
+
} }) })] }));
|
|
47
|
+
}
|
|
22
48
|
/** A string, number, true/false, or null, as its input. */
|
|
23
49
|
function PlainField({ value, onChange, label, isLabelHidden }) {
|
|
24
|
-
|
|
50
|
+
const { images } = useContext(StateImages);
|
|
51
|
+
// why: Decided once, so the field doesn't swap inputs (and lose focus) as the text grows or changes.
|
|
25
52
|
const [isLong] = useState(() => typeof value === 'string' && (value.length > LONG_TEXT || value.includes('\n')));
|
|
53
|
+
const [isImage] = useState(() => typeof value === 'string' && Object.hasOwn(images, value));
|
|
54
|
+
if (isImage && typeof value === 'string')
|
|
55
|
+
return _jsx(ImageField, { value: value, label: label, isLabelHidden: isLabelHidden });
|
|
26
56
|
if (typeof value === 'boolean')
|
|
27
57
|
return _jsx(Switch, { label: label, value: value, onChange: onChange });
|
|
28
58
|
if (typeof value === 'number') {
|
|
29
|
-
return _jsx(NumberInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: value, isWheelEnabled: false, onChange: onChange });
|
|
59
|
+
return (_jsx(NumberInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: value, isWheelEnabled: false, onChange: onChange }));
|
|
30
60
|
}
|
|
31
61
|
if (value === null)
|
|
32
|
-
return _jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: "", placeholder: "null", isDisabled: true, onChange: () => { } });
|
|
62
|
+
return (_jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: "", placeholder: "null", isDisabled: true, onChange: () => { } }));
|
|
33
63
|
const text = String(value);
|
|
34
|
-
return isLong ? (_jsx(TextArea, { label: label, isLabelHidden: isLabelHidden, size: "sm", rows: 2, value: text, onChange: onChange })) : (_jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: text, onChange: onChange }));
|
|
64
|
+
return isLong ? (_jsx(TextArea, { label: label, isLabelHidden: isLabelHidden, size: "sm", rows: textRows(text, 2, 12), value: text, onChange: onChange })) : (_jsx(TextInput, { label: label, isLabelHidden: isLabelHidden, size: "sm", value: text, onChange: onChange }));
|
|
35
65
|
}
|
|
36
66
|
/** Several fields under one name, in the same label a single field has. */
|
|
37
|
-
export function Labelled({ label, isRequired, description, children }) {
|
|
67
|
+
export function Labelled({ label, isRequired, description, children, }) {
|
|
38
68
|
const id = useId();
|
|
39
69
|
return (_jsxs(VStack, { gap: 2, role: "group", "aria-labelledby": id, children: [_jsx(FieldLabel, { label: label, inputID: "", labelID: id, isGroupLabel: true, isRequired: isRequired, description: description }), children] }));
|
|
40
70
|
}
|
|
@@ -50,7 +80,9 @@ export function AddRow({ field, onAdd }) {
|
|
|
50
80
|
/** Removes a list's row, named by its place. */
|
|
51
81
|
export function RemoveRow({ index, onRemove }) {
|
|
52
82
|
const t = useLabels();
|
|
53
|
-
return (_jsx(IconButton, { label: t('@theorem.decision.remove_item', {
|
|
83
|
+
return (_jsx(IconButton, { label: t('@theorem.decision.remove_item', {
|
|
84
|
+
item: t('@theorem.data.item', { index: String(index + 1) }),
|
|
85
|
+
}), size: "sm", variant: "ghost", icon: _jsx(IconX, { size: 14 }), onClick: onRemove }));
|
|
54
86
|
}
|
|
55
87
|
function ObjectFields({ value, onChange }) {
|
|
56
88
|
return (_jsx(VStack, { gap: 3, children: Object.entries(value).map(([key, entry]) => (_jsx(Field, { label: humanize(key), value: entry, onChange: (next) => onChange({ ...value, [key]: next }) }, key))) }));
|
|
@@ -58,22 +90,33 @@ function ObjectFields({ value, onChange }) {
|
|
|
58
90
|
/** A list of words as tokens; any other list as rows, each removable, with one more shaped like the last. */
|
|
59
91
|
function ListField({ value, onChange, label }) {
|
|
60
92
|
const t = useLabels();
|
|
93
|
+
const keyed = useKeyedRows(value);
|
|
61
94
|
if (isWordList(value)) {
|
|
62
95
|
return (_jsx(Tokenizer, { label: label, size: "sm", value: value.map((word, index) => ({ id: `${String(index)}:${word}`, label: word })), searchSource: NO_SUGGESTIONS, hasCreate: true, debounceMs: 0, placeholder: t('@theorem.decision.add_value'), onChange: (items) => onChange(items.map((item) => item.label)) }));
|
|
63
96
|
}
|
|
64
97
|
const columns = tableColumns(value);
|
|
65
98
|
const add = _jsx(AddRow, { field: label, onAdd: () => onChange([...value, blankLike(value.at(-1))]) });
|
|
66
|
-
const remove = (index) => _jsx(RemoveRow, { index: index, onRemove: () =>
|
|
99
|
+
const remove = (index) => (_jsx(RemoveRow, { index: index, onRemove: () => {
|
|
100
|
+
keyed.drop(index);
|
|
101
|
+
onChange(value.filter((_, at) => at !== index));
|
|
102
|
+
} }));
|
|
67
103
|
if (columns) {
|
|
68
|
-
|
|
69
|
-
|
|
104
|
+
const grid = {
|
|
105
|
+
display: 'grid',
|
|
106
|
+
gap: 8,
|
|
107
|
+
alignItems: 'start',
|
|
108
|
+
gridTemplateColumns: `${columns.map((column) => `minmax(0, ${String(column.weight)}fr)`).join(' ')} auto`,
|
|
109
|
+
};
|
|
70
110
|
const rows = value;
|
|
71
|
-
return (_jsxs(Labelled, { label: label, children: [_jsxs("div", { style: grid, "aria-hidden": true, children: [columns.map((column) => (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: humanize(column.key) }, column.key))), _jsx("span", { style: { width: 28 } })] }), rows.map((
|
|
111
|
+
return (_jsxs(Labelled, { label: label, children: [_jsxs("div", { style: grid, "aria-hidden": true, children: [columns.map((column) => (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 1, children: humanize(column.key) }, column.key))), _jsx("span", { style: { width: 28 } })] }), keyed.rows.map(({ index, key }) => {
|
|
112
|
+
const row = rows[index];
|
|
113
|
+
return (_jsxs("div", { style: grid, children: [columns.map((column) => (_jsx(PlainField, { label: `${humanize(column.key)} ${String(index + 1)}`, isLabelHidden: true, value: row[column.key] ?? null, onChange: (next) => onChange(rows.map((old, at) => (at === index ? { ...old, [column.key]: next } : old))) }, column.key))), remove(index)] }, key));
|
|
114
|
+
}), add] }));
|
|
72
115
|
}
|
|
73
|
-
return (_jsx(Group, { label: label, children: _jsxs(VStack, { gap: 3, children: [
|
|
116
|
+
return (_jsx(Group, { label: label, children: _jsxs(VStack, { gap: 3, children: [keyed.rows.map(({ item: entry, index, key }) => {
|
|
74
117
|
const item = t('@theorem.data.item', { index: String(index + 1) });
|
|
75
118
|
const set = (next) => onChange(value.map((old, at) => (at === index ? next : old)));
|
|
76
|
-
return (_jsxs(HStack, { gap: 2, vAlign: "start", children: [_jsx(StackItem, { size: "fill", children: isPlain(entry) ? _jsx(PlainField, { label: item, isLabelHidden: true, value: entry, onChange: set }) : _jsx(Field, { label: item, value: entry, onChange: set }) }), remove(index)] },
|
|
119
|
+
return (_jsxs(HStack, { gap: 2, vAlign: "start", children: [_jsx(StackItem, { size: "fill", children: isPlain(entry) ? (_jsx(PlainField, { label: item, isLabelHidden: true, value: entry, onChange: set })) : (_jsx(Field, { label: item, value: entry, onChange: set })) }), remove(index)] }, key));
|
|
77
120
|
}), add] }) }));
|
|
78
121
|
}
|
|
79
122
|
function Field({ value, onChange, label }) {
|
package/ui/TheoremChat.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { DefinedTheme } from '@astryxdesign/core/theme';
|
|
2
2
|
import { type ReactNode, type Ref } from 'react';
|
|
3
|
-
import
|
|
4
|
-
import { type
|
|
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';
|
|
5
6
|
import type { TheoremLabels } from './labels.ts';
|
|
6
7
|
export type TheoremChatProps = {
|
|
7
8
|
/** Where `createTheoremHandler` is mounted. Default `/api/theorem`. Ignored when `transport` is set. */
|
|
8
9
|
endpoint?: string;
|
|
9
10
|
/** Extra fetch options for the default HTTP transport (auth headers, custom fetch). */
|
|
10
11
|
http?: Omit<HttpTransportOptions, 'endpoint'>;
|
|
11
|
-
/** Bring your own transport (tests,
|
|
12
|
+
/** Bring your own transport (tests, studios, non-HTTP hosts). */
|
|
12
13
|
transport?: TheoremTransport;
|
|
13
14
|
/** Astryx theme. Omit to inherit the host's `<Theme>` or use `theoremTheme`. */
|
|
14
15
|
theme?: DefinedTheme;
|
|
@@ -19,6 +20,32 @@ export type TheoremChatProps = {
|
|
|
19
20
|
* the host's Astryx `InternationalizationProvider` locale (default `en`).
|
|
20
21
|
*/
|
|
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'];
|
|
22
49
|
placeholder?: string;
|
|
23
50
|
/** Shown above the centred composer before the first message. Default: the agent's handle and a prompt. */
|
|
24
51
|
emptyState?: ReactNode;
|
|
@@ -31,30 +58,54 @@ export type TheoremChatProps = {
|
|
|
31
58
|
/** Page scroller when the chat is not its own scroll container (e.g. `document.documentElement`). */
|
|
32
59
|
scrollRef?: React.RefObject<HTMLElement | null>;
|
|
33
60
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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.
|
|
37
65
|
*/
|
|
38
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;
|
|
39
72
|
className?: string;
|
|
40
73
|
style?: React.CSSProperties;
|
|
41
|
-
/** A conversation to resume, as `onChatChange` reported it. Read once, when the chat mounts. */
|
|
42
|
-
initialChat?: ChatSnapshot;
|
|
43
74
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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.
|
|
46
83
|
*/
|
|
47
|
-
|
|
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;
|
|
48
92
|
/** Lets the host send a message as the composer would; see {@link TheoremChatHandle}. */
|
|
49
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;
|
|
50
99
|
};
|
|
51
100
|
/** What a host can do to a mounted {@link TheoremChat}. */
|
|
52
101
|
export type TheoremChatHandle = {
|
|
53
102
|
/**
|
|
54
|
-
* Sends `text` as the user, and resolves with the blocks the turn added once the reply is done;
|
|
103
|
+
* Sends `text` and optional `files` as the user, and resolves with the blocks the turn added once the reply is done;
|
|
55
104
|
* `null` when the chat can't take a message now (a reply is streaming, or waits on a gate).
|
|
56
105
|
*/
|
|
57
|
-
send: (text: string) => Promise<SentTurn | null>;
|
|
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;
|
|
58
109
|
};
|
|
59
110
|
/**
|
|
60
111
|
* 50% of the chat's width, but never narrower than 640px (or the full width,
|
|
@@ -68,4 +119,4 @@ export declare const DEFAULT_CHAT_MAX_WIDTH = "max(50%, min(100%, 640px))";
|
|
|
68
119
|
* <TheoremChat endpoint="/api/theorem" />
|
|
69
120
|
* ```
|
|
70
121
|
*/
|
|
71
|
-
export declare function TheoremChat({ endpoint, http, transport, theme, mode, labels, ...rest }: TheoremChatProps): import("react").JSX.Element;
|
|
122
|
+
export declare function TheoremChat({ endpoint, http, transport, theme, mode, labels, detectCodeLanguage, ...rest }: TheoremChatProps): import("react").JSX.Element;
|
package/ui/TheoremChat.js
CHANGED
|
@@ -3,69 +3,32 @@ import { Banner } from '@astryxdesign/core/Banner';
|
|
|
3
3
|
import { Center } from '@astryxdesign/core/Center';
|
|
4
4
|
import { ChatLayout } from '@astryxdesign/core/Chat';
|
|
5
5
|
import { Layout, LayoutContent } from '@astryxdesign/core/Layout';
|
|
6
|
-
import { Spinner } from '@astryxdesign/core/Spinner';
|
|
7
6
|
import { Text } from '@astryxdesign/core/Text';
|
|
8
7
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
9
|
-
import { useImperativeHandle, useLayoutEffect, useMemo, useRef, } from 'react';
|
|
10
8
|
import { moveComposerPendingWithinKind, removeComposerPendingMessage, } from '@theoremjs/agents/interface';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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
13
|
import { useTheoremInterface } from "../hooks/use-theorem-interface.js";
|
|
14
14
|
import { ChatComposerBar } from "./ChatComposerBar.js";
|
|
15
|
-
import { parseAspectRatio } from "../client/image-output.js";
|
|
16
15
|
import { ChatTranscript } from "./ChatTranscript.js";
|
|
16
|
+
import { useGlide } from "./glide.js";
|
|
17
17
|
import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
|
|
18
|
-
import {
|
|
18
|
+
import { MarkdownPluginsProvider } from "./markdown-plugins.js";
|
|
19
|
+
import { PaneFailure, PaneLoading } from "./PaneState.js";
|
|
19
20
|
import { SidePanelHeader } from "./SidePanel.js";
|
|
20
21
|
import { useTraceInspector, WithTrace } from "./TraceInspectorPanel.js";
|
|
22
|
+
import { TheoremThemeProvider } from "./theme.js";
|
|
21
23
|
/**
|
|
22
24
|
* 50% of the chat's width, but never narrower than 640px (or the full width,
|
|
23
25
|
* whichever is smaller) — so phones and narrow panes get 100%.
|
|
24
26
|
*/
|
|
25
27
|
export const DEFAULT_CHAT_MAX_WIDTH = 'max(50%, min(100%, 640px))';
|
|
26
28
|
/** Centres content in a column no wider than `maxWidth`, using Astryx layout props. */
|
|
27
|
-
function ChatColumn({ maxWidth, ref, children }) {
|
|
29
|
+
function ChatColumn({ maxWidth, ref, children, }) {
|
|
28
30
|
return (_jsx(Center, { axis: "horizontal", width: "100%", children: _jsx(VStack, { ref: ref, width: "100%", maxWidth: maxWidth, children: children }) }));
|
|
29
31
|
}
|
|
30
|
-
/** Reads an Astryx duration token ("410ms" / "0.4s") as milliseconds. */
|
|
31
|
-
function tokenMs(value, fallback) {
|
|
32
|
-
const n = Number.parseFloat(value);
|
|
33
|
-
if (Number.isNaN(n))
|
|
34
|
-
return fallback;
|
|
35
|
-
return value.trim().endsWith('ms') ? n : n * 1_000;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The composer is centred until the first message, then lives in ChatLayout's
|
|
39
|
-
* dock. Astryx has no layout-transition helper, so this eases the jump (FLIP)
|
|
40
|
-
* with Astryx's `--duration-medium` / `--ease-standard` tokens and keeps focus.
|
|
41
|
-
*/
|
|
42
|
-
function useComposerGlide(landing, inputRef) {
|
|
43
|
-
const columnRef = useRef(null);
|
|
44
|
-
const landingTop = useRef(null);
|
|
45
|
-
useLayoutEffect(() => {
|
|
46
|
-
const el = columnRef.current;
|
|
47
|
-
if (!el)
|
|
48
|
-
return;
|
|
49
|
-
if (landing) {
|
|
50
|
-
landingTop.current = el.getBoundingClientRect().top;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const from = landingTop.current;
|
|
54
|
-
if (from === null)
|
|
55
|
-
return;
|
|
56
|
-
landingTop.current = null;
|
|
57
|
-
inputRef.current?.focus();
|
|
58
|
-
const delta = from - el.getBoundingClientRect().top;
|
|
59
|
-
if (delta === 0 || globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches)
|
|
60
|
-
return;
|
|
61
|
-
const tokens = getComputedStyle(el);
|
|
62
|
-
el.animate([{ transform: `translateY(${String(delta)}px)` }, { transform: 'none' }], {
|
|
63
|
-
duration: tokenMs(tokens.getPropertyValue('--duration-medium'), 410),
|
|
64
|
-
easing: tokens.getPropertyValue('--ease-standard').trim() || 'ease-out',
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
return columnRef;
|
|
68
|
-
}
|
|
69
32
|
/** The composer bar wired to a `useTheoremChat` model. */
|
|
70
33
|
function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
|
|
71
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) => {
|
|
@@ -78,7 +41,7 @@ function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
|
|
|
78
41
|
chat.setIssues([]);
|
|
79
42
|
}, onVoiceClear: () => chat.setPendingVoice([]), onSubmit: () => {
|
|
80
43
|
void chat.handleSubmit();
|
|
81
|
-
}, onStop: chat.handleStop, onMenuAction: chat.handleMenuAction, onGenerationChange: chat.handleGenerationChange, onPendingMove: (id, direction) => {
|
|
44
|
+
}, onStop: chat.handleStop, onMenuAction: chat.handleMenuAction, onGenerationChange: chat.handleGenerationChange, slotChoices: chat.slotChoices, onPendingMove: (id, direction) => {
|
|
82
45
|
chat.setPendingMessages((prev) => moveComposerPendingWithinKind(prev, id, direction));
|
|
83
46
|
}, onPendingRemove: (id) => {
|
|
84
47
|
chat.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
|
|
@@ -90,44 +53,69 @@ function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
|
|
|
90
53
|
void chat.handleSendNow(message);
|
|
91
54
|
} }));
|
|
92
55
|
}
|
|
93
|
-
function ChatBody({ transport, iface, placeholder, emptyState, density, maxWidth = DEFAULT_CHAT_MAX_WIDTH, scrollRef, trace, className, style,
|
|
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, }) {
|
|
94
57
|
const t = useLabels();
|
|
95
|
-
const chat = useTheoremChat({
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
]);
|
|
98
76
|
const blocks = useMemo(() => [...chat.blocks, ...chat.streamBlocks], [chat.blocks, chat.streamBlocks]);
|
|
99
77
|
const handle = t('@theorem.agent.handle', { handle: iface.identity.handle });
|
|
100
78
|
const landing = blocks.length === 0;
|
|
101
79
|
const inputRef = useRef(null);
|
|
102
|
-
const composerRef =
|
|
80
|
+
const composerRef = useGlide(landing, () => inputRef.current?.focus());
|
|
103
81
|
const inspector = useTraceInspector(iface, transport.traces, trace);
|
|
104
|
-
const header = _jsx(SidePanelHeader, { children: inspector.toggle });
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
} }) }) }) }));
|
|
120
109
|
}
|
|
121
110
|
function ChatForTransport(props) {
|
|
122
111
|
const t = useLabels();
|
|
123
112
|
const described = useTheoremInterface(props.transport);
|
|
124
113
|
if (described.status === 'loading')
|
|
125
|
-
return _jsx(
|
|
126
|
-
if (described.status === 'error')
|
|
127
|
-
return _jsx(
|
|
128
|
-
}
|
|
114
|
+
return _jsx(PaneLoading, { label: t('@theorem.chat.loading') });
|
|
115
|
+
if (described.status === 'error')
|
|
116
|
+
return _jsx(PaneFailure, { title: described.failure.error });
|
|
129
117
|
if (described.iface.type === 'live') {
|
|
130
|
-
return (_jsx(
|
|
118
|
+
return (_jsx(PaneFailure, { title: t('@theorem.chat.live_unsupported.title'), description: t('@theorem.chat.live_unsupported.description') }));
|
|
131
119
|
}
|
|
132
120
|
return _jsx(ChatBody, { ...props, transport: props.transport, iface: described.iface });
|
|
133
121
|
}
|
|
@@ -138,9 +126,9 @@ function ChatForTransport(props) {
|
|
|
138
126
|
* <TheoremChat endpoint="/api/theorem" />
|
|
139
127
|
* ```
|
|
140
128
|
*/
|
|
141
|
-
export function TheoremChat({ endpoint, http, transport, theme, mode, labels, ...rest }) {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
[transport, endpoint]);
|
|
145
|
-
return (_jsx(TheoremThemeProvider, { theme: theme, mode: mode, children: _jsx(TheoremLabelsProvider, { labels: labels, children: _jsx(ChatForTransport, { ...rest, transport: resolved }) }) }));
|
|
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 }) }) }) }));
|
|
146
134
|
}
|
package/ui/TheoremDecision.d.ts
CHANGED
|
@@ -1,31 +1,23 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type CSSProperties } from 'react';
|
|
1
|
+
import { type DecisionImages } from '../client/decision-images.ts';
|
|
3
2
|
import { type DecisionTransport } from '../client/decision-transport.ts';
|
|
4
|
-
import type
|
|
5
|
-
import type
|
|
6
|
-
export type TheoremDecisionProps = {
|
|
3
|
+
import { type ConsolePageProps } from './ConsoleFrame.tsx';
|
|
4
|
+
import { type TheoremDecisionAnswersProps } from './DecisionAnswers.tsx';
|
|
5
|
+
export type TheoremDecisionProps = ConsolePageProps & {
|
|
7
6
|
/** Where `createTheoremDecisionHandler` is mounted. Default `/api/decision`. Ignored when `transport` is set. */
|
|
8
7
|
endpoint?: string;
|
|
9
|
-
/**
|
|
10
|
-
http?: HttpOptions;
|
|
11
|
-
/** Bring your own transport (tests, playgrounds, non-HTTP hosts). */
|
|
8
|
+
/** Bring your own transport (tests, studios, non-HTTP hosts). */
|
|
12
9
|
transport?: DecisionTransport;
|
|
13
10
|
/** The JSON the state field starts with. Default `{}`. */
|
|
14
11
|
defaultState?: string;
|
|
15
|
-
theme?: DefinedTheme;
|
|
16
|
-
mode?: 'system' | 'light' | 'dark';
|
|
17
|
-
/** Replacement lines by locale, for any `@theorem.*` or `@astryx.*` key. */
|
|
18
|
-
labels?: TheoremLabels;
|
|
19
|
-
/** Widest the column may grow, as a CSS length. Default `960px`. */
|
|
20
|
-
maxWidth?: string;
|
|
21
12
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* that records traces.
|
|
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.
|
|
25
15
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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'];
|
|
29
21
|
};
|
|
30
22
|
/**
|
|
31
23
|
* Drop-in surface for a decision profile served by `createTheoremDecisionHandler`:
|