@datalayer/agent-runtimes 1.3.74 → 1.3.78
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/lib/agents/AgentDetails.d.ts +16 -2
- package/lib/agents/AgentDetails.js +381 -272
- package/lib/chat/ChatFloating.d.ts +27 -1
- package/lib/chat/ChatFloating.js +97 -13
- package/lib/chat/assistant/AgentDetailsDialog.d.ts +21 -0
- package/lib/chat/assistant/AgentDetailsDialog.js +19 -0
- package/lib/chat/assistant/AssistantContextMenu.d.ts +67 -0
- package/lib/chat/assistant/AssistantContextMenu.js +68 -0
- package/lib/chat/assistant/AssistantStage.d.ts +144 -4
- package/lib/chat/assistant/AssistantStage.js +280 -10
- package/lib/chat/assistant/BalloonParts.d.ts +33 -5
- package/lib/chat/assistant/BalloonParts.js +66 -44
- package/lib/chat/assistant/BalloonVisual.d.ts +10 -2
- package/lib/chat/assistant/BalloonVisual.js +4 -3
- package/lib/chat/assistant/SandboxDetailsDialog.d.ts +44 -0
- package/lib/chat/assistant/SandboxDetailsDialog.js +214 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +54 -6
- package/lib/chat/assistant/SpeechBalloon.js +73 -22
- package/lib/chat/assistant/assistantDetails.d.ts +84 -0
- package/lib/chat/assistant/assistantDetails.js +140 -0
- package/lib/chat/assistant/formats/files.d.ts +23 -0
- package/lib/chat/assistant/formats/files.js +48 -0
- package/lib/chat/assistant/formats/index.d.ts +2 -0
- package/lib/chat/assistant/formats/index.js +1 -0
- package/lib/chat/assistant/state.d.ts +7 -14
- package/lib/chat/assistant/state.js +21 -20
- package/lib/chat/assistant/toolLine.d.ts +16 -0
- package/lib/chat/assistant/toolLine.js +46 -0
- package/lib/chat/base/ChatBase.js +71 -1
- package/lib/chat/base/loopMessage.d.ts +37 -0
- package/lib/chat/base/loopMessage.js +37 -0
- package/lib/chat/indicators/TypingDots.d.ts +13 -0
- package/lib/chat/indicators/TypingDots.js +21 -0
- package/lib/chat/messages/ChatMarkdown.d.ts +16 -0
- package/lib/chat/messages/ChatMarkdown.js +18 -0
- package/lib/chat/messages/ChatMessageList.d.ts +8 -1
- package/lib/chat/messages/ChatMessageList.js +25 -33
- package/lib/chat/styles/streamdownStyles.js +3 -1
- package/lib/chat/tools/ToolCallDisplay.d.ts +8 -1
- package/lib/chat/tools/ToolCallDisplay.js +8 -7
- package/lib/components/code-sandboxes/CodeSandboxCellVariablesDialog.js +5 -11
- package/lib/components/code-sandboxes/CodeSandboxVariables.d.ts +8 -0
- package/lib/components/code-sandboxes/CodeSandboxVariables.js +17 -11
- package/lib/components/inspector/AgentInspector.d.ts +59 -0
- package/lib/components/inspector/AgentInspector.js +220 -0
- package/lib/components/inspector/AgentInspectorDialog.d.ts +19 -0
- package/lib/components/inspector/AgentInspectorDialog.js +9 -0
- package/lib/components/inspector/JsonTree.d.ts +24 -0
- package/lib/components/inspector/JsonTree.js +89 -0
- package/lib/components/inspector/a2aInspect.d.ts +56 -0
- package/lib/components/inspector/a2aInspect.js +478 -0
- package/lib/components/inspector/agentInspector.d.ts +179 -0
- package/lib/components/inspector/agentInspector.js +267 -0
- package/lib/components/inspector/chatInspect.d.ts +38 -0
- package/lib/components/inspector/chatInspect.js +65 -0
- package/lib/components/inspector/classify.d.ts +19 -0
- package/lib/components/inspector/classify.js +42 -0
- package/lib/components/inspector/index.d.ts +14 -0
- package/lib/components/inspector/index.js +18 -0
- package/lib/components/teams/A2ATeamGraph.d.ts +69 -1
- package/lib/components/teams/A2ATeamGraph.js +277 -37
- package/lib/components/teams/NotebookPreviewView.js +9 -1
- package/lib/components/teams/TeamNotebook.d.ts +3 -1
- package/lib/components/teams/TeamNotebook.js +3 -3
- package/lib/components/teams/TeamNotebookView.js +15 -0
- package/lib/components/teams/jupyterThemeVariables.d.ts +9 -0
- package/lib/components/teams/jupyterThemeVariables.js +441 -0
- package/lib/components/teams/teamConnections.d.ts +9 -1
- package/lib/components/teams/teamConnections.js +20 -0
- package/lib/components/teams/teamNotebookKernel.d.ts +3 -0
- package/lib/components/teams/teamNotebookKernel.js +17 -0
- package/lib/components/teams/useA2ATeam.d.ts +20 -3
- package/lib/components/teams/useA2ATeam.js +108 -23
- package/lib/examples/AgentA2ATeamExample.d.ts +13 -0
- package/lib/examples/AgentA2ATeamExample.js +67 -13
- package/lib/examples/AssistantExample.js +65 -26
- package/lib/examples/AssistantGalleryExample.js +2 -1
- package/lib/examples/exampleGroups.d.ts +1 -1
- package/lib/examples/exampleGroups.js +1 -1
- package/lib/examples/utils/assistantGallery.d.ts +7 -6
- package/lib/examples/utils/assistantGallery.js +23 -8
- package/lib/jupyter/exec/Python.d.ts +0 -1
- package/lib/jupyter/exec/Python.js +0 -32
- package/lib/jupyter/exec/Snippets.d.ts +4 -12
- package/lib/jupyter/exec/Snippets.js +0 -8
- package/lib/loop/apps/AppFeedback.js +3 -1
- package/lib/loop/apps/AppKept.d.ts +22 -0
- package/lib/loop/apps/AppKept.js +36 -0
- package/lib/loop/apps/AppRenderer.d.ts +11 -1
- package/lib/loop/apps/AppRenderer.js +22 -4
- package/lib/loop/apps/checks.d.ts +7 -0
- package/lib/loop/apps/checks.js +39 -0
- package/lib/loop/apps/index.d.ts +3 -0
- package/lib/loop/apps/index.js +3 -0
- package/lib/loop/apps/kept.d.ts +30 -0
- package/lib/loop/apps/kept.js +52 -0
- package/lib/loop/apps/records.d.ts +12 -4
- package/lib/loop/apps/records.js +13 -5
- package/lib/loop/apps/saved.d.ts +30 -0
- package/lib/loop/apps/saved.js +40 -0
- package/lib/loop/core/index.d.ts +17 -0
- package/lib/loop/core/index.js +1 -0
- package/lib/loop/embed/AppEmbed.d.ts +8 -1
- package/lib/loop/embed/AppEmbed.js +4 -4
- package/lib/loop/embed/LoopEmbed.d.ts +6 -1
- package/lib/loop/embed/LoopEmbed.js +4 -2
- package/lib/loop/embed/element.js +1 -1
- package/lib/loop/plugins/agents/DatalayerAgentBridge.js +12 -6
- package/lib/loop/plugins/agents/browserService.d.ts +7 -1
- package/lib/loop/plugins/agents/browserService.js +4 -0
- package/lib/loop/plugins/agents/plugin.d.ts +16 -0
- package/lib/loop/plugins/agents/switchable.d.ts +3 -0
- package/lib/loop/plugins/agents/switchable.js +1 -0
- package/lib/loop/plugins/app-activity/index.js +3 -0
- package/lib/loop/plugins/app-computer/index.js +3 -0
- package/lib/loop/plugins/app-rules/AppRulesCard.d.ts +5 -0
- package/lib/loop/plugins/app-rules/AppRulesCard.js +25 -6
- package/lib/loop/plugins/app-rules/index.js +3 -0
- package/lib/loop/plugins/assistant/index.d.ts +7 -2
- package/lib/loop/plugins/assistant/index.js +58 -4
- package/lib/loop/plugins/assistant-characters/index.d.ts +7 -0
- package/lib/loop/plugins/assistant-characters/index.js +17 -0
- package/lib/loop/plugins/chat/ChatView.js +4 -1
- package/lib/loop/plugins/page-layout/SplitLayout.d.ts +2 -1
- package/lib/loop/plugins/page-layout/SplitLayout.js +4 -0
- package/lib/loop/presets.d.ts +4 -0
- package/lib/loop/presets.js +3 -1
- package/lib/loop/shell/LoopWorkspace.d.ts +7 -0
- package/lib/loop/shell/LoopWorkspace.js +5 -4
- package/lib/loop/shell/SidebarRail.d.ts +53 -0
- package/lib/loop/shell/SidebarRail.js +74 -0
- package/lib/loop/shell/index.d.ts +2 -0
- package/lib/loop/shell/index.js +2 -0
- package/lib/loop/shell/paneMotion.d.ts +21 -0
- package/lib/loop/shell/paneMotion.js +37 -0
- package/lib/runtimes/browser/a2aPeer.d.ts +4 -1
- package/lib/specs/apps.js +8 -5
- package/lib/specs/uiPlugins.js +529 -0
- package/lib/types/agentspecs.d.ts +5 -3
- package/lib/types/chat.d.ts +12 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_jupyter_theme_variables.mjs +57 -0
- package/scripts/codegen/generate_ui_plugins.py +112 -2
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Suspense, lazy, useCallback, useEffect, useId, useRef, useState, } from 'react';
|
|
3
3
|
import { ActionList, ActionMenu, IconButton, useTheme } from '@primer/react';
|
|
4
4
|
import { Box } from '@datalayer/primer-addons';
|
|
5
|
-
import { XIcon } from '@primer/octicons-react';
|
|
5
|
+
import { CodeIcon, CommentDiscussionIcon, DependabotIcon, InfoIcon, MuteIcon, PencilIcon, PlusIcon, PulseIcon, ScreenNormalIcon, SquareFillIcon, TrashIcon, UnmuteIcon, XIcon, } from '@primer/octicons-react';
|
|
6
|
+
import { Dialog, Text } from '@primer/react';
|
|
6
7
|
import { assistantCharacter } from './characters';
|
|
7
|
-
import { SpeechBalloon } from './SpeechBalloon';
|
|
8
|
+
import { BalloonHistoryLarge, SpeechBalloon, } from './SpeechBalloon';
|
|
9
|
+
import { conversationHeaderText } from './BalloonParts';
|
|
10
|
+
import { AssistantContextMenu, LONG_PRESS_MS, opensContextMenu, } from './AssistantContextMenu';
|
|
8
11
|
import { BalloonExpandContext, ExpandedVisual, focusExpanded, } from './BalloonVisual';
|
|
9
12
|
import { SpriteCharacter } from './SpriteCharacter';
|
|
13
|
+
import { conversationCount, } from './toolLine';
|
|
10
14
|
import { ASSISTANT_OBSTACLES, POINTER_CALM_MS, boxesMeet, pointerNear, } from './state';
|
|
11
15
|
/** The motions, by the state the stage is in. */
|
|
12
16
|
const MOTIONS = {
|
|
@@ -58,6 +62,148 @@ const AWAY_CHOICES = [
|
|
|
58
62
|
{ away: 'session', label: 'Hide for this session' },
|
|
59
63
|
{ away: 'always', label: 'Don\u2019t show again' },
|
|
60
64
|
];
|
|
65
|
+
/** The Agent Inspector's dialog: loaded when it is first asked for. */
|
|
66
|
+
const AgentInspectorDialog = lazy(() => import('../../components/inspector/AgentInspectorDialog'));
|
|
67
|
+
/** *Agent Details…*: loaded when it is first asked for. */
|
|
68
|
+
const AgentDetailsDialog = lazy(() => import('./AgentDetailsDialog'));
|
|
69
|
+
/** *Code Sandbox Details…*: loaded when it is first asked for. */
|
|
70
|
+
const SandboxDetailsDialog = lazy(() => import('./SandboxDetailsDialog'));
|
|
71
|
+
/**
|
|
72
|
+
* The entries of the assistant's menu, composed from what the host passes:
|
|
73
|
+
* each shows when what it does is there to do.
|
|
74
|
+
*/
|
|
75
|
+
export function assistantMenuItems(options) {
|
|
76
|
+
const items = [];
|
|
77
|
+
if (options.inspect) {
|
|
78
|
+
items.push({
|
|
79
|
+
id: 'inspect',
|
|
80
|
+
label: 'Inspect the agent…',
|
|
81
|
+
icon: PulseIcon,
|
|
82
|
+
onSelect: options.inspect,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (options.agentDetails) {
|
|
86
|
+
items.push({
|
|
87
|
+
id: 'agent-details',
|
|
88
|
+
label: 'Agent Details…',
|
|
89
|
+
icon: DependabotIcon,
|
|
90
|
+
onSelect: options.agentDetails,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (options.sandboxDetails) {
|
|
94
|
+
items.push({
|
|
95
|
+
id: 'sandbox-details',
|
|
96
|
+
label: 'Code Sandbox Details…',
|
|
97
|
+
icon: CodeIcon,
|
|
98
|
+
onSelect: options.sandboxDetails,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (options.conversationLabel !== false) {
|
|
102
|
+
items.push({
|
|
103
|
+
id: 'conversation',
|
|
104
|
+
label: options.conversationLabel ??
|
|
105
|
+
(options.open ? 'Close the conversation' : 'Open the conversation'),
|
|
106
|
+
icon: CommentDiscussionIcon,
|
|
107
|
+
onSelect: options.onToggle,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (options.busy && options.onStop) {
|
|
111
|
+
items.push({
|
|
112
|
+
id: 'stop',
|
|
113
|
+
label: 'Stop the turn',
|
|
114
|
+
icon: SquareFillIcon,
|
|
115
|
+
onSelect: options.onStop,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (options.onNewChat) {
|
|
119
|
+
items.push({
|
|
120
|
+
id: 'new-chat',
|
|
121
|
+
label: 'New conversation',
|
|
122
|
+
icon: PlusIcon,
|
|
123
|
+
onSelect: options.onNewChat,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (options.onClear) {
|
|
127
|
+
items.push({
|
|
128
|
+
id: 'clear',
|
|
129
|
+
label: 'Clear the conversation',
|
|
130
|
+
icon: TrashIcon,
|
|
131
|
+
onSelect: options.onClear,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (options.suggestions?.length && options.onSuggestion) {
|
|
135
|
+
const send = options.onSuggestion;
|
|
136
|
+
for (const suggestion of options.suggestions) {
|
|
137
|
+
items.push({
|
|
138
|
+
id: `suggestion:${suggestion.label}`,
|
|
139
|
+
label: suggestion.label,
|
|
140
|
+
description: suggestion.prompt,
|
|
141
|
+
group: 'Suggestions',
|
|
142
|
+
disabled: options.busy,
|
|
143
|
+
onSelect: () => send(suggestion),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (options.onBalloonDisplayChange) {
|
|
148
|
+
const change = options.onBalloonDisplayChange;
|
|
149
|
+
items.push({
|
|
150
|
+
id: 'balloon-history',
|
|
151
|
+
label: 'History',
|
|
152
|
+
group: 'Balloon',
|
|
153
|
+
checked: options.balloonDisplay !== 'current',
|
|
154
|
+
onSelect: () => change('history'),
|
|
155
|
+
}, {
|
|
156
|
+
id: 'balloon-current',
|
|
157
|
+
label: 'Current',
|
|
158
|
+
group: 'Balloon',
|
|
159
|
+
checked: options.balloonDisplay === 'current',
|
|
160
|
+
onSelect: () => change('current'),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (options.onChangeCharacter) {
|
|
164
|
+
items.push({
|
|
165
|
+
id: 'character',
|
|
166
|
+
label: 'Change character…',
|
|
167
|
+
icon: PencilIcon,
|
|
168
|
+
onSelect: options.onChangeCharacter,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (options.speech) {
|
|
172
|
+
items.push({
|
|
173
|
+
id: 'speech',
|
|
174
|
+
label: options.speech.muted ? 'Unmute speech' : 'Mute speech',
|
|
175
|
+
icon: options.speech.muted ? UnmuteIcon : MuteIcon,
|
|
176
|
+
onSelect: options.speech.onToggle,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (options.onResetPosition) {
|
|
180
|
+
items.push({
|
|
181
|
+
id: 'reset-position',
|
|
182
|
+
label: 'Reset position',
|
|
183
|
+
icon: ScreenNormalIcon,
|
|
184
|
+
onSelect: options.onResetPosition,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (options.about) {
|
|
188
|
+
items.push({
|
|
189
|
+
id: 'about',
|
|
190
|
+
label: `About ${options.name}`,
|
|
191
|
+
icon: InfoIcon,
|
|
192
|
+
onSelect: options.about,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
items.push(...(options.extra ?? []));
|
|
196
|
+
for (const choice of AWAY_CHOICES) {
|
|
197
|
+
items.push({
|
|
198
|
+
id: `away:${choice.away}`,
|
|
199
|
+
label: choice.label,
|
|
200
|
+
group: 'Send away',
|
|
201
|
+
icon: XIcon,
|
|
202
|
+
onSelect: () => options.onDismiss(choice.away),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return items;
|
|
206
|
+
}
|
|
61
207
|
/** Room the balloon needs above the character before it goes below. */
|
|
62
208
|
const BALLOON_ROOM = 200;
|
|
63
209
|
/** A place's length in pixels, given as a number or as `"<n>px"`. */
|
|
@@ -210,7 +356,7 @@ export function useMouth(stageRef, mouthLevel, speaking) {
|
|
|
210
356
|
};
|
|
211
357
|
}, [stageRef, mouthLevel, speaking]);
|
|
212
358
|
}
|
|
213
|
-
export function AssistantStage({ character, state, size = 88, place, stageRef, onDragStart, open, onToggle, balloon, insist = false, onDismiss, ownRef, decide, mouthLevel, stayPut = false, balloonDisplay = 'history', expandTarget, expandOnArrival = false, }) {
|
|
359
|
+
export function AssistantStage({ character, state, size = 88, place, stageRef, onDragStart, open, onToggle, onCharacterClick, onCharacterKeyDown, balloon, insist = false, onDismiss, ownRef, decide, mouthLevel, stayPut = false, balloonDisplay = 'history', expandTarget, expandOnArrival = false, suggestions, onSuggestion, inspector, inspectAgent, conversationLabel, onBalloonDisplayChange, onStop, onNewChat, onClear, onChangeCharacter, speech, onResetPosition, about, sandbox, contextMenu, agentName, label, }) {
|
|
214
360
|
// A shipped one by id, a drawing contributed by a plugin (T-24), or a
|
|
215
361
|
// character read from a file (T-26).
|
|
216
362
|
const shipped = typeof character === 'string'
|
|
@@ -218,21 +364,69 @@ export function AssistantStage({ character, state, size = 88, place, stageRef, o
|
|
|
218
364
|
: 'Drawing' in character
|
|
219
365
|
? character
|
|
220
366
|
: undefined;
|
|
221
|
-
const
|
|
367
|
+
const characterName = shipped
|
|
222
368
|
? shipped.name
|
|
223
369
|
: character.name;
|
|
370
|
+
// The agent's name where the host gives it, else the character's.
|
|
371
|
+
const name = agentName ?? characterName;
|
|
372
|
+
// The character, as the figure's description, when it is not its name.
|
|
373
|
+
const describedBy = useId();
|
|
374
|
+
const described = Boolean(agentName || label);
|
|
224
375
|
// Drawn for the chat's colour mode: the dark drawing on a dark page (T-25).
|
|
225
376
|
const { colorScheme } = useTheme();
|
|
226
377
|
const colorMode = colorScheme?.startsWith('dark') ? 'dark' : 'light';
|
|
227
378
|
const [hovered, setHovered] = useState(false);
|
|
228
379
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
380
|
+
// Its own menu, where it was opened; the inspector and *About* it opens.
|
|
381
|
+
const [menuAt, setMenuAt] = useState(null);
|
|
382
|
+
const [inspecting, setInspecting] = useState(false);
|
|
383
|
+
const [telling, setTelling] = useState(false);
|
|
384
|
+
const [detailing, setDetailing] = useState(null);
|
|
385
|
+
const characterRef = useRef(null);
|
|
386
|
+
const longPress = useRef(undefined);
|
|
387
|
+
const closeMenu = useCallback(() => setMenuAt(null), []);
|
|
388
|
+
useEffect(() => () => clearTimeout(longPress.current), []);
|
|
229
389
|
// Where the press began: a press that moves is a drag, not a click.
|
|
230
390
|
const pressedAt = useRef(null);
|
|
231
|
-
const aside = useKeepClear(stageRef, ownRef, menuOpen ||
|
|
391
|
+
const aside = useKeepClear(stageRef, ownRef, menuOpen ||
|
|
392
|
+
menuAt !== null ||
|
|
393
|
+
inspecting ||
|
|
394
|
+
telling ||
|
|
395
|
+
detailing !== null ||
|
|
396
|
+
stayPut);
|
|
232
397
|
useMouth(stageRef, mouthLevel, state === 'speaking');
|
|
233
398
|
// A decision being asked, or its answer, keeps the balloon up.
|
|
234
399
|
const [deciding, setDeciding] = useState(false);
|
|
235
400
|
const showBalloon = !aside && !open && !!balloon && (hovered || insist || deciding);
|
|
401
|
+
// Suggestions while it waits for a question.
|
|
402
|
+
const waitsForQuestion = (state === 'idle' || state === 'greeting') &&
|
|
403
|
+
!balloon?.tool &&
|
|
404
|
+
!balloon?.approval &&
|
|
405
|
+
!balloon?.busy;
|
|
406
|
+
const busy = state === 'thinking' || state === 'working' || state === 'speaking';
|
|
407
|
+
const menuItems = assistantMenuItems({
|
|
408
|
+
name,
|
|
409
|
+
open,
|
|
410
|
+
onToggle,
|
|
411
|
+
onDismiss,
|
|
412
|
+
conversationLabel,
|
|
413
|
+
inspect: inspector ? () => setInspecting(true) : undefined,
|
|
414
|
+
agentDetails: about ? () => setDetailing('agent') : undefined,
|
|
415
|
+
sandboxDetails: sandbox ? () => setDetailing('sandbox') : undefined,
|
|
416
|
+
balloonDisplay,
|
|
417
|
+
onBalloonDisplayChange,
|
|
418
|
+
suggestions,
|
|
419
|
+
onSuggestion,
|
|
420
|
+
busy,
|
|
421
|
+
onStop,
|
|
422
|
+
onNewChat,
|
|
423
|
+
onClear,
|
|
424
|
+
onChangeCharacter,
|
|
425
|
+
speech,
|
|
426
|
+
onResetPosition,
|
|
427
|
+
about: about ? () => setTelling(true) : undefined,
|
|
428
|
+
extra: contextMenu,
|
|
429
|
+
});
|
|
236
430
|
// The large visual drawn now: kept while the balloon moves on to other
|
|
237
431
|
// words, until it is closed or another one is expanded.
|
|
238
432
|
const visual = balloon?.visual;
|
|
@@ -245,6 +439,17 @@ export function AssistantStage({ character, state, size = 88, place, stageRef, o
|
|
|
245
439
|
}
|
|
246
440
|
}
|
|
247
441
|
: null;
|
|
442
|
+
// The history, large: drawn from the balloon's messages as they are now.
|
|
443
|
+
const history = balloon?.history;
|
|
444
|
+
const historyVisual = balloonDisplay !== 'current' && history && history.length > 0
|
|
445
|
+
? {
|
|
446
|
+
id: 'balloon-history',
|
|
447
|
+
title: conversationHeaderText(conversationCount(history)),
|
|
448
|
+
shrink: true,
|
|
449
|
+
render: () => (_jsx(BalloonHistoryLarge, { history: history, tool: balloon?.tool, attachment: balloon?.attachment })),
|
|
450
|
+
}
|
|
451
|
+
: null;
|
|
452
|
+
const shown = expanded?.id === 'balloon-history' ? historyVisual : expanded;
|
|
248
453
|
useEffect(() => {
|
|
249
454
|
if (visual && expandOnArrival && expandTarget) {
|
|
250
455
|
setExpanded(visual);
|
|
@@ -349,8 +554,46 @@ export function AssistantStage({ character, state, size = 88, place, stageRef, o
|
|
|
349
554
|
'@media (prefers-reduced-motion: reduce)': {
|
|
350
555
|
'& *': { animation: 'none !important' },
|
|
351
556
|
},
|
|
352
|
-
}, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: [
|
|
557
|
+
}, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: [shown && (_jsx(ExpandedVisual, { visual: shown, target: expandTarget, onClose: () => setExpanded(null) })), showBalloon && balloon && (_jsx(BalloonExpandContext.Provider, { value: expand, children: _jsx(SpeechBalloon, { text: balloon.text, more: balloon.more, approval: balloon.approval, onDismissPeek: balloon.onDismiss, display: balloonDisplay, tool: balloon.tool, speaking: balloon.speaking, busy: balloon.busy, attachment: balloon.attachment, history: balloon.history, onExpand: expand ?? undefined, expandTitle: visual?.title, onExpandHistory: historyVisual
|
|
558
|
+
? () => {
|
|
559
|
+
setExpanded(historyVisual);
|
|
560
|
+
if (expandTarget) {
|
|
561
|
+
requestAnimationFrame(() => focusExpanded(expandTarget));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
: undefined, suggestions: waitsForQuestion ? suggestions : undefined, fullText: balloon.fullText, waiting: (state === 'thinking' ||
|
|
565
|
+
state === 'working' ||
|
|
566
|
+
state === 'waiting') &&
|
|
567
|
+
!balloon.speaking, onSuggestion: onSuggestion, decide: decide, onDecisionActive: setDeciding, wide: deciding, onOpen: onToggle, above: size + 8, side: balloonSide(place).side, align: balloonSide(place).align, tailAt: size / 2 }) })), _jsxs(Box, { as: "button", type: "button", "aria-label": label ??
|
|
568
|
+
(open ? `Close the conversation with ${name}` : `Talk to ${name}`), "aria-describedby": described ? describedBy : undefined, "aria-expanded": open, "aria-haspopup": "menu", ref: characterRef, "data-assistant-figure": "", onContextMenu: (event) => {
|
|
569
|
+
event.preventDefault();
|
|
570
|
+
clearTimeout(longPress.current);
|
|
571
|
+
setMenuAt({ x: event.clientX, y: event.clientY });
|
|
572
|
+
}, onKeyDown: (event) => {
|
|
573
|
+
if (opensContextMenu(event)) {
|
|
574
|
+
event.preventDefault();
|
|
575
|
+
const box = event.currentTarget.getBoundingClientRect();
|
|
576
|
+
setMenuAt({ x: box.left + box.width / 2, y: box.bottom });
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
onCharacterKeyDown?.(event);
|
|
580
|
+
}, onPointerUp: () => clearTimeout(longPress.current), onPointerCancel: () => clearTimeout(longPress.current), onPointerMove: (event) => {
|
|
581
|
+
const from = pressedAt.current;
|
|
582
|
+
if (from &&
|
|
583
|
+
Math.hypot(event.clientX - from.x, event.clientY - from.y) > 8) {
|
|
584
|
+
clearTimeout(longPress.current);
|
|
585
|
+
}
|
|
586
|
+
}, onPointerDown: (event) => {
|
|
353
587
|
pressedAt.current = { x: event.clientX, y: event.clientY };
|
|
588
|
+
if (event.pointerType === 'touch') {
|
|
589
|
+
const at = { x: event.clientX, y: event.clientY };
|
|
590
|
+
clearTimeout(longPress.current);
|
|
591
|
+
longPress.current = setTimeout(() => {
|
|
592
|
+
// Held, not tapped: the menu, and no click after it.
|
|
593
|
+
pressedAt.current = { x: -1e6, y: -1e6 };
|
|
594
|
+
setMenuAt(at);
|
|
595
|
+
}, LONG_PRESS_MS);
|
|
596
|
+
}
|
|
354
597
|
onDragStart(event);
|
|
355
598
|
}, onClick: (event) => {
|
|
356
599
|
const from = pressedAt.current;
|
|
@@ -359,7 +602,7 @@ export function AssistantStage({ character, state, size = 88, place, stageRef, o
|
|
|
359
602
|
Math.hypot(event.clientX - from.x, event.clientY - from.y) > 4) {
|
|
360
603
|
return;
|
|
361
604
|
}
|
|
362
|
-
onToggle();
|
|
605
|
+
(onCharacterClick ?? onToggle)();
|
|
363
606
|
}, sx: {
|
|
364
607
|
display: 'block',
|
|
365
608
|
width: size,
|
|
@@ -377,10 +620,37 @@ export function AssistantStage({ character, state, size = 88, place, stageRef, o
|
|
|
377
620
|
outlineOffset: 2,
|
|
378
621
|
borderRadius: '50%',
|
|
379
622
|
},
|
|
380
|
-
}, children:
|
|
623
|
+
}, children: [described && (_jsx(Box, { as: "span", id: describedBy, sx: {
|
|
624
|
+
position: 'absolute',
|
|
625
|
+
width: 1,
|
|
626
|
+
height: 1,
|
|
627
|
+
overflow: 'hidden',
|
|
628
|
+
clip: 'rect(0 0 0 0)',
|
|
629
|
+
whiteSpace: 'nowrap',
|
|
630
|
+
}, children: `Its character: ${characterName}` })), shipped ? (_jsx(shipped.Drawing, { size: size, mode: colorMode })) : (_jsx(SpriteCharacter, { character: character, state: state, size: size }))] }), (hovered || menuOpen) && !aside && state !== 'goodbye' && (_jsx(Box, { sx: { position: 'absolute', top: '-6px', right: '-6px' }, children: _jsxs(ActionMenu, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { icon: XIcon, "aria-label": `Send ${name} away`, size: "small", variant: "invisible", "data-assistant-dismiss": "", sx: {
|
|
381
631
|
bg: 'canvas.default',
|
|
382
632
|
borderRadius: '50%',
|
|
383
633
|
boxShadow: 'shadow.small',
|
|
384
|
-
} }) }), _jsx(ActionMenu.Overlay, { width: "auto", children: _jsx(ActionList, { children: AWAY_CHOICES.map(choice => (_jsx(ActionList.Item, { "data-assistant-away": choice.away, onSelect: () => onDismiss(choice.away), children: choice.label }, choice.away))) }) })] }) }))
|
|
634
|
+
} }) }), _jsx(ActionMenu.Overlay, { width: "auto", children: _jsx(ActionList, { children: AWAY_CHOICES.map(choice => (_jsx(ActionList.Item, { "data-assistant-away": choice.away, onSelect: () => onDismiss(choice.away), children: choice.label }, choice.away))) }) })] }) })), _jsx(AssistantContextMenu, { at: menuAt, items: menuItems, onClose: closeMenu, returnFocusRef: characterRef, label: `${name}\u2019s menu` }), inspecting && (_jsx(Suspense, { fallback: null, children: _jsx(AgentInspectorDialog, { sink: inspector ?? null, agent: inspectAgent, title: `${about?.name ?? name} \u00b7 Agent Inspector`, onClose: () => {
|
|
635
|
+
setInspecting(false);
|
|
636
|
+
characterRef.current?.focus();
|
|
637
|
+
} }) })), detailing === 'agent' && about && (_jsx(Suspense, { fallback: null, children: _jsx(AgentDetailsDialog, { about: about, name: name, onClose: () => {
|
|
638
|
+
setDetailing(null);
|
|
639
|
+
characterRef.current?.focus();
|
|
640
|
+
} }) })), detailing === 'sandbox' && sandbox && (_jsx(Suspense, { fallback: null, children: _jsx(SandboxDetailsDialog, { sandbox: sandbox, name: about?.name ?? name, onClose: () => {
|
|
641
|
+
setDetailing(null);
|
|
642
|
+
characterRef.current?.focus();
|
|
643
|
+
} }) })), telling && about && (_jsx(Dialog, { title: `About ${about.name ?? name}`, onClose: () => {
|
|
644
|
+
setTelling(false);
|
|
645
|
+
characterRef.current?.focus();
|
|
646
|
+
}, "data-assistant-about": "", children: [
|
|
647
|
+
['Agent', about.name],
|
|
648
|
+
['Spec', about.spec],
|
|
649
|
+
['Model', about.model],
|
|
650
|
+
['Runs', about.where],
|
|
651
|
+
['Character', characterName],
|
|
652
|
+
]
|
|
653
|
+
.filter(([, value]) => value)
|
|
654
|
+
.map(([key, value]) => (_jsxs(Text, { as: "p", sx: { m: 0, mb: 1 }, children: [_jsxs(Text, { sx: { color: 'fg.muted' }, children: [key, ": "] }), value] }, key))) }))] }));
|
|
385
655
|
}
|
|
386
656
|
export default AssistantStage;
|
|
@@ -7,15 +7,29 @@
|
|
|
7
7
|
* @module chat/assistant/BalloonParts
|
|
8
8
|
*/
|
|
9
9
|
import type { JSX, ReactNode } from 'react';
|
|
10
|
+
import { type ChatDensity } from '../messages/ChatMarkdown';
|
|
11
|
+
import type { DisplayItem } from '../../types/chat';
|
|
10
12
|
import { type BalloonToolLine } from './toolLine';
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* The conversation's items in the balloon, drawn by the chat's own
|
|
15
|
+
* components (`ChatMessageList`): markdown, code blocks, tool calls and
|
|
16
|
+
* their approvals, notes, the three dots — in the compact density, or the
|
|
17
|
+
* chat's own when drawn large.
|
|
18
|
+
*/
|
|
19
|
+
export declare function BalloonChatItems({ items, density, waiting, }: {
|
|
20
|
+
items: readonly DisplayItem[];
|
|
21
|
+
density?: ChatDensity;
|
|
22
|
+
/** At work with nothing written yet: the chat's dots, last. */
|
|
23
|
+
waiting?: boolean;
|
|
24
|
+
}): JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* The tool being called, in the balloon: the chat's tool call card, compact,
|
|
27
|
+
* with its runtime's own words (`Asking Accounting…`) as its summary.
|
|
15
28
|
* Seen, not read out: {@link ToolLineAnnouncer} says it once per change.
|
|
16
29
|
*/
|
|
17
|
-
export declare function
|
|
30
|
+
export declare function BalloonToolCall({ line, waiting, }: {
|
|
18
31
|
line: BalloonToolLine;
|
|
32
|
+
waiting?: boolean;
|
|
19
33
|
}): JSX.Element;
|
|
20
34
|
/**
|
|
21
35
|
* What a screen reader hears of the tool lines, politely: once when a call
|
|
@@ -45,10 +59,24 @@ export declare function ConversationBalloonHeader({ count, }: {
|
|
|
45
59
|
* done — the tool line while a tool runs, else the words — cut to a few
|
|
46
60
|
* lines, never scrolled; and what goes with them (`attachment`).
|
|
47
61
|
*/
|
|
48
|
-
export declare function CurrentBalloonBody({ text, tool, busy, speaking, attachment, }: {
|
|
62
|
+
export declare function CurrentBalloonBody({ text, tool, busy, speaking, attachment, waiting, whole, fullText, onOverflow, }: {
|
|
63
|
+
/**
|
|
64
|
+
* Shown whole: the full text (`fullText`, else `text`), unclamped, in a
|
|
65
|
+
* taller box that scrolls.
|
|
66
|
+
*/
|
|
67
|
+
whole?: boolean;
|
|
68
|
+
/** The text uncut, when `text` was cut to fit (`…`). */
|
|
69
|
+
fullText?: string;
|
|
70
|
+
/** Whether the clamped text overflows its lines: *more* has more to show. */
|
|
71
|
+
onOverflow?: (overflowing: boolean) => void;
|
|
49
72
|
text?: string;
|
|
50
73
|
tool?: BalloonToolLine;
|
|
51
74
|
busy?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* At work with nothing written yet (thinking, working, waiting): the
|
|
77
|
+
* chat's three dots follow the line, or stand alone.
|
|
78
|
+
*/
|
|
79
|
+
waiting?: boolean;
|
|
52
80
|
/** Words are arriving: read out once they have. */
|
|
53
81
|
speaking?: boolean;
|
|
54
82
|
attachment?: ReactNode;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Text } from '@primer/react';
|
|
4
4
|
import { Box } from '@datalayer/primer-addons';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { TypingDots } from '../indicators/TypingDots';
|
|
6
|
+
import { ChatMessageList } from '../messages/ChatMessageList';
|
|
7
|
+
import { ChatMarkdown } from '../messages/ChatMarkdown';
|
|
8
|
+
import { agentRuntimeStore } from '../../stores/agentRuntimeStore';
|
|
9
|
+
import { toolAnnouncement, displayItemOfLine, } from './toolLine';
|
|
8
10
|
/** Out of sight, still heard. */
|
|
9
11
|
const VISUALLY_HIDDEN = {
|
|
10
12
|
position: 'absolute',
|
|
@@ -14,42 +16,48 @@ const VISUALLY_HIDDEN = {
|
|
|
14
16
|
clip: 'rect(0 0 0 0)',
|
|
15
17
|
whiteSpace: 'nowrap',
|
|
16
18
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/** How the balloon's chat items know their people: no avatars are drawn. */
|
|
20
|
+
const BALLOON_AVATARS = {
|
|
21
|
+
userAvatar: null,
|
|
22
|
+
assistantAvatar: null,
|
|
23
|
+
showAvatars: false,
|
|
24
|
+
avatarSize: 0,
|
|
25
|
+
userAvatarBg: 'neutral.muted',
|
|
26
|
+
assistantAvatarBg: 'accent.emphasis',
|
|
21
27
|
};
|
|
22
28
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
|
|
29
|
+
* Inline approvals answered in the balloon go where the chat's go: the
|
|
30
|
+
* shared monitoring socket.
|
|
31
|
+
*/
|
|
32
|
+
async function respondInBalloon(_toolCallId, result) {
|
|
33
|
+
if (result && typeof result === 'object') {
|
|
34
|
+
const record = result;
|
|
35
|
+
if (record.type === 'tool-approval-decision' &&
|
|
36
|
+
typeof record.approved === 'boolean' &&
|
|
37
|
+
typeof record.approvalId === 'string') {
|
|
38
|
+
agentRuntimeStore
|
|
39
|
+
.getState()
|
|
40
|
+
.sendDecision(record.approvalId, record.approved);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The conversation's items in the balloon, drawn by the chat's own
|
|
46
|
+
* components (`ChatMessageList`): markdown, code blocks, tool calls and
|
|
47
|
+
* their approvals, notes, the three dots — in the compact density, or the
|
|
48
|
+
* chat's own when drawn large.
|
|
49
|
+
*/
|
|
50
|
+
export function BalloonChatItems({ items, density = 'compact', waiting = false, }) {
|
|
51
|
+
const end = useRef(null);
|
|
52
|
+
return (_jsx(Box, { "data-balloon-chat-items": "", sx: { minWidth: 0 }, children: _jsx(ChatMessageList, { displayItems: items, isLoading: waiting, isStreaming: false, showLoadingIndicator: waiting, hideMessagesAfterToolUI: false, avatarConfig: BALLOON_AVATARS, padding: 0, onRespond: respondInBalloon, messagesEndRef: end, emptyContent: waiting ? _jsx(TypingDots, { size: 6 }) : null, density: density }) }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The tool being called, in the balloon: the chat's tool call card, compact,
|
|
56
|
+
* with its runtime's own words (`Asking Accounting…`) as its summary.
|
|
26
57
|
* Seen, not read out: {@link ToolLineAnnouncer} says it once per change.
|
|
27
58
|
*/
|
|
28
|
-
export function
|
|
29
|
-
|
|
30
|
-
const marks = line.tool ? marksOfToolCall(line.tool, undefined) : null;
|
|
31
|
-
const Icon = line.phase === 'done'
|
|
32
|
-
? CheckIcon
|
|
33
|
-
: line.phase === 'failed'
|
|
34
|
-
? XIcon
|
|
35
|
-
: ToolsIcon;
|
|
36
|
-
return (_jsxs(Box, { as: "span", "aria-hidden": "true", "data-balloon-tool": line.phase, "data-balloon-tool-name": line.tool || undefined, sx: {
|
|
37
|
-
display: 'inline-flex',
|
|
38
|
-
alignItems: 'center',
|
|
39
|
-
gap: 1,
|
|
40
|
-
minWidth: 0,
|
|
41
|
-
maxWidth: '100%',
|
|
42
|
-
color: line.phase === 'failed' ? 'danger.fg' : 'fg.default',
|
|
43
|
-
}, children: [_jsx(Box, { as: "span", sx: {
|
|
44
|
-
display: 'inline-flex',
|
|
45
|
-
flexShrink: 0,
|
|
46
|
-
color: PHASE_COLOR[line.phase],
|
|
47
|
-
}, children: line.phase === 'running' && hasMark(marks) ? (_jsx(SpecMark, { icon: marks?.icon, emoji: marks?.emoji, size: 14 })) : (_jsx(Icon, { size: 14 })) }), _jsxs(Box, { as: "span", sx: {
|
|
48
|
-
minWidth: 0,
|
|
49
|
-
overflow: 'hidden',
|
|
50
|
-
textOverflow: 'ellipsis',
|
|
51
|
-
whiteSpace: 'nowrap',
|
|
52
|
-
}, children: [before, name ? (_jsx(Box, { as: "strong", sx: { fontWeight: 'semibold' }, children: name })) : null, after] })] }));
|
|
59
|
+
export function BalloonToolCall({ line, waiting = false, }) {
|
|
60
|
+
return (_jsx(Box, { "aria-hidden": "true", "data-balloon-tool": line.phase, "data-balloon-tool-name": line.tool || undefined, sx: { minWidth: 0, width: '100%' }, children: _jsx(BalloonChatItems, { items: [displayItemOfLine(line)], waiting: waiting }) }));
|
|
53
61
|
}
|
|
54
62
|
/**
|
|
55
63
|
* What a screen reader hears of the tool lines, politely: once when a call
|
|
@@ -129,12 +137,26 @@ export function ConversationBalloonHeader({ count, }) {
|
|
|
129
137
|
* done — the tool line while a tool runs, else the words — cut to a few
|
|
130
138
|
* lines, never scrolled; and what goes with them (`attachment`).
|
|
131
139
|
*/
|
|
132
|
-
export function CurrentBalloonBody({ text, tool, busy = false, speaking = false, attachment, }) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
export function CurrentBalloonBody({ text, tool, busy = false, speaking = false, attachment, waiting = false, whole = false, fullText, onOverflow, }) {
|
|
141
|
+
const textRef = useRef(null);
|
|
142
|
+
useLayoutEffect(() => {
|
|
143
|
+
const element = textRef.current;
|
|
144
|
+
if (!element || whole || !onOverflow) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
onOverflow(element.scrollHeight > element.clientHeight + 1);
|
|
148
|
+
}, [text, whole, onOverflow]);
|
|
149
|
+
return (_jsxs(Box, { "data-balloon-current": "", sx: { display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0 }, children: [_jsx(BalloonNow, { busy: busy }), tool ? (_jsx(BalloonToolCall, { line: tool, waiting: waiting })) : text ? (_jsxs(Box, { "aria-live": "polite", "aria-busy": speaking, ref: textRef, "data-balloon-current-text": whole ? 'whole' : '', sx: whole
|
|
150
|
+
? {
|
|
151
|
+
maxHeight: 240,
|
|
152
|
+
overflowY: 'auto',
|
|
153
|
+
overflowWrap: 'anywhere',
|
|
154
|
+
pr: 1,
|
|
155
|
+
}
|
|
156
|
+
: {
|
|
157
|
+
// About four lines; *more* shows the rest.
|
|
158
|
+
maxHeight: '5.6em',
|
|
159
|
+
overflow: 'hidden',
|
|
160
|
+
overflowWrap: 'anywhere',
|
|
161
|
+
}, children: [_jsx(ChatMarkdown, { density: "compact", text: whole ? (fullText ?? text) : text }), waiting && (_jsx(Box, { as: "span", sx: { ml: 2, display: 'inline-flex' }, children: _jsx(TypingDots, { size: 5 }) }))] })) : waiting ? (_jsx(TypingDots, { size: 6 })) : null, attachment, _jsx(ToolLineAnnouncer, { line: tool })] }));
|
|
140
162
|
}
|
|
@@ -19,8 +19,16 @@ export interface BalloonVisual {
|
|
|
19
19
|
id: string;
|
|
20
20
|
/** What it is: `Accounting's notebook`; the dialog's title. */
|
|
21
21
|
title: string;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The visual, large: drawn only once it is expanded — in the page's
|
|
24
|
+
* target, or in a dialog, which already shows `title`.
|
|
25
|
+
*/
|
|
26
|
+
render: (place: 'target' | 'overlay') => ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Large, it shrinks back into the balloon (*Shrink*) rather than closes:
|
|
29
|
+
* the balloon's own content drawn large, such as its history.
|
|
30
|
+
*/
|
|
31
|
+
shrink?: boolean;
|
|
24
32
|
}
|
|
25
33
|
/** Where an expanded visual goes: an element of the page, or none (a dialog). */
|
|
26
34
|
export type BalloonExpandTarget = RefObject<HTMLElement | null>;
|
|
@@ -3,7 +3,7 @@ import { createContext, useContext } from 'react';
|
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { Button, Dialog, IconButton } from '@primer/react';
|
|
5
5
|
import { Box } from '@datalayer/primer-addons';
|
|
6
|
-
import { ScreenFullIcon, XIcon } from '@primer/octicons-react';
|
|
6
|
+
import { ScreenFullIcon, ScreenNormalIcon, XIcon, } from '@primer/octicons-react';
|
|
7
7
|
/**
|
|
8
8
|
* Expands the visual of the balloon it is in: what its compact form (a
|
|
9
9
|
* notebook's preview) calls when it is clicked. None outside such a balloon.
|
|
@@ -29,14 +29,15 @@ export function ExpandedVisual({ visual, target, onClose, }) {
|
|
|
29
29
|
if (!element) {
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
|
-
return createPortal(_jsxs(Box, { "data-balloon-expanded": "target", "data-balloon-visual": visual.id, sx: { position: 'relative', minWidth: 0 }, children: [_jsx(Box, { sx: { position: 'absolute', top: 0, right: 0, zIndex: 1 }, children: _jsx(IconButton, { icon: XIcon, size: "small", variant: "invisible", "aria-label":
|
|
32
|
+
return createPortal(_jsxs(Box, { "data-balloon-expanded": "target", "data-balloon-visual": visual.id, sx: { position: 'relative', minWidth: 0 }, children: [_jsx(Box, { sx: { position: 'absolute', top: 0, right: 0, zIndex: 1 }, children: _jsx(IconButton, { icon: visual.shrink ? ScreenNormalIcon : XIcon, size: "small", variant: "invisible", "aria-label": `${visual.shrink ? 'Shrink' : 'Close'} ${visual.title}`, "data-balloon-expanded-close": "", ...(visual.shrink ? { 'data-balloon-shrink': '' } : {}), onClick: onClose }) }), visual.render('target')] }), element);
|
|
33
33
|
}
|
|
34
|
+
// The dialog's × (and Esc) is what shrinks it back: no Shrink of its own.
|
|
34
35
|
return (_jsx(Dialog, { title: visual.title, onClose: onClose, sx: {
|
|
35
36
|
width: EXPANDED_DIALOG_SIZE.width,
|
|
36
37
|
maxWidth: EXPANDED_DIALOG_SIZE.width,
|
|
37
38
|
height: EXPANDED_DIALOG_SIZE.height,
|
|
38
39
|
maxHeight: EXPANDED_DIALOG_SIZE.height,
|
|
39
|
-
}, children: _jsx(Box, { "data-balloon-expanded": "overlay", "data-balloon-visual": visual.id, sx: { minWidth: 0 }, children: visual.render() }) }));
|
|
40
|
+
}, children: _jsx(Box, { "data-balloon-expanded": "overlay", "data-balloon-visual": visual.id, sx: { minWidth: 0 }, children: visual.render('overlay') }) }));
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
43
|
* Brings an expanded visual into view in its target: scrolled to, and the
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* *Code Sandbox Details…*, from the assistant's menu: where the agent's
|
|
3
|
+
* code runs, and what is in it.
|
|
4
|
+
*
|
|
5
|
+
* A compact summary first — the kind and where, its URL (without its
|
|
6
|
+
* tokens), the kernel (name, id, language, status), a cloud runtime's uid,
|
|
7
|
+
* environment and time left, a local server's agent, Pyodide's version and
|
|
8
|
+
* packages, when it started and was last active — each field shown only
|
|
9
|
+
* when it is known. Then the kernel's variables: jupyter-react's
|
|
10
|
+
* `KernelVariables`, with its renderers, ipywidgets drawn live where the
|
|
11
|
+
* page talks to the kernel.
|
|
12
|
+
*
|
|
13
|
+
* How the variables are read, by what the page can reach:
|
|
14
|
+
* - a kernel connection the host holds (the browser's Pyodide kernel):
|
|
15
|
+
* listed live, refreshed after each execution;
|
|
16
|
+
* - a Jupyter server and the sandbox's kernel id (a local server, a cloud
|
|
17
|
+
* runtime): a connection to that kernel is opened here, and closed with
|
|
18
|
+
* the dialog;
|
|
19
|
+
* - neither (an `eval` sandbox, a provider's, a server whose token the page
|
|
20
|
+
* does not hold): the agent-runtimes server runs the same snippet in the
|
|
21
|
+
* agent's sandbox (`POST /api/v1/sandbox/execute`), listed on demand.
|
|
22
|
+
*
|
|
23
|
+
* Loaded when it is first opened, never with the character.
|
|
24
|
+
*
|
|
25
|
+
* @module chat/assistant/SandboxDetailsDialog
|
|
26
|
+
*/
|
|
27
|
+
import type { JSX } from 'react';
|
|
28
|
+
import { type AssistantSandbox } from './assistantDetails';
|
|
29
|
+
export type SandboxDetailsDialogProps = {
|
|
30
|
+
sandbox: AssistantSandbox;
|
|
31
|
+
/** The agent's name, for the title. */
|
|
32
|
+
name: string;
|
|
33
|
+
onClose: () => void;
|
|
34
|
+
};
|
|
35
|
+
/** Pyodide's version and loaded packages, asked of the kernel. */
|
|
36
|
+
type PyodideFacts = {
|
|
37
|
+
version?: string;
|
|
38
|
+
python?: string;
|
|
39
|
+
packages: string[];
|
|
40
|
+
};
|
|
41
|
+
/** Pyodide's facts out of what the snippet printed. */
|
|
42
|
+
export declare function parsePyodideFacts(stdout: string): PyodideFacts | undefined;
|
|
43
|
+
export declare function SandboxDetailsDialog({ sandbox, name, onClose, }: SandboxDetailsDialogProps): JSX.Element;
|
|
44
|
+
export default SandboxDetailsDialog;
|