@theoremjs/react 0.3.4-next.847.g6513d7e → 0.3.4-next.849.g2dfd688

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
@@ -367,7 +367,7 @@ The call lasts as long as the component that calls `useLiveCall`. Call it above
367
367
 
368
368
  Give `LiveRunner` an `onMinimize` to show a minimize control in the call. Theorem calls it and does not navigate: your code decides where to go, and the call goes on.
369
369
 
370
- `LiveCallTile` draws the same call small: the camera preview, or the ink waveform when the camera is off, then the call time, mute, end, and expand. It fills the box you give it, so your code places it and sizes it. A press on the tile or its expand control calls `onExpand`.
370
+ `LiveCallTile` draws the same call small: the camera preview, or the ink waveform when the camera is off, then the call time, mute, end, and expand. While a tool runs, its name shows in place of the time, and a tool gate opens the same dialog `LiveRunner` shows, so the person can answer it on any page. It fills the box you give it, so your code places it and sizes it. A press on the tile or its expand control calls `onExpand`.
371
371
 
372
372
  ```tsx
373
373
  import { LiveCallTile } from '@theoremjs/react/live';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theoremjs/react",
3
- "version": "0.3.4-next.847.g6513d7e",
3
+ "version": "0.3.4-next.849.g2dfd688",
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.847.g6513d7e",
64
- "@theoremjs/web": "^0.3.4-next.847.g6513d7e"
63
+ "@theoremjs/agents": "^0.3.4-next.849.g2dfd688",
64
+ "@theoremjs/web": "^0.3.4-next.849.g2dfd688"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@astryxdesign/core": {
@@ -18,7 +18,9 @@ export type LiveCallTileProps = {
18
18
  };
19
19
  /**
20
20
  * A small view of a running call: the camera preview, or the ink waveform
21
- * when the camera is off, then the call time, mute, end, and expand. It fills
22
- * the box the host gives it; the host places it.
21
+ * when the camera is off, then the call time, mute, end, and expand. While a
22
+ * tool runs, its name takes the time's place, and a tool gate opens the same
23
+ * dialog `LiveRunner` shows. It fills the box the host gives it; the host
24
+ * places it.
23
25
  */
24
26
  export declare function LiveCallTile({ theme, mode, labels, ...props }: LiveCallTileProps): import("react").JSX.Element;
@@ -11,33 +11,40 @@ import { VStack } from '@astryxdesign/core/VStack';
11
11
  import { IconArrowsDiagonal, IconMicrophone, IconMicrophoneOff, IconPhoneOff, } from '@tabler/icons-react';
12
12
  import { InkWaveform } from "../components/InkWaveform.js";
13
13
  import { useSecondTicker } from "../hooks/use-second-ticker.js";
14
+ import { LiveToolGateDialog } from "./LiveToolGateDialog.js";
14
15
  import { LiveVideoPreview } from "./LiveVideoPreview.js";
15
16
  import { liveDuration, liveStateLabel } from "./labels.js";
16
17
  import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
17
18
  import { TheoremThemeProvider } from "./theme.js";
18
19
  /**
19
20
  * A small view of a running call: the camera preview, or the ink waveform
20
- * when the camera is off, then the call time, mute, end, and expand. It fills
21
- * the box the host gives it; the host places it.
21
+ * when the camera is off, then the call time, mute, end, and expand. While a
22
+ * tool runs, its name takes the time's place, and a tool gate opens the same
23
+ * dialog `LiveRunner` shows. It fills the box the host gives it; the host
24
+ * places it.
22
25
  */
23
26
  export function LiveCallTile({ theme, mode, labels, ...props }) {
24
27
  return (_jsx(TheoremThemeProvider, { theme: theme, mode: mode, children: _jsx(TheoremLabelsProvider, { labels: labels, children: _jsx(LiveCallTileBody, { ...props }) }) }));
25
28
  }
29
+ /** why: The tile floats on its shadow; Astryx's default card has no borderless variant with its surface. */
30
+ const TILE_BORDERLESS = { borderWidth: 0 };
31
+ /** The bars in the icon colour, as on `LiveRunner`'s stage. */
32
+ const WAVE_COLOR = { color: 'var(--color-icon-primary)' };
26
33
  function LiveCallTileBody({ call, onExpand, }) {
27
34
  const t = useLabels();
28
- return (_jsx(ClickableCard, { label: t('@theorem.live.expand'), onClick: onExpand, padding: 0, elevation: "high", width: "100%", children: _jsxs(VStack, { children: [_jsx(TileMedia, { call: call }), _jsxs(HStack, { gap: 1, vAlign: "center", padding: 2, children: [_jsx(StackItem, { size: "fill", children: _jsx(TileTime, { call: call }) }), _jsx(TileControls, { call: call, onExpand: onExpand })] })] }) }));
35
+ return (_jsxs(_Fragment, { children: [_jsx(ClickableCard, { label: t('@theorem.live.expand'), onClick: onExpand, padding: 0, elevation: "high", width: "100%", style: TILE_BORDERLESS, children: _jsxs(VStack, { children: [_jsx(TileMedia, { call: call }), _jsxs(HStack, { gap: 1, vAlign: "center", padding: 2, children: [_jsx(StackItem, { size: "fill", children: _jsx(TileTime, { call: call }) }), _jsx(TileControls, { call: call, onExpand: onExpand })] })] }) }), _jsx(LiveToolGateDialog, { call: call })] }));
29
36
  }
30
37
  /** The camera preview while the camera is on; otherwise the ink waveform. */
31
38
  function TileMedia({ call }) {
32
39
  if (call.isVideoOn && call.videoPreview) {
33
40
  return _jsx(LiveVideoPreview, { video: call.videoPreview, facingMode: call.videoFacingMode });
34
41
  }
35
- return (_jsx(AspectRatio, { ratio: 4 / 3, children: _jsx(VStack, { height: "100%", vAlign: "center", padding: 4, "aria-hidden": "true", children: _jsx(InkWaveform, { status: call.status, levelsRef: call.levelsRef, toolActive: call.toolActive, variant: "pill" }) }) }));
42
+ return (_jsx(AspectRatio, { ratio: 4 / 3, children: _jsx(VStack, { height: "100%", vAlign: "center", paddingBlock: 4, style: WAVE_COLOR, "aria-hidden": "true", children: _jsx(InkWaveform, { status: call.status, levelsRef: call.levelsRef, toolActive: call.toolActive, variant: "pill" }) }) }));
36
43
  }
37
- /** How long the call has run, or its state before it connects and after it ends. */
44
+ /** How long the call has run, or its state: before it connects, after it ends, and while a tool runs. */
38
45
  function TileTime({ call }) {
39
46
  const t = useLabels();
40
- const since = call.sessionActive ? call.connectedAt : null;
47
+ const since = call.sessionActive && call.liveState !== 'calling_tool' ? call.connectedAt : null;
41
48
  const now = useSecondTicker(since !== null);
42
49
  return (_jsx(Text, { type: "supporting", hasTabularNumbers: true, maxLines: 1, children: since === null
43
50
  ? liveStateLabel(t, call.liveState, call.activeTool)
package/ui/LiveRunner.js CHANGED
@@ -2,7 +2,6 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { Banner } from '@astryxdesign/core/Banner';
3
3
  import { Button } from '@astryxdesign/core/Button';
4
4
  import { Center } from '@astryxdesign/core/Center';
5
- import { Dialog } from '@astryxdesign/core/Dialog';
6
5
  import { HStack } from '@astryxdesign/core/HStack';
7
6
  import { Icon } from '@astryxdesign/core/Icon';
8
7
  import { IconButton } from '@astryxdesign/core/IconButton';
@@ -16,12 +15,12 @@ import { IconArrowsDiagonalMinimize2, IconCameraRotate, IconMessage, IconMicroph
16
15
  import { Fragment, useLayoutEffect, useRef } from 'react';
17
16
  import { InkWaveform } from "../components/InkWaveform.js";
18
17
  import { LiveCaptionsPanel } from "./LiveCaptionsPanel.js";
18
+ import { LiveToolGateDialog } from "./LiveToolGateDialog.js";
19
19
  import { LiveVideoPreview } from "./LiveVideoPreview.js";
20
20
  import { liveStateLabel } from "./labels.js";
21
21
  import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
22
22
  import { PANEL_EDGE, SidePanel, SidePanelHeader, SidePanelToggle, useSidePanel, } from "./SidePanel.js";
23
23
  import { DEFAULT_CHAT_MAX_WIDTH } from "./TheoremChat.js";
24
- import { ApprovalCard, AuthChallengeCard, GATE_CARD_WIDTH } from "./ToolGateCard.js";
25
24
  import { useTraceInspector, WithTrace } from "./TraceInspectorPanel.js";
26
25
  import { TheoremThemeProvider } from "./theme.js";
27
26
  /** The waveform's box: it rests on the stage floor rather than filling the stage. */
@@ -53,7 +52,7 @@ function LiveRunnerBody({ call, trace, onMinimize, }) {
53
52
  if (!call.callStarted) {
54
53
  return (_jsx(WithTrace, { inspector: inspector, children: _jsx(Layout, { ref: layoutRef, height: "fill", header: _jsx(SidePanelHeader, { children: inspector.toggle }), content: _jsx(LayoutContent, { padding: 0, children: _jsx(LiveLanding, { call: call }) }) }) }));
55
54
  }
56
- return (_jsxs(_Fragment, { children: [_jsx(WithTrace, { inspector: inspector, children: _jsx(Layout, { ref: layoutRef, height: "fill", header: _jsx(SidePanelHeader, { children: inspector.toggle }), content: _jsx(LayoutContent, { padding: 0, children: _jsx(Layout, { height: "fill", end: _jsx(SidePanel, { id: captions.id, labels: captionLabels, resizable: captions.resizable, open: captions.open, children: _jsx(LiveCaptions, { call: call }) }), content: _jsx(LayoutContent, { padding: 0, children: _jsx(VStack, { height: "100%", padding: PANEL_EDGE, children: _jsx(LiveStage, { call: call, captionsToggle: captionsToggle, onMinimize: onMinimize }) }) }) }) }) }) }), _jsx(LiveToolGateDialog, { prompt: call.gatePrompt, agent: t('@theorem.agent.handle', { handle: call.handle }), onResolve: call.resolveGateDecision })] }));
55
+ return (_jsxs(_Fragment, { children: [_jsx(WithTrace, { inspector: inspector, children: _jsx(Layout, { ref: layoutRef, height: "fill", header: _jsx(SidePanelHeader, { children: inspector.toggle }), content: _jsx(LayoutContent, { padding: 0, children: _jsx(Layout, { height: "fill", end: _jsx(SidePanel, { id: captions.id, labels: captionLabels, resizable: captions.resizable, open: captions.open, children: _jsx(LiveCaptions, { call: call }) }), content: _jsx(LayoutContent, { padding: 0, children: _jsx(VStack, { height: "100%", padding: PANEL_EDGE, children: _jsx(LiveStage, { call: call, captionsToggle: captionsToggle, onMinimize: onMinimize }) }) }) }) }) }) }), _jsx(LiveToolGateDialog, { call: call })] }));
57
56
  }
58
57
  /**
59
58
  * Before a call: the chat landing's greeting, with call buttons where the
@@ -134,24 +133,3 @@ function LiveCallButton({ call }) {
134
133
  function LiveCaptions({ call }) {
135
134
  return (_jsx(LiveCaptionsPanel, { handle: call.handle, captions: call.captions, pastCalls: call.pastCalls, draftText: call.textDraft, onDraftTextChange: call.setTextDraft, onSubmit: call.handleSendText, isDisabled: !call.sessionActive, showComposer: call.textAvailable, leading: call.videoPreview ? (_jsx(LiveVideoPreview, { video: call.videoPreview, facingMode: call.videoFacingMode })) : null }));
136
135
  }
137
- /** Room around the gate card for its shadow. */
138
- const GATE_DIALOG_PADDING = 4;
139
- const GATE_DIALOG_WIDTH = GATE_CARD_WIDTH + 2 * GATE_DIALOG_PADDING * 4;
140
- /** The dialog draws no surface of its own: the card is the one chat shows, alone. */
141
- const GATE_DIALOG_BARE = { background: 'transparent', boxShadow: 'none' };
142
- /**
143
- * Tool approvals and credential prompts, as a dialog that must be answered. It shows the card
144
- * chat shows inline and nothing else, and closes on the answer.
145
- */
146
- function LiveToolGateDialog({ prompt, agent, onResolve, }) {
147
- return (_jsx(Dialog, { isOpen: prompt !== null, purpose: "required", width: GATE_DIALOG_WIDTH, padding: GATE_DIALOG_PADDING, style: GATE_DIALOG_BARE,
148
- // why: "required" disables dismissal; should one slip through, it denies.
149
- onOpenChange: (open) => {
150
- if (!open)
151
- onResolve({ action: 'deny' });
152
- }, children: prompt ? (prompt.gate.kind === 'auth' ? (_jsx(AuthChallengeCard, { gate: prompt.gate, toolName: prompt.gate.tool, onAuthenticated: (secret) => {
153
- onResolve({ action: 'auth', secret });
154
- } })) : (_jsx(ApprovalCard, { gate: prompt.gate, toolName: prompt.gate.tool, agent: agent, input: prompt.input, onDecision: (action) => {
155
- onResolve({ action });
156
- } }))) : null }));
157
- }
@@ -0,0 +1,9 @@
1
+ import type { LiveCall } from '../components/live/use-live-call.ts';
2
+ /**
3
+ * Tool approvals and credential prompts, as a dialog that must be answered. It shows the card
4
+ * chat shows inline and nothing else, and closes on the answer. `LiveRunner` and
5
+ * `LiveCallTile` both show it, so a gate is answered wherever the call is drawn.
6
+ */
7
+ export declare function LiveToolGateDialog({ call }: {
8
+ call: LiveCall;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Dialog } from '@astryxdesign/core/Dialog';
3
+ import { useLabels } from "./labels-provider.js";
4
+ import { ApprovalCard, AuthChallengeCard, GATE_CARD_WIDTH } from "./ToolGateCard.js";
5
+ /** Room around the gate card for its shadow. */
6
+ const GATE_DIALOG_PADDING = 4;
7
+ const GATE_DIALOG_WIDTH = GATE_CARD_WIDTH + 2 * GATE_DIALOG_PADDING * 4;
8
+ /** The dialog draws no surface of its own: the card is the one chat shows, alone. */
9
+ const GATE_DIALOG_BARE = { background: 'transparent', boxShadow: 'none' };
10
+ /**
11
+ * Tool approvals and credential prompts, as a dialog that must be answered. It shows the card
12
+ * chat shows inline and nothing else, and closes on the answer. `LiveRunner` and
13
+ * `LiveCallTile` both show it, so a gate is answered wherever the call is drawn.
14
+ */
15
+ export function LiveToolGateDialog({ call }) {
16
+ const t = useLabels();
17
+ const prompt = call.gatePrompt;
18
+ const onResolve = call.resolveGateDecision;
19
+ const agent = t('@theorem.agent.handle', { handle: call.handle });
20
+ return (_jsx(Dialog, { isOpen: prompt !== null, purpose: "required", width: GATE_DIALOG_WIDTH, padding: GATE_DIALOG_PADDING, style: GATE_DIALOG_BARE,
21
+ // why: "required" disables dismissal; should one slip through, it denies.
22
+ onOpenChange: (open) => {
23
+ if (!open)
24
+ onResolve({ action: 'deny' });
25
+ }, children: prompt ? (prompt.gate.kind === 'auth' ? (_jsx(AuthChallengeCard, { gate: prompt.gate, toolName: prompt.gate.tool, onAuthenticated: (secret) => {
26
+ onResolve({ action: 'auth', secret });
27
+ } })) : (_jsx(ApprovalCard, { gate: prompt.gate, toolName: prompt.gate.tool, agent: agent, input: prompt.input, onDecision: (action) => {
28
+ onResolve({ action });
29
+ } }))) : null }));
30
+ }