@theoremjs/react 0.3.4-next.755.g88665bd → 0.3.4-next.759.g46e0e27

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 CHANGED
@@ -365,11 +365,11 @@ Three more props give the call values from the page. The profile declares each o
365
365
 
366
366
  | Prop | The profile declares | What the page gives |
367
367
  | --- | --- | --- |
368
- | `slots` | `inputs.slots` | The value chosen for each slot. It is read when the call starts. A value that changes during a call takes the call up again with it when the profile has resumption on; otherwise the next call has it. |
368
+ | `options` | `inputs.options` | The value chosen for each option. It is read when the call starts. A value that changes during a call takes the call up again with it when the profile has resumption on; otherwise the next call has it. |
369
369
  | `context` | `inputs.context` with `from: ['client']` | Any JSON the agent should know, such as the page the person is on. |
370
370
  | `pageTools` | A function tool with `answeredBy: 'page'` | A function for each page tool, by name. It returns `{ output }`. |
371
371
 
372
- `stated` is a fourth prop, for a page you trust as yourself. It says the `path` and `conditions` that choose the call's prompt lines, and the server reads it only with `pageStatesTurn` set. A change during a call takes the call up again, as a slot does. `TheoremChat` takes the same prop, on each turn.
372
+ `stated` is a fourth prop, for a page you trust as yourself. It says the `path` and the `options` from `identity.options` that choose the call's prompt lines, and the server reads it only with `pageStatesTurn` set. A change during a call takes the call up again, as an option does. `TheoremChat` takes the same prop, on each turn.
373
373
 
374
374
  `model` and `effort` pick the call's model when the profile sets `allowModelSelect`. They are read when the call starts.
375
375
 
@@ -377,7 +377,7 @@ Three more props give the call values from the page. The profile declares each o
377
377
  <LiveRunner
378
378
  iface={iface}
379
379
  connection={() => ({ profile: 'support.voice' })}
380
- slots={{ language: 'fr' }}
380
+ options={{ language: 'fr' }}
381
381
  context={{ page: '/pricing', cart }}
382
382
  pageTools={{
383
383
  highlight: (args) => ({ output: { success: highlight(String(args.target)) } }),
@@ -417,7 +417,7 @@ A failed call does not reconnect. Without `live.sessionResumption`, a provider-e
417
417
 
418
418
  The relay is server code. To authenticate the conversation and resume a call in your relay, see [Your relay](https://github.com/masudl-hub/theoremai/blob/main/web/README.md#your-relay).
419
419
 
420
- To build your own call view, use `useLiveRunnerModel(iface, connection, { slots, context, pageTools })` from `@theoremjs/react/ui`. It returns the state of the call, its captions, and its controls. `LiveCaptionsPanel` draws the captions.
420
+ To build your own call view, use `useLiveRunnerModel(iface, connection, { options, context, pageTools })` from `@theoremjs/react/ui`. It returns the state of the call, its captions, and its controls. `LiveCaptionsPanel` draws the captions.
421
421
 
422
422
  ### A custom browser transport
423
423
 
@@ -5,7 +5,7 @@ import { type LiveCaptionState, type LiveCaptionTurn } from '../../client/live/l
5
5
  * Owns LiveRunner state, session client, and stage callbacks.
6
6
  * `connection` resolves what the call opens: a host with a fixed profile returns
7
7
  * its id; the studio returns its draft as the relay's open message. `options`
8
- * are the host's part in the call: its slots, its stated turn, the page's context, and its page tools.
8
+ * are the host's part in the call: its options, its stated turn, the page's context, and its page tools.
9
9
  */
10
10
  export declare function useLiveRunnerModel(iface: LiveProfileInterface, connection: () => LiveConnection | Promise<LiveConnection>, options?: LiveCallOptions): {
11
11
  handle: string;
@@ -53,7 +53,7 @@ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailab
53
53
  * Owns LiveRunner state, session client, and stage callbacks.
54
54
  * `connection` resolves what the call opens: a host with a fixed profile returns
55
55
  * its id; the studio returns its draft as the relay's open message. `options`
56
- * are the host's part in the call: its slots, its stated turn, the page's context, and its page tools.
56
+ * are the host's part in the call: its options, its stated turn, the page's context, and its page tools.
57
57
  */
58
58
  export function useLiveRunnerModel(iface, connection, options = {}) {
59
59
  const ui = useLiveRunnerUiState(iface.labels);
@@ -77,7 +77,7 @@ export function useLiveRunnerModel(iface, connection, options = {}) {
77
77
  // why: One feed per runner: every session it opens adds its records.
78
78
  const traces = useMemo(createTraceFeed, []);
79
79
  const { clientRef, ensureClient, clearClient } = useLiveSessionClient({
80
- slots: options.slots,
80
+ options: options.options,
81
81
  stated: options.stated,
82
82
  model: options.model,
83
83
  effort: options.effort,
@@ -146,12 +146,12 @@ export function useLiveRunnerModel(iface, connection, options = {}) {
146
146
  useEffect(() => {
147
147
  clientRef.current?.setContext(contextJson === undefined ? undefined : JSON.parse(contextJson));
148
148
  }, [clientRef, contextJson]);
149
- // why: Slots are compared by value too; a changed value takes an open call up again with it.
150
- const slotsJson = JSON.stringify(options.slots);
149
+ // why: Options are compared by value too; a changed value takes an open call up again with it.
150
+ const optionsJson = JSON.stringify(options.options);
151
151
  useEffect(() => {
152
- clientRef.current?.setSlots(slotsJson === undefined ? undefined : JSON.parse(slotsJson));
153
- }, [clientRef, slotsJson]);
154
- // why: As slots: a changed pick takes an open call up again with the lines it chooses.
152
+ clientRef.current?.setOptions(optionsJson === undefined ? undefined : JSON.parse(optionsJson));
153
+ }, [clientRef, optionsJson]);
154
+ // why: As options: a changed pick takes an open call up again with the lines it chooses.
155
155
  const statedJson = JSON.stringify(options.stated);
156
156
  useEffect(() => {
157
157
  clientRef.current?.setStated(statedJson === undefined ? undefined : JSON.parse(statedJson));
@@ -52,7 +52,7 @@ export function useLiveSessionClient(bindings) {
52
52
  const b = bindingsRef.current;
53
53
  const client = new LiveSessionClient({
54
54
  ...connection,
55
- slots: b.slots,
55
+ options: b.options,
56
56
  stated: b.stated,
57
57
  model: b.model,
58
58
  effort: b.effort,
@@ -8,9 +8,9 @@ export type UseTheoremChatOptions = ChatOptions;
8
8
  * `@theoremjs/web/client`, which works with no framework.
9
9
  */
10
10
  export declare function useTheoremChat(options: UseTheoremChatOptions): {
11
- slotChoices: {
11
+ optionChoices: {
12
12
  onChange: (name: string, value: string) => void;
13
- declared: Record<string, import("@theoremjs/web/client").ChatSlotDecl>;
13
+ declared: import("../../../mod").ProfileOptions;
14
14
  values: Record<string, string>;
15
15
  } | undefined;
16
16
  setDraftText: (value: import("@theoremjs/web/client").ChatUpdate<string>) => void;
@@ -50,5 +50,5 @@ export declare function useTheoremChat(options: UseTheoremChatOptions): {
50
50
  session: import("../../../src/interface/session").InterfaceTurnSession;
51
51
  delivery: import("@theoremjs/web/client").MessageDelivery | null;
52
52
  streaming: boolean;
53
- slots: import("@theoremjs/web/client").ChatSlots | undefined;
53
+ options: import("@theoremjs/web/client").ChatOptionPicks | undefined;
54
54
  };
@@ -12,13 +12,13 @@ export function useTheoremChat(options) {
12
12
  const [chat] = useState(() => createChatController(options));
13
13
  useOptionsEffect(() => chat.update(options));
14
14
  const state = useSyncExternalStore(chat.subscribe, chat.getState, chat.getState);
15
- const slotChoices = useMemo(() => (state.slots ? { ...state.slots, onChange: chat.pickSlot } : undefined), [chat, state.slots]);
15
+ const optionChoices = useMemo(() => (state.options ? { ...state.options, onChange: chat.pickOption } : undefined), [chat, state.options]);
16
16
  // why: The transcript draws each gate by its index; the chat answers the one gate that waits.
17
17
  const handleAuthenticated = useCallback((_index, secret) => chat.authenticate(secret), [chat]);
18
18
  const handleToolDecision = useCallback((_index, action) => chat.decide(action), [chat]);
19
19
  return {
20
20
  ...state,
21
- slotChoices,
21
+ optionChoices,
22
22
  setDraftText: chat.setDraftText,
23
23
  setPendingFiles: chat.setPendingFiles,
24
24
  setPendingVoice: chat.setPendingVoice,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/react",
3
- "version": "0.3.4-next.755.g88665bd",
3
+ "version": "0.3.4-next.759.g46e0e27",
4
4
  "description": "React bindings for Theorem: headless hooks + transport, an Astryx chat UI, and a Web-standard server handler.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -60,8 +60,8 @@
60
60
  "@stylexjs/stylex": "0.19.0",
61
61
  "react": "^19.0.0",
62
62
  "react-dom": "^19.0.0",
63
- "@theoremjs/agents": "^0.3.4-next.755.g88665bd",
64
- "@theoremjs/web": "^0.3.4-next.755.g88665bd"
63
+ "@theoremjs/agents": "^0.3.4-next.759.g46e0e27",
64
+ "@theoremjs/web": "^0.3.4-next.759.g46e0e27"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@astryxdesign/core": {
@@ -2,19 +2,19 @@ import { type ChatComposerInputHandle } from '@astryxdesign/core/Chat';
2
2
  import { type AttachmentValidationIssue, type ComposerMenuAction, type ComposerPendingMessage, type ComposerProfileInterface, type ComposerRunPhase, type LiveProfileInterface } from '@theoremjs/agents/interface';
3
3
  import type { ClientFailure } from '@theoremjs/web/client';
4
4
  import { type ReactNode } from 'react';
5
- /** A slot the profile declares: its allowed values, the one chosen, and how to choose another. */
5
+ /** An option the profile declares: its allowed values, the one chosen, and how to choose another. */
6
6
  /** The effort selector's icon. The studio's Agents list shows the same one. */
7
7
  export declare const EFFORT_ICON: import("react").ForwardRefExoticComponent<import("@tabler/icons-react").IconProps & import("react").RefAttributes<SVGSVGElement>>;
8
- export type SlotChoiceOption = {
8
+ export type OptionChoiceView = {
9
9
  value: string;
10
10
  description?: string;
11
11
  };
12
- export type SlotDeclView = {
12
+ export type OptionDeclView = {
13
13
  label?: string;
14
- choices: readonly SlotChoiceOption[];
14
+ choices: readonly OptionChoiceView[];
15
15
  };
16
- export type SlotChoices = {
17
- declared: Readonly<Record<string, SlotDeclView>>;
16
+ export type OptionChoices = {
17
+ declared: Readonly<Record<string, OptionDeclView>>;
18
18
  values: Readonly<Record<string, string>>;
19
19
  onChange: (name: string, value: string) => void;
20
20
  };
@@ -47,8 +47,8 @@ export type ChatComposerBarProps = {
47
47
  }) => void;
48
48
  /** Stands in for the model and effort selectors, for a bar whose choices are not one chat's. */
49
49
  generationSelect?: ReactNode;
50
- /** The profile's `inputs.slots` with the value chosen for each; one selector per slot, beside the model's. */
51
- slotChoices?: SlotChoices;
50
+ /** The profile's `inputs.options` with the value chosen for each; one selector per option, beside the model's. */
51
+ optionChoices?: OptionChoices;
52
52
  onPendingMove: (id: string, direction: 'up' | 'down') => void;
53
53
  onPendingRemove: (id: string) => void;
54
54
  onPendingQueue: (id: string) => void;
@@ -24,7 +24,7 @@ import { composerDrawerLabel } from "./labels.js";
24
24
  import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
25
25
  import { VoiceNote } from "./VoiceNote.js";
26
26
  const NO_FOCUS_RING = { '--focus-outline-width': '0px' };
27
- /** A slot the profile declares: its allowed values, the one chosen, and how to choose another. */
27
+ /** An option the profile declares: its allowed values, the one chosen, and how to choose another. */
28
28
  /** The effort selector's icon. The studio's Agents list shows the same one. */
29
29
  export const EFFORT_ICON = IconBrain;
30
30
  const isImage = (file) => file.type.startsWith('image/');
@@ -89,7 +89,7 @@ function EffortSelector(props) {
89
89
  return null;
90
90
  return (_jsx(Selector, { label: props.label, isLabelHidden: true, size: "sm", variant: "ghost", startIcon: _jsx(EFFORT_ICON, { size: 14 }), placement: "above", isDisabled: props.isDisabled, value: props.value ?? '', options: props.efforts.map((e) => ({ value: e.alias, label: e.alias, description: e.level })), onChange: props.onChange }));
91
91
  }
92
- function SlotSelectors(props) {
92
+ function OptionSelectors(props) {
93
93
  if (!props.choices)
94
94
  return null;
95
95
  const { declared, values, onChange } = props.choices;
@@ -108,15 +108,15 @@ function SlotSelectors(props) {
108
108
  }
109
109
  function GenerationSelect(props) {
110
110
  const { models, efforts } = generationOptions(props.iface, props.selectedModel);
111
- const slotCount = Object.keys(props.slotChoices?.declared ?? {}).length;
111
+ const optionCount = Object.keys(props.optionChoices?.declared ?? {}).length;
112
112
  if (props.control)
113
- return (_jsxs(HStack, { gap: 1, children: [props.control, _jsx(SlotSelectors, { choices: props.slotChoices, isDisabled: props.isDisabled })] }));
114
- if (models.length + efforts.length + slotCount === 0)
113
+ return (_jsxs(HStack, { gap: 1, children: [props.control, _jsx(OptionSelectors, { choices: props.optionChoices, isDisabled: props.isDisabled })] }));
114
+ if (models.length + efforts.length + optionCount === 0)
115
115
  return null;
116
116
  return (_jsxs(HStack, { gap: 1, children: [_jsx(ModelSelector, { label: labelText('composer.model', {}, props.iface.labels), models: models, value: props.selectedModel, isDisabled: props.isDisabled, onChange: (modelId) => props.onChange?.({ modelId }) }), _jsx(EffortSelector, { label: labelText('composer.effort', {}, props.iface.labels), efforts: efforts, value: props.selectedEffort, isDisabled: props.isDisabled, onChange: (effort) => {
117
117
  if (props.selectedModel)
118
118
  props.onChange?.({ modelId: props.selectedModel, effort });
119
- } }), _jsx(SlotSelectors, { choices: props.slotChoices, isDisabled: props.isDisabled })] }));
119
+ } }), _jsx(OptionSelectors, { choices: props.optionChoices, isDisabled: props.isDisabled })] }));
120
120
  }
121
121
  function composerStatus(args) {
122
122
  if (args.failure)
@@ -330,7 +330,7 @@ function ChatComposerBarBody(props) {
330
330
  : (props.placeholder ??
331
331
  t('@theorem.composer.placeholder', { handle: iface.identity.handle })),
332
332
  // why: A profile that takes no text has no text field (false, not null: null gets Astryx's default one).
333
- input: inputs.text && (_jsx(ComposerTextInput, { editorRef: editorRef, handleRef: props.inputRef, onStash: canStash ? () => props.onMenuAction('stash') : undefined })), drawer: composerDrawer(props, stagedFiles, () => voice.discardRecordingOrVoice()), headerActions: inputs.attachments ? (_jsx(AttachFilesButton, { accept: inputs.attachments.acceptAttr, onFiles: stageFiles })) : undefined, headerContext: hint ? _jsx(ComposerHint, { hint: hint, onAction: () => props.onMenuAction('stash') }) : undefined, footerActions: _jsx(GenerationSelect, { iface: iface, slotChoices: props.slotChoices, selectedModel: props.selectedModel, selectedEffort: props.selectedEffort, isDisabled: phase === 'streaming', onChange: props.onGenerationChange, control: props.generationSelect }),
333
+ input: inputs.text && (_jsx(ComposerTextInput, { editorRef: editorRef, handleRef: props.inputRef, onStash: canStash ? () => props.onMenuAction('stash') : undefined })), drawer: composerDrawer(props, stagedFiles, () => voice.discardRecordingOrVoice()), headerActions: inputs.attachments ? (_jsx(AttachFilesButton, { accept: inputs.attachments.acceptAttr, onFiles: stageFiles })) : undefined, headerContext: hint ? _jsx(ComposerHint, { hint: hint, onAction: () => props.onMenuAction('stash') }) : undefined, footerActions: _jsx(GenerationSelect, { iface: iface, optionChoices: props.optionChoices, selectedModel: props.selectedModel, selectedEffort: props.selectedEffort, isDisabled: phase === 'streaming', onChange: props.onGenerationChange, control: props.generationSelect }),
334
334
  // why: Astryx: sendActions render to the left of the send button, at size="md".
335
335
  sendActions: _jsxs(_Fragment, { children: [_jsx(SendMenu, { actions: menuActions, onAction: props.onMenuAction }), _jsx(ComposerRecordButton, { hasVoice: Boolean(inputs.voice), voice: voice })] }), sendButton: _jsx(ChatSendButton, { isStopShown: primary === 'stop', isDisabled: primaryDisabled, onSend: runPrimary, onStop: props.onStop }), status: composerStatus({
336
336
  failure: props.failure,
@@ -28,13 +28,13 @@ export type TheoremChatProps = {
28
28
  */
29
29
  detectCodeLanguage?: boolean;
30
30
  /**
31
- * The value chosen for each of the profile's `inputs.slots`, sent with every turn. A slot you
31
+ * The value chosen for each of the profile's `inputs.options`, sent with every turn. An option you
32
32
  * name is fixed to that value; one you leave out gets a selector in the composer bar, beside
33
33
  * the model's, starting at its first allowed value.
34
34
  */
35
- slots?: Record<string, string>;
36
- /** Told each time the visitor chooses a slot in the composer, with every slot's value. */
37
- onSlotsChange?: (slots: Record<string, string>) => void;
35
+ options?: Record<string, string>;
36
+ /** Told each time the visitor chooses an option in the composer, with every option's value. */
37
+ onOptionsChange?: (options: Record<string, string>) => void;
38
38
  /** Which prompt lines each turn gets, for a host that lets its page choose (`pageStatesTurn`). */
39
39
  stated?: StatedTurn;
40
40
  /**
package/ui/TheoremChat.js CHANGED
@@ -41,7 +41,7 @@ function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
41
41
  chat.setIssues([]);
42
42
  }, onVoiceClear: () => chat.setPendingVoice([]), onSubmit: () => {
43
43
  void chat.handleSubmit();
44
- }, onStop: chat.handleStop, onMenuAction: chat.handleMenuAction, onGenerationChange: chat.handleGenerationChange, slotChoices: chat.slotChoices, onPendingMove: chat.handlePendingMove, onPendingRemove: (id) => {
44
+ }, onStop: chat.handleStop, onMenuAction: chat.handleMenuAction, onGenerationChange: chat.handleGenerationChange, optionChoices: chat.optionChoices, onPendingMove: chat.handlePendingMove, onPendingRemove: (id) => {
45
45
  chat.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
46
46
  }, onPendingQueue: chat.handlePendingQueue, onPendingRestore: (id) => {
47
47
  void chat.handlePendingRestore(id);
@@ -51,7 +51,7 @@ function ChatComposerForChat({ chat, iface, placeholder, inputRef, }) {
51
51
  void chat.handleSendNow(message);
52
52
  } }));
53
53
  }
54
- function ChatBody({ transport, iface, placeholder, emptyState, density, maxWidth = DEFAULT_CHAT_MAX_WIDTH, scrollRef, trace, usage, className, style, conversation, conversationView, initialText, initialFiles, initialVoice, initialPending, onConversationChange, chatRef, composer: hasComposer = true, slots, onSlotsChange, stated, context, pageTools, onTurnEvent, }) {
54
+ function ChatBody({ transport, iface, placeholder, emptyState, density, maxWidth = DEFAULT_CHAT_MAX_WIDTH, scrollRef, trace, usage, className, style, conversation, conversationView, initialText, initialFiles, initialVoice, initialPending, onConversationChange, chatRef, composer: hasComposer = true, options, onOptionsChange, stated, context, pageTools, onTurnEvent, }) {
55
55
  const t = useLabels();
56
56
  const chat = useTheoremChat({
57
57
  transport,
@@ -63,8 +63,8 @@ function ChatBody({ transport, iface, placeholder, emptyState, density, maxWidth
63
63
  initialVoice,
64
64
  initialPending,
65
65
  onConversationChange,
66
- slots,
67
- onSlotsChange,
66
+ options,
67
+ onOptionsChange,
68
68
  stated,
69
69
  context,
70
70
  pageTools,