@datalayer/agent-runtimes 1.3.73 → 1.3.74
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/chat/ChatFloating.d.ts +9 -2
- package/lib/chat/ChatFloating.js +30 -9
- package/lib/chat/assistant/AssistantStage.d.ts +29 -5
- package/lib/chat/assistant/AssistantStage.js +20 -2
- package/lib/chat/assistant/BalloonVisual.d.ts +57 -0
- package/lib/chat/assistant/BalloonVisual.js +58 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +22 -2
- package/lib/chat/assistant/SpeechBalloon.js +61 -7
- package/lib/chat/assistant/state.d.ts +19 -0
- package/lib/chat/assistant/state.js +44 -17
- package/lib/components/teams/A2ATeamGraph.d.ts +12 -2
- package/lib/components/teams/A2ATeamGraph.js +14 -5
- package/lib/components/teams/NotebookPreview.d.ts +11 -2
- package/lib/components/teams/NotebookPreview.js +6 -3
- package/lib/components/teams/TeamNotebook.d.ts +7 -0
- package/lib/components/teams/TeamNotebook.js +13 -1
- package/lib/examples/AgentA2ATeamExample.d.ts +3 -2
- package/lib/examples/AgentA2ATeamExample.js +6 -2
- package/lib/examples/{ChatAssistantExample.d.ts → AssistantExample.d.ts} +2 -2
- package/lib/examples/{ChatAssistantExample.js → AssistantExample.js} +7 -5
- package/lib/examples/{ChatAssistantGalleryExample.d.ts → AssistantGalleryExample.d.ts} +2 -2
- package/lib/examples/{ChatAssistantGalleryExample.js → AssistantGalleryExample.js} +45 -13
- package/lib/examples/example-selector.js +4 -2
- package/lib/examples/exampleGroups.d.ts +9 -0
- package/lib/examples/exampleGroups.js +56 -0
- package/lib/examples/index.d.ts +1 -1
- package/lib/examples/index.js +1 -1
- package/lib/examples/main.js +3 -43
- package/lib/examples/utils/AssistantGalleryGrid.d.ts +6 -1
- package/lib/examples/utils/AssistantGalleryGrid.js +16 -3
- package/lib/examples/utils/assistantGallery.d.ts +51 -2
- package/lib/examples/utils/assistantGallery.js +32 -7
- package/package.json +1 -1
|
@@ -27,6 +27,7 @@ import { type Edge, type EdgeProps, type Viewport } from '@xyflow/react';
|
|
|
27
27
|
import '@xyflow/react/dist/base.css';
|
|
28
28
|
import { type A2ATeamCall, type A2ATeamConnection, type A2ATeamFlow } from './a2aTeamFlow';
|
|
29
29
|
import type { A2ATeamPersona } from './useA2ATeam';
|
|
30
|
+
import type { BalloonExpandTarget } from '../../chat/assistant/BalloonVisual';
|
|
30
31
|
import type { BalloonDisplay } from '../../chat/assistant/toolLine';
|
|
31
32
|
/** One member of the team, as the graph draws it. */
|
|
32
33
|
export type A2ATeamGraphMember = {
|
|
@@ -51,10 +52,19 @@ export type A2ATeamGraphMember = {
|
|
|
51
52
|
*/
|
|
52
53
|
balloonDisplay?: BalloonDisplay;
|
|
53
54
|
/**
|
|
54
|
-
* The notebook in its balloon was clicked: by default,
|
|
55
|
-
* `
|
|
55
|
+
* The notebook in its balloon was clicked: by default, it is expanded —
|
|
56
|
+
* into `expandTarget`, scrolled to and focused, or in a dialog.
|
|
56
57
|
*/
|
|
57
58
|
onNotebookOpen?: () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Where the notebook it was given runs (`TeamNotebook`, editable, on the
|
|
61
|
+
* browser sandbox): an element of the page — the area under the graph —
|
|
62
|
+
* where it is drawn as it arrives and when it is expanded; without one,
|
|
63
|
+
* *Expand* opens it in a dialog over the page.
|
|
64
|
+
*/
|
|
65
|
+
expandTarget?: BalloonExpandTarget;
|
|
66
|
+
/** What its notebook is called: `Accounting's notebook`. */
|
|
67
|
+
notebookTitle?: string;
|
|
58
68
|
};
|
|
59
69
|
export type A2ATeamGraphProps = {
|
|
60
70
|
/** The member that asks: drawn on the left. */
|
|
@@ -7,7 +7,8 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
7
7
|
import { AssistantStage } from '../../chat/assistant/AssistantStage';
|
|
8
8
|
import { SpecMark } from '../../chat/marks/SpecMark';
|
|
9
9
|
import { toolWords, } from './a2aTeamFlow';
|
|
10
|
-
import { NotebookPreview
|
|
10
|
+
import { NotebookPreview } from './NotebookPreview';
|
|
11
|
+
import { notebookBalloonVisual } from './TeamNotebook';
|
|
11
12
|
import { toolLineText } from '../../chat/assistant/toolLine';
|
|
12
13
|
/** The graph's own coordinates: the members, and room above them for their balloons. */
|
|
13
14
|
const NODE_WIDTH = 200;
|
|
@@ -53,6 +54,15 @@ const MemberNode = memo(function MemberNode({ data, }) {
|
|
|
53
54
|
return null;
|
|
54
55
|
}
|
|
55
56
|
const { persona } = member;
|
|
57
|
+
// A notebook it was given: read-only in its balloon, and its large visual
|
|
58
|
+
// the one that runs, editable — under the graph, or in a dialog.
|
|
59
|
+
const notebookTitle = member.notebookTitle ?? (persona.notebook?.name || 'The notebook');
|
|
60
|
+
const given = persona.notebook
|
|
61
|
+
? {
|
|
62
|
+
attachment: (_jsx(NotebookPreview, { notebook: persona.notebook.data, title: notebookTitle, maxHeight: NOTEBOOK_PREVIEW_HEIGHT, onOpen: member.onNotebookOpen, openLabel: member.expandTarget ? 'Open it below to run it' : undefined })),
|
|
63
|
+
visual: notebookBalloonVisual(persona.notebook, notebookTitle),
|
|
64
|
+
}
|
|
65
|
+
: {};
|
|
56
66
|
return (_jsxs(Box
|
|
57
67
|
// React Flow lets the pointer through a node that is neither dragged
|
|
58
68
|
// nor selected (`pointer-events: none` on its wrapper): the character,
|
|
@@ -81,6 +91,7 @@ const MemberNode = memo(function MemberNode({ data, }) {
|
|
|
81
91
|
text: toolLineText(persona.tool),
|
|
82
92
|
tool: persona.tool,
|
|
83
93
|
busy: persona.state !== 'idle',
|
|
94
|
+
...given,
|
|
84
95
|
}
|
|
85
96
|
: persona.saying
|
|
86
97
|
? {
|
|
@@ -90,11 +101,9 @@ const MemberNode = memo(function MemberNode({ data, }) {
|
|
|
90
101
|
busy: persona.state === 'thinking' ||
|
|
91
102
|
persona.state === 'working' ||
|
|
92
103
|
persona.state === 'speaking',
|
|
93
|
-
|
|
94
|
-
// runs is under the graph.
|
|
95
|
-
attachment: persona.notebook ? (_jsx(NotebookPreview, { notebook: persona.notebook.data, title: persona.notebook.name || 'The notebook', maxHeight: NOTEBOOK_PREVIEW_HEIGHT, onOpen: member.onNotebookOpen ?? focusTeamNotebook })) : undefined,
|
|
104
|
+
...given,
|
|
96
105
|
}
|
|
97
|
-
: undefined, insist: persona.insist, onDismiss: away => member.onAway?.(away !== 'none'),
|
|
106
|
+
: undefined, expandTarget: member.expandTarget, expandOnArrival: !!member.expandTarget, insist: persona.insist, onDismiss: away => member.onAway?.(away !== 'none'),
|
|
98
107
|
// A member of the graph: what is clicked around it is the graph.
|
|
99
108
|
stayPut: true })) }), _jsxs(Text, { sx: { fontWeight: 600, textAlign: 'center' }, children: [member.emoji ? `${member.emoji} ` : '', member.name] }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', textAlign: 'center' }, "data-member-where": "", children: member.where }), _jsx(Handle, { type: "source", position: Position.Right, style: HANDLE(size), isConnectable: false }), member.connections?.length ? (_jsx(Handle, { id: "connections", type: "source", position: Position.Bottom, style: { ...HANDLE(size), top: 'auto', bottom: 0 }, isConnectable: false })) : null] }));
|
|
100
109
|
});
|
|
@@ -27,11 +27,20 @@ export type NotebookPreviewProps = {
|
|
|
27
27
|
title: string;
|
|
28
28
|
/** How tall it is, in pixels; it scrolls within. */
|
|
29
29
|
maxHeight?: number;
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Takes the reader to the notebook that runs; the line under it says so.
|
|
32
|
+
* Unsaid, in a balloon that carries the notebook as its large visual, a
|
|
33
|
+
* click expands it (`BalloonVisual`).
|
|
34
|
+
*/
|
|
31
35
|
onOpen?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* The line under it, which opens it: `Open it below to run it`. Shown
|
|
38
|
+
* when `onOpen` or this is given — in a balloon, its *Expand* says it.
|
|
39
|
+
*/
|
|
40
|
+
openLabel?: string;
|
|
32
41
|
};
|
|
33
42
|
/** A notebook, read-only and small, with the way to the one that runs. */
|
|
34
|
-
export declare function NotebookPreview({ notebook, title, maxHeight, onOpen, }: NotebookPreviewProps): JSX.Element | null;
|
|
43
|
+
export declare function NotebookPreview({ notebook, title, maxHeight, onOpen: onOpenGiven, openLabel, }: NotebookPreviewProps): JSX.Element | null;
|
|
35
44
|
/** Where the notebook that runs is: `TeamNotebook` marks its section. */
|
|
36
45
|
export declare const TEAM_NOTEBOOK_SELECTOR = "[data-team-notebook]";
|
|
37
46
|
/** Takes the reader to the notebook that runs: scrolled to, and focused. */
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Suspense, lazy } from 'react';
|
|
3
3
|
import { Text } from '@primer/react';
|
|
4
4
|
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { useBalloonExpand } from '../../chat/assistant/BalloonVisual';
|
|
5
6
|
/** jupyter-react's notebook, read-only, with no kernel: loaded when a notebook is drawn. */
|
|
6
7
|
const NotebookPreviewView = lazy(() => import('./NotebookPreviewView'));
|
|
7
8
|
/** The document a notebook artifact carries, or none when it is not one. */
|
|
@@ -24,7 +25,9 @@ export function notebookDocument(data) {
|
|
|
24
25
|
return undefined;
|
|
25
26
|
}
|
|
26
27
|
/** A notebook, read-only and small, with the way to the one that runs. */
|
|
27
|
-
export function NotebookPreview({ notebook, title, maxHeight = 220, onOpen, }) {
|
|
28
|
+
export function NotebookPreview({ notebook, title, maxHeight = 220, onOpen: onOpenGiven, openLabel, }) {
|
|
29
|
+
const expand = useBalloonExpand();
|
|
30
|
+
const onOpen = onOpenGiven ?? expand ?? undefined;
|
|
28
31
|
const doc = notebookDocument(notebook);
|
|
29
32
|
if (!doc) {
|
|
30
33
|
return null;
|
|
@@ -38,7 +41,7 @@ export function NotebookPreview({ notebook, title, maxHeight = 220, onOpen, }) {
|
|
|
38
41
|
borderRadius: 2,
|
|
39
42
|
bg: 'canvas.default',
|
|
40
43
|
cursor: onOpen ? 'pointer' : 'default',
|
|
41
|
-
}, children: _jsx(Suspense, { fallback: _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 0, m: 2 }, children: "Opening the notebook\u2026" }), children: _jsx(NotebookPreviewView, { nbformat: doc, height: maxHeight }) }) }), onOpen ? (_jsx(Text, { as: "button", type: "button", onClick: onOpen, "data-notebook-preview-open": "", sx: {
|
|
44
|
+
}, children: _jsx(Suspense, { fallback: _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 0, m: 2 }, children: "Opening the notebook\u2026" }), children: _jsx(NotebookPreviewView, { nbformat: doc, height: maxHeight }) }) }), onOpen && (onOpenGiven || openLabel) ? (_jsx(Text, { as: "button", type: "button", onClick: onOpen, "data-notebook-preview-open": "", sx: {
|
|
42
45
|
alignSelf: 'flex-start',
|
|
43
46
|
p: 0,
|
|
44
47
|
border: 0,
|
|
@@ -47,7 +50,7 @@ export function NotebookPreview({ notebook, title, maxHeight = 220, onOpen, }) {
|
|
|
47
50
|
fontSize: 0,
|
|
48
51
|
cursor: 'pointer',
|
|
49
52
|
'&:hover': { textDecoration: 'underline' },
|
|
50
|
-
}, children:
|
|
53
|
+
}, children: openLabel ?? 'Open it to run it' })) : null] }));
|
|
51
54
|
}
|
|
52
55
|
/** Where the notebook that runs is: `TeamNotebook` marks its section. */
|
|
53
56
|
export const TEAM_NOTEBOOK_SELECTOR = '[data-team-notebook]';
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { JSX } from 'react';
|
|
19
19
|
import type { A2APeerArtifact } from '../../runtimes/browser/a2aPeer';
|
|
20
|
+
import type { BalloonVisual } from '../../chat/assistant/BalloonVisual';
|
|
20
21
|
export type TeamNotebookProps = {
|
|
21
22
|
/** The notebook, as the peer gave it: its `data` is the nbformat document. */
|
|
22
23
|
notebook: A2APeerArtifact;
|
|
@@ -29,4 +30,10 @@ export type TeamNotebookProps = {
|
|
|
29
30
|
export declare function notebookFileName(notebook: A2APeerArtifact): string;
|
|
30
31
|
/** A notebook a member of the team gave, under its title, to run in the page. */
|
|
31
32
|
export declare function TeamNotebook({ notebook, title, height, }: TeamNotebookProps): JSX.Element;
|
|
33
|
+
/**
|
|
34
|
+
* A notebook as a balloon's large visual: read-only in the balloon, and,
|
|
35
|
+
* expanded, this notebook — editable, run on the browser sandbox, loaded
|
|
36
|
+
* only then.
|
|
37
|
+
*/
|
|
38
|
+
export declare function notebookBalloonVisual(notebook: A2APeerArtifact, title: string, height?: number): BalloonVisual;
|
|
32
39
|
export default TeamNotebook;
|
|
@@ -18,7 +18,7 @@ export function notebookFileName(notebook) {
|
|
|
18
18
|
/** A notebook a member of the team gave, under its title, to run in the page. */
|
|
19
19
|
export function TeamNotebook({ notebook, title, height = 560, }) {
|
|
20
20
|
return (_jsxs(Box, { as: "section", "aria-label": title, "data-team-notebook": "",
|
|
21
|
-
// Focused when the notebook in a balloon is clicked (`
|
|
21
|
+
// Focused when the notebook in a balloon is clicked (`focusExpanded`).
|
|
22
22
|
tabIndex: -1, sx: {
|
|
23
23
|
display: 'flex',
|
|
24
24
|
flexDirection: 'column',
|
|
@@ -34,4 +34,16 @@ export function TeamNotebook({ notebook, title, height = 560, }) {
|
|
|
34
34
|
},
|
|
35
35
|
}, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, m: 0 }, children: title }), _jsx(Suspense, { fallback: _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Opening the notebook\u2026" }), children: _jsx(TeamNotebookView, { notebook: notebook, fileName: notebookFileName(notebook), height: height }) })] }));
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* A notebook as a balloon's large visual: read-only in the balloon, and,
|
|
39
|
+
* expanded, this notebook — editable, run on the browser sandbox, loaded
|
|
40
|
+
* only then.
|
|
41
|
+
*/
|
|
42
|
+
export function notebookBalloonVisual(notebook, title, height) {
|
|
43
|
+
return {
|
|
44
|
+
id: `notebook:${notebook.filename ?? notebook.name}`,
|
|
45
|
+
title,
|
|
46
|
+
render: () => (_jsx(TeamNotebook, { notebook: notebook, title: title, height: height })),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
37
49
|
export default TeamNotebook;
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
* Sales' balloon shows it read-only (`NotebookPreview`: no kernel), and it
|
|
25
25
|
* opens right under the team, in place of the report, on the browser
|
|
26
26
|
* sandbox — a Pyodide kernel in this page — to run, change and download
|
|
27
|
-
* (`TeamNotebook`, loaded only then)
|
|
28
|
-
*
|
|
27
|
+
* (`TeamNotebook`, loaded only then): the graph draws it into the area
|
|
28
|
+
* under it (`expandTarget`), and a click on the one in the balloon, or its
|
|
29
|
+
* *Expand*, comes to it.
|
|
29
30
|
*
|
|
30
31
|
* Where Accounting is: `VITE_A2A_ACCOUNTING_URL`, else the examples' local
|
|
31
32
|
* server (`…:8765/api/v1/a2a/agents/accounting`), which answers this machine
|
|
@@ -10,7 +10,7 @@ import { useExampleAgentRuntimesUrl } from './utils/useExampleAgentRuntimesUrl';
|
|
|
10
10
|
import { useBrowserInference } from '../hooks/useBrowserInference';
|
|
11
11
|
import { readJwtClaims, tokenLifetimeMs, useAnonymousSessionStore, } from '../runtimes/browser/anonymousToken';
|
|
12
12
|
import { connectA2APeer } from '../runtimes/browser/a2aPeer';
|
|
13
|
-
import { A2ATeamGraph, NOTEBOOK_AND_WORDS,
|
|
13
|
+
import { A2ATeamGraph, NOTEBOOK_AND_WORDS, teamConnectionsOf, useA2ATeam, } from '../components/teams';
|
|
14
14
|
import { ACCOUNTING_APP_0_0_1, SALES_APP_0_0_1 } from '../specs/apps';
|
|
15
15
|
import { SALES_AND_ACCOUNTING_TEAM_SPEC_0_0_1 } from '../specs/teams/teams';
|
|
16
16
|
const TEAM = SALES_AND_ACCOUNTING_TEAM_SPEC_0_0_1;
|
|
@@ -49,6 +49,8 @@ function AgentA2ATeam() {
|
|
|
49
49
|
const [peerError, setPeerError] = useState(null);
|
|
50
50
|
const [draft, setDraft] = useState('');
|
|
51
51
|
const composer = useRef(null);
|
|
52
|
+
// Under the graph: where the notebook Accounting gives runs.
|
|
53
|
+
const notebookArea = useRef(null);
|
|
52
54
|
useEffect(() => {
|
|
53
55
|
let current = true;
|
|
54
56
|
setPeer(null);
|
|
@@ -103,6 +105,8 @@ function AgentA2ATeam() {
|
|
|
103
105
|
persona: team.entryPersona,
|
|
104
106
|
onToggle: () => composer.current?.focus(),
|
|
105
107
|
onAway: team.setEntryAway,
|
|
108
|
+
expandTarget: notebookArea,
|
|
109
|
+
notebookTitle: `${ACCOUNTING.name}\u2019s notebook`,
|
|
106
110
|
}, peer: {
|
|
107
111
|
id: ACCOUNTING.id,
|
|
108
112
|
name: ACCOUNTING.name,
|
|
@@ -112,7 +116,7 @@ function AgentA2ATeam() {
|
|
|
112
116
|
persona: team.peerPersona,
|
|
113
117
|
onAway: team.setPeerAway,
|
|
114
118
|
connections: ACCOUNTING_CONNECTIONS,
|
|
115
|
-
}, flow: team.flow, calls: team.calls, connected: peer !== null, label: peer ? `A2A · ${peer.skill.name}` : 'A2A · not connected' }) }),
|
|
119
|
+
}, flow: team.flow, calls: team.calls, connected: peer !== null, label: peer ? `A2A · ${peer.skill.name}` : 'A2A · not connected' }) }), _jsx(Box, { ref: notebookArea, sx: { mb: team.notebook ? 3 : 0 }, "data-team-notebook-placement": "under-graph" }), _jsxs(Box, { sx: { display: 'flex', gap: 3, flexWrap: 'wrap' }, children: [_jsxs(Box, { sx: { flex: '1 1 420px', minWidth: 0 }, children: [_jsxs(Heading, { as: "h3", sx: { fontSize: 2, mb: 2 }, children: ["Ask ", SALES.name] }), _jsx(Box, { sx: { display: 'flex', gap: 2, flexWrap: 'wrap', mb: 2 }, children: (SALES.interface.starters ?? []).map(starter => (_jsx(Button, { size: "small", disabled: !team.ready || team.busy, onClick: () => send(starter.message), children: starter.label }, starter.label))) }), _jsx(Textarea, { ref: composer, block: true, rows: 3, value: draft, placeholder: "Ask for a financial report\u2026", onChange: event => setDraft(event.target.value), onKeyDown: event => {
|
|
116
120
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
117
121
|
event.preventDefault();
|
|
118
122
|
send(draft);
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
* and so are the characters clippy.js publishes, fetched when picked.
|
|
11
11
|
*/
|
|
12
12
|
import React from 'react';
|
|
13
|
-
declare const
|
|
14
|
-
export default
|
|
13
|
+
declare const AssistantExample: React.FC;
|
|
14
|
+
export default AssistantExample;
|
|
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
*/
|
|
17
17
|
import { useEffect, useRef, useState } from 'react';
|
|
18
18
|
import { buildReactorFromPlugins } from '@datalayer/reactor';
|
|
19
|
-
import { Button, Heading, Text } from '@primer/react';
|
|
19
|
+
import { Button, Heading, Text, ToggleSwitch } from '@primer/react';
|
|
20
20
|
import { Box } from '@datalayer/primer-addons';
|
|
21
21
|
import { ThemedProvider } from './utils/themedProvider';
|
|
22
22
|
import { ChatFloating } from '../chat';
|
|
@@ -66,7 +66,7 @@ async function readPicked(files) {
|
|
|
66
66
|
soundsJs: soundsJs ? await soundsJs.text() : undefined,
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
const
|
|
69
|
+
const AssistantExample = () => {
|
|
70
70
|
const [character, setCharacter] = useState('paperclip');
|
|
71
71
|
// A character chosen by id is the one its plugin contributes.
|
|
72
72
|
const drawn = typeof character === 'string'
|
|
@@ -131,13 +131,15 @@ const ChatAssistantExample = () => {
|
|
|
131
131
|
};
|
|
132
132
|
const isClippyJs = typeof character !== 'string' &&
|
|
133
133
|
Object.values(clippyJs).includes(character);
|
|
134
|
-
return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: { minHeight: '100vh', bg: 'canvas.default', p: 4 }, children: [_jsxs(Box, { sx: { maxWidth: 720, mx: 'auto' }, children: [_jsx(Heading, { as: "h1", sx: { mb: 2 }, children: "
|
|
134
|
+
return (_jsx(ThemedProvider, { children: _jsxs(Box, { sx: { minHeight: '100vh', bg: 'canvas.default', p: 4 }, children: [_jsxs(Box, { sx: { maxWidth: 720, mx: 'auto' }, children: [_jsx(Heading, { as: "h1", sx: { mb: 2 }, children: "Assistant" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', mb: 3 }, children: "The chat as a character on the page. It greets you, acts out what the agent is doing, and says the agent\u2019s words in a balloon while the conversation is closed. Click it to talk, drag it to move it, hover it to send it away." }), _jsxs(Box, { sx: { display: 'flex', gap: 2, flexWrap: 'wrap' }, children: [CATALOGUE.map(option => (_jsx(Button, { variant: option.id === character ? 'primary' : 'default', onClick: () => {
|
|
135
135
|
setCharacter(option.id);
|
|
136
136
|
setLoadError(undefined);
|
|
137
137
|
}, "aria-pressed": option.id === character, "data-assistant-character": option.id, children: option.name }, option.id))), pixel && (_jsxs(Button, { variant: character === pixel ? 'primary' : 'default', onClick: () => {
|
|
138
138
|
setCharacter(pixel);
|
|
139
139
|
setLoadError(undefined);
|
|
140
|
-
}, "aria-pressed": character === pixel, "data-assistant-character": "sprite", children: [pixel.name, " (sprite)"] }))] }), _jsxs(Box, { as: "section", sx: { mt: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 2, mb: 1 }, children: "The balloon" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 2 }, children: [_jsx("strong", { children: "History" }), ":
|
|
140
|
+
}, "aria-pressed": character === pixel, "data-assistant-character": "sprite", children: [pixel.name, " (sprite)"] }))] }), _jsxs(Box, { as: "section", sx: { mt: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 2, mb: 1 }, children: "The balloon" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 2 }, children: [_jsx("strong", { children: "History" }), ": the whole conversation, counted in its header \u2014 closed, every message listed and scrolled; open, the composer last. ", _jsx("strong", { children: "Current" }), ": only what it says or does now \u2014 the answer as it is written, or the tool it calls (\u201CUsing current_time\u2026\u201D) \u2014 in one compact balloon. Either way, a tool call is said in the balloon."] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, "data-assistant-balloon-toggle": "", children: [_jsx(Text, { id: "assistant-balloon-history", sx: { fontWeight: 'semibold' }, children: "History" }), _jsx(ToggleSwitch, { "aria-labelledby": "assistant-balloon-history", size: "small", checked: balloon === 'history',
|
|
141
|
+
// Controlled, Primer's switch flips only through its click.
|
|
142
|
+
onClick: () => setBalloon(balloon === 'history' ? 'current' : 'history') }), _jsx(Text, { "aria-live": "polite", "data-toggle-state": "", sx: { color: 'fg.muted' }, children: balloon === 'history' ? 'History' : 'Current' })] })] }), _jsxs(Box, { as: "section", sx: { mt: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 2, mb: 1 }, children: "The clippy.js characters" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 2 }, children: ["Fetched when picked from the", ' ', _jsxs("code", { children: ["clippyjs@", CLIPPY_JS_VERSION] }), " package on jsDelivr and read as data by the same reader. They are not in this repository: clippy.js\u2019s licence covers its code only, and the characters are Microsoft\u2019s."] }), _jsx(Box, { sx: { display: 'flex', gap: 2, flexWrap: 'wrap' }, children: CLIPPY_JS_CHARACTERS.map(name => {
|
|
141
143
|
const picked = clippyJs[name] !== undefined && character === clippyJs[name];
|
|
142
144
|
return (_jsx(Button, { variant: picked ? 'primary' : 'default', onClick: () => void pickClippyJs(name), disabled: fetching !== undefined, "aria-pressed": picked, "data-assistant-character": `clippy-js-${name.toLowerCase()}`, children: fetching === name ? `${name}…` : name }, name));
|
|
143
145
|
}) })] }), _jsxs(Box, { as: "section", sx: { mt: 4 }, children: [_jsx(Heading, { as: "h2", sx: { fontSize: 2, mb: 1 }, children: "A character you bring" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 2 }, children: ["The characters of Microsoft Office \u2014 Clippy, Merlin, Links and the others \u2014 are Microsoft\u2019s. Load only a character file you have the right to use: a Microsoft Agent ", _jsx("code", { children: ".acs" }), " file, or a clippy.js character\u2019s ", _jsx("code", { children: "agent.js" }), " and map image. It is read in this page and sent nowhere."] }), _jsx("input", { type: "file", multiple: true, accept: ".acs,.js,.png,.gif,.webp,.jpg,.jpeg", "aria-label": "Character files", onChange: async (event) => {
|
|
@@ -158,4 +160,4 @@ const ChatAssistantExample = () => {
|
|
|
158
160
|
// Ask a decision beside the composer: Jev, through this server.
|
|
159
161
|
decisions: { serverUrl: SERVER } }, typeof character === 'string' ? character : character.sprite)] }) }));
|
|
160
162
|
};
|
|
161
|
-
export default
|
|
163
|
+
export default AssistantExample;
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* Static data, no agent: it opens without a server or an account.
|
|
14
14
|
*/
|
|
15
15
|
import React from 'react';
|
|
16
|
-
declare const
|
|
17
|
-
export default
|
|
16
|
+
declare const AssistantGalleryExample: React.FC;
|
|
17
|
+
export default AssistantGalleryExample;
|
|
@@ -18,17 +18,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
* Static data, no agent: it opens without a server or an account.
|
|
19
19
|
*/
|
|
20
20
|
import { useEffect, useRef, useState } from 'react';
|
|
21
|
-
import { Button, Checkbox, FormControl, Heading, SegmentedControl, Text, } from '@primer/react';
|
|
21
|
+
import { Button, Checkbox, FormControl, Heading, SegmentedControl, Text, ToggleSwitch, } from '@primer/react';
|
|
22
22
|
import { Box, DatalayerThemeProvider, themeConfigs, } from '@datalayer/primer-addons';
|
|
23
23
|
import { ThemedProvider } from './utils/themedProvider';
|
|
24
24
|
import { useExampleThemeStore } from './utils/themeStore';
|
|
25
25
|
import { AssistantStage } from '../chat/assistant/AssistantStage';
|
|
26
26
|
import { useViewportDrag } from '../chat/useViewportDrag';
|
|
27
|
-
import { AssistantGalleryGrid, GalleryBalloons, GalleryNotebook, GalleryObstacle, Still, useGalleryCharacters, } from './utils/AssistantGalleryGrid';
|
|
28
|
-
import { GALLERY_POSES, GALLERY_POSE_LABELS, SAMPLE_NOTEBOOK_SAYING, balloonForPose, sampleApproval, sampleSaying, sampleToolLine, stateOfPose, } from './utils/assistantGallery';
|
|
27
|
+
import { AssistantGalleryGrid, GalleryBalloons, GalleryNotebook, GalleryObstacle, galleryNotebookVisual, Still, useGalleryCharacters, } from './utils/AssistantGalleryGrid';
|
|
28
|
+
import { GALLERY_POSES, GALLERY_POSE_LABELS, SAMPLE_NOTEBOOK_SAYING, balloonForPose, sampleApproval, sampleHistory, sampleSaying, sampleToolLine, stateOfPose, } from './utils/assistantGallery';
|
|
29
29
|
import { CLIPPY_JS_VERSION, useClippyJsCharacters, } from './utils/clippyJsCharacters';
|
|
30
30
|
const STAGE_SIZE = 96;
|
|
31
|
-
const PANEL_HEIGHT =
|
|
31
|
+
const PANEL_HEIGHT = 640;
|
|
32
32
|
const LEAVE_MS = 700;
|
|
33
33
|
const ARRIVE_MS = 1900;
|
|
34
34
|
const AWAY_WORDS = {
|
|
@@ -38,7 +38,7 @@ const AWAY_WORDS = {
|
|
|
38
38
|
always: 'for good',
|
|
39
39
|
};
|
|
40
40
|
/** One colour mode's panel, with the character on its own stage. */
|
|
41
|
-
function ModePanel({ mode, character, pose, presence, balloon, insist, onToggle, onDismiss, onCallBack, away, replay, display, }) {
|
|
41
|
+
function ModePanel({ mode, character, pose, presence, balloon, insist, onToggle, onDismiss, onCallBack, away, replay, display, expandTarget, }) {
|
|
42
42
|
const panelRef = useRef(null);
|
|
43
43
|
const stageRef = useRef(null);
|
|
44
44
|
const drag = useViewportDrag(stageRef, { whole: true });
|
|
@@ -84,13 +84,19 @@ function ModePanel({ mode, character, pose, presence, balloon, insist, onToggle,
|
|
|
84
84
|
position: 'absolute',
|
|
85
85
|
left: spot.left,
|
|
86
86
|
top: spot.top + STAGE_SIZE / 2 - 16,
|
|
87
|
-
}, children: [_jsxs(Button, { size: "small", onClick: onCallBack, "data-gallery-call-back": "", children: ["Call ", character.name, " back"] }), _jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 1 }, children: ["Sent away ", AWAY_WORDS[away], "."] })] })) : (_jsx(AssistantStage, { character: character.character, state: state, size: STAGE_SIZE, place: { position: 'absolute', left: spot.left, top: spot.top }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: false, onToggle: onToggle, balloon: presence === 'here' ? balloon : undefined, balloonDisplay: display, insist: insist, onDismiss: onDismiss }, `${character.id}-${replay}`)), pose === 'aside' && presence === 'here' && (_jsx(GalleryObstacle, { sx: {
|
|
87
|
+
}, children: [_jsxs(Button, { size: "small", onClick: onCallBack, "data-gallery-call-back": "", children: ["Call ", character.name, " back"] }), _jsxs(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 1 }, children: ["Sent away ", AWAY_WORDS[away], "."] })] })) : (_jsx(AssistantStage, { character: character.character, state: state, size: STAGE_SIZE, place: { position: 'absolute', left: spot.left, top: spot.top }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: false, onToggle: onToggle, balloon: presence === 'here' ? balloon : undefined, balloonDisplay: display, expandTarget: expandTarget, insist: insist, onDismiss: onDismiss }, `${character.id}-${replay}`)), pose === 'aside' && presence === 'here' && (_jsx(GalleryObstacle, { sx: {
|
|
88
88
|
left: spot.left - 12,
|
|
89
89
|
top: spot.top - 12,
|
|
90
90
|
width: STAGE_SIZE + 24,
|
|
91
91
|
height: STAGE_SIZE + 24,
|
|
92
92
|
} }))] }));
|
|
93
93
|
}
|
|
94
|
+
/** A two-way choice as a Primer toggle: its label, the switch, what is on. */
|
|
95
|
+
function GalleryToggle({ id, label, on, onChange, state, ...rest }) {
|
|
96
|
+
return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, ...rest, children: [_jsx(Text, { id: id, sx: { fontSize: 1, fontWeight: 'semibold' }, children: label }), _jsx(ToggleSwitch, { "aria-labelledby": id, size: "small", checked: on,
|
|
97
|
+
// Controlled, Primer's switch flips only through its click.
|
|
98
|
+
onClick: () => onChange(!on) }), _jsx(Text, { "aria-live": "polite", "data-toggle-state": "", sx: { fontSize: 1, color: 'fg.muted' }, children: state })] }));
|
|
99
|
+
}
|
|
94
100
|
/** The panels in the modes asked, each in the examples' theme. */
|
|
95
101
|
function InModes({ modes, stacked = false, children, }) {
|
|
96
102
|
const { theme } = useExampleThemeStore();
|
|
@@ -103,7 +109,7 @@ function InModes({ modes, stacked = false, children, }) {
|
|
|
103
109
|
flexWrap: 'wrap',
|
|
104
110
|
}, children: shown.map(mode => (_jsx(Box, { sx: { flex: '1 1 320px', minWidth: 0 }, children: _jsx(DatalayerThemeProvider, { colorMode: mode, theme: config.primerTheme, themeStyles: config.themeStyles, children: children(mode) }) }, mode))) }));
|
|
105
111
|
}
|
|
106
|
-
const
|
|
112
|
+
const AssistantGalleryExample = () => {
|
|
107
113
|
const { characters: ours, problem } = useGalleryCharacters();
|
|
108
114
|
const [withClippyJs, setWithClippyJs] = useState(false);
|
|
109
115
|
const clippyJs = useClippyJsCharacters(withClippyJs);
|
|
@@ -123,6 +129,9 @@ const ChatAssistantGalleryExample = () => {
|
|
|
123
129
|
const [display, setDisplay] = useState('current');
|
|
124
130
|
const [toolPhase, setToolPhase] = useState();
|
|
125
131
|
const [withNotebook, setWithNotebook] = useState(false);
|
|
132
|
+
// Where Expand draws the notebook: the area under the stage, or a dialog.
|
|
133
|
+
const [expandIntoPage, setExpandIntoPage] = useState(true);
|
|
134
|
+
const expandArea = useRef(null);
|
|
126
135
|
const character = characters.find(entry => entry.id === characterId) ?? characters[0];
|
|
127
136
|
// Leaving takes the goodbye's length; arriving, the greeting's.
|
|
128
137
|
useEffect(() => {
|
|
@@ -145,6 +154,21 @@ const ChatAssistantGalleryExample = () => {
|
|
|
145
154
|
setSaid(said + 1);
|
|
146
155
|
};
|
|
147
156
|
const approval = sampleApproval(() => setDecision('Approved: send_reply.'), () => setDecision('Denied: send_reply.'));
|
|
157
|
+
// History: the conversation so far, every message, as the chat holds it.
|
|
158
|
+
const history = display === 'history'
|
|
159
|
+
? [
|
|
160
|
+
...sampleHistory(saying === undefined ? said : saying + 1),
|
|
161
|
+
...(withNotebook
|
|
162
|
+
? [
|
|
163
|
+
{
|
|
164
|
+
id: 'notebook',
|
|
165
|
+
role: 'assistant',
|
|
166
|
+
text: SAMPLE_NOTEBOOK_SAYING,
|
|
167
|
+
},
|
|
168
|
+
]
|
|
169
|
+
: []),
|
|
170
|
+
]
|
|
171
|
+
: undefined;
|
|
148
172
|
const balloon = balloonForPose(pose, {
|
|
149
173
|
saying: saying === undefined
|
|
150
174
|
? withNotebook
|
|
@@ -153,7 +177,9 @@ const ChatAssistantGalleryExample = () => {
|
|
|
153
177
|
: sampleSaying(saying),
|
|
154
178
|
approval,
|
|
155
179
|
tool: toolPhase ? sampleToolLine(toolPhase) : undefined,
|
|
156
|
-
attachment: withNotebook
|
|
180
|
+
attachment: withNotebook ? _jsx(GalleryNotebook, {}) : undefined,
|
|
181
|
+
visual: withNotebook ? galleryNotebookVisual() : undefined,
|
|
182
|
+
history,
|
|
157
183
|
});
|
|
158
184
|
const insist = saying !== undefined ||
|
|
159
185
|
toolPhase !== undefined ||
|
|
@@ -165,7 +191,7 @@ const ChatAssistantGalleryExample = () => {
|
|
|
165
191
|
bg: 'canvas.default',
|
|
166
192
|
color: 'fg.default',
|
|
167
193
|
p: 4,
|
|
168
|
-
}, children: _jsxs(Box, { sx: { maxWidth: 1200, mx: 'auto' }, children: [_jsx(Heading, { as: "h1", sx: { mb: 2 }, children: "
|
|
194
|
+
}, children: _jsxs(Box, { sx: { maxWidth: 1200, mx: 'auto' }, children: [_jsx(Heading, { as: "h1", sx: { mb: 2 }, children: "Assistant gallery" }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 3, maxWidth: 760 }, children: ["Every character in every state the floating assistant acts, with its balloon, in light and dark. Datalayer\u2019s four and the owl are what the enabled plugins contribute to", ' ', _jsx("code", { children: "loop.assistant.character" }), "; Pixel is a test sprite of our own, read through the clippy.js reader. No agent is behind it: the words are a sample conversation."] }), _jsxs(Text, { as: "p", sx: { color: 'fg.muted', mb: 3, maxWidth: 760 }, children: ["The characters clippy.js publishes \u2014 Clippy, Merlin, Links, Rover and the others \u2014 are loaded on request from the", ' ', _jsxs("code", { children: ["clippyjs@", CLIPPY_JS_VERSION] }), " package on jsDelivr (about 15 MB) and read by the same reader. They are not in this repository: clippy.js\u2019s licence covers its code only, and the characters are Microsoft\u2019s."] }), _jsxs(Box, { as: "section", "aria-label": "Gallery controls", sx: {
|
|
169
195
|
display: 'grid',
|
|
170
196
|
gap: 3,
|
|
171
197
|
mb: 4,
|
|
@@ -192,7 +218,7 @@ const ChatAssistantGalleryExample = () => {
|
|
|
192
218
|
gap: 2,
|
|
193
219
|
flexWrap: 'wrap',
|
|
194
220
|
alignItems: 'center',
|
|
195
|
-
}, children: [_jsx(
|
|
221
|
+
}, children: [_jsx(GalleryToggle, { id: "gallery-balloon-history", label: "History", on: display === 'history', onChange: on => setDisplay(on ? 'history' : 'current'), state: display === 'history' ? 'History' : 'Current', "data-gallery-balloon-toggle": "" }), _jsx(GalleryToggle, { id: "gallery-expand-into-page", label: "Expand into the page", on: expandIntoPage, onChange: setExpandIntoPage, state: expandIntoPage ? 'The page area' : 'An overlay', "data-gallery-expand-toggle": "" }), _jsx(Button, { onClick: say, "data-gallery-say": "", children: "Say something" }), _jsx(Button, { "data-gallery-tool": toolPhase ?? 'none', onClick: () => setToolPhase(toolPhase === undefined
|
|
196
222
|
? 'running'
|
|
197
223
|
: toolPhase === 'running'
|
|
198
224
|
? 'done'
|
|
@@ -204,13 +230,19 @@ const ChatAssistantGalleryExample = () => {
|
|
|
204
230
|
? 'Tool done'
|
|
205
231
|
: toolPhase === 'done'
|
|
206
232
|
? 'Tool failed'
|
|
207
|
-
: 'No tool' }), _jsx(Button, { "data-gallery-notebook": withNotebook ? 'on' : 'off', "aria-pressed": withNotebook, onClick: () => setWithNotebook(!withNotebook), children: withNotebook ? 'Take the notebook' : 'Give a notebook' }), saying !== undefined && (_jsx(Button, { variant: "invisible", onClick: () => setSaying(undefined), children: "Hush" })), presence === 'away' ? (_jsx(Button, { onClick: callBack, children: "Call it back" })) : (_jsx(Button, { onClick: () => sendAway('page'), disabled: presence !== 'here', "data-gallery-send-away": "", children: "Send it away" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Drag the character to move it; hover it for its own send-away menu." }), decision && (_jsx(Text, { role: "status", sx: { fontSize: 1 }, children: decision }))] })] }))] }), problem && (_jsxs(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mb: 3 }, children: ["The test sprite could not be read: ", problem] })), clippyJs.problem && (_jsxs(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mb: 3 }, children: ["A clippy.js character could not be read: ", clippyJs.problem] })), _jsx(Still, { still: still, children: view === 'stage' ? (_jsx(InModes, { modes: modes, children: mode => (_jsx(ModePanel, { mode: mode, character: character, pose: pose, presence: presence, balloon: balloon, insist: insist, onToggle: () => saying === undefined ? say() : setSaying(undefined), onDismiss: sendAway, onCallBack: callBack, away: away, replay: replay, display: display })) })) : (_jsx(InModes, { modes: modes, stacked: true, children: mode => (_jsxs(Box, { "data-gallery-mode": mode, sx: {
|
|
233
|
+
: 'No tool' }), _jsx(Button, { "data-gallery-notebook": withNotebook ? 'on' : 'off', "aria-pressed": withNotebook, onClick: () => setWithNotebook(!withNotebook), children: withNotebook ? 'Take the notebook' : 'Give a notebook' }), saying !== undefined && (_jsx(Button, { variant: "invisible", onClick: () => setSaying(undefined), children: "Hush" })), presence === 'away' ? (_jsx(Button, { onClick: callBack, children: "Call it back" })) : (_jsx(Button, { onClick: () => sendAway('page'), disabled: presence !== 'here', "data-gallery-send-away": "", children: "Send it away" })), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Drag the character to move it; hover it for its own send-away menu." }), decision && (_jsx(Text, { role: "status", sx: { fontSize: 1 }, children: decision }))] })] }))] }), problem && (_jsxs(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mb: 3 }, children: ["The test sprite could not be read: ", problem] })), clippyJs.problem && (_jsxs(Text, { as: "p", role: "alert", sx: { color: 'danger.fg', mb: 3 }, children: ["A clippy.js character could not be read: ", clippyJs.problem] })), _jsx(Still, { still: still, children: view === 'stage' ? (_jsx(InModes, { modes: modes, children: mode => (_jsx(ModePanel, { mode: mode, character: character, pose: pose, presence: presence, balloon: balloon, insist: insist, onToggle: () => saying === undefined ? say() : setSaying(undefined), onDismiss: sendAway, onCallBack: callBack, away: away, replay: replay, display: display, expandTarget: expandIntoPage ? expandArea : undefined })) })) : (_jsx(InModes, { modes: modes, stacked: true, children: mode => (_jsxs(Box, { "data-gallery-mode": mode, sx: {
|
|
208
234
|
bg: 'canvas.default',
|
|
209
235
|
border: '1px solid',
|
|
210
236
|
borderColor: 'border.default',
|
|
211
237
|
borderRadius: 2,
|
|
212
238
|
p: 2,
|
|
213
239
|
overflowX: 'auto',
|
|
214
|
-
}, children: [_jsx(AssistantGalleryGrid, { characters: characters }, replay), _jsx(GalleryBalloons, { character: character })] })) })) })
|
|
240
|
+
}, children: [_jsx(AssistantGalleryGrid, { characters: characters }, replay), _jsx(GalleryBalloons, { character: character })] })) })) }), view === 'stage' && expandIntoPage && (_jsxs(Box, { as: "section", "aria-label": "Expanded here", sx: {
|
|
241
|
+
mt: 4,
|
|
242
|
+
p: 3,
|
|
243
|
+
border: '1px dashed',
|
|
244
|
+
borderColor: 'border.default',
|
|
245
|
+
borderRadius: 2,
|
|
246
|
+
}, children: [_jsxs(Text, { as: "p", sx: { m: 0, mb: 2, color: 'fg.muted', fontSize: 1 }, children: ["Expanded here: what the balloon\u2019s ", _jsx("em", { children: "Expand" }), " draws large when the page names an area for it \u2014 the notebook, to edit and run on the browser sandbox. Without one, it opens in an overlay."] }), _jsx(Box, { ref: expandArea, "data-gallery-expand-target": "" })] }))] }) }) }));
|
|
215
247
|
};
|
|
216
|
-
export default
|
|
248
|
+
export default AssistantGalleryExample;
|
|
@@ -43,6 +43,8 @@ function inferTags(id) {
|
|
|
43
43
|
tags.add('document');
|
|
44
44
|
if (id.includes('Chat'))
|
|
45
45
|
tags.add('chat');
|
|
46
|
+
if (id.startsWith('Assistant'))
|
|
47
|
+
tags.add('assistant');
|
|
46
48
|
if (id.includes('Sandbox'))
|
|
47
49
|
tags.add('sandbox');
|
|
48
50
|
if (id.includes('Monitoring') || id.includes('Otel'))
|
|
@@ -87,9 +89,9 @@ export const EXAMPLE_ENTRIES = [
|
|
|
87
89
|
makeEntry('DocumentPageAgent', () => import('./DocumentPageAgent'), 'A document as a page: floating prompt on top, an agent writing in it.'),
|
|
88
90
|
makeEntry('DecksAgent', () => import('./DecksAgent'), 'A deck beside the chat: the decks plugin in a Loop, and an agent that writes presentations as data.'),
|
|
89
91
|
makeEntry('CellExample', () => import('./CellExample'), 'Simple cell example.'),
|
|
90
|
-
makeEntry('
|
|
92
|
+
makeEntry('AssistantExample', () => import('./AssistantExample'), 'The floating assistant: the chat as a character on the page.'),
|
|
91
93
|
makeEntry('VoiceChatExample', () => import('./VoiceChatExample'), 'A voice chat with the floating assistant: push-to-talk heard in the page, answers read aloud by the speech service, the mouth moving with the sound.'),
|
|
92
|
-
makeEntry('
|
|
94
|
+
makeEntry('AssistantGalleryExample', () => import('./AssistantGalleryExample'), 'Every representation of the floating assistant: each character in each state, its balloon, light and dark, still or moving, one at a time or as a grid.'),
|
|
93
95
|
makeEntry('ChatCustomExample', () => import('./ChatCustomExample'), 'Custom chat experience composition example.'),
|
|
94
96
|
makeEntry('ChatExample', () => import('./ChatExample'), 'Baseline chat integration example.'),
|
|
95
97
|
makeEntry('ChatStandaloneExample', () => import('./ChatStandaloneExample'), 'Standalone chat component usage example.'),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The examples' groups, in the menu's order, and the group of each example
|
|
3
|
+
* by its id: a module of its own so the order can be tested without the
|
|
4
|
+
* examples shell.
|
|
5
|
+
*
|
|
6
|
+
* @module examples/exampleGroups
|
|
7
|
+
*/
|
|
8
|
+
export declare const EXAMPLE_GROUP_ORDER: readonly ["Apps", "A2UI", "A2A", "AG-UI", "Assistant", "Chat", "Document", "Notebook", "Capabilities", "Cell", "CopilotKit"];
|
|
9
|
+
export declare const getExampleGroup: (id: string) => string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The examples' groups, in the menu's order, and the group of each example
|
|
7
|
+
* by its id: a module of its own so the order can be tested without the
|
|
8
|
+
* examples shell.
|
|
9
|
+
*
|
|
10
|
+
* @module examples/exampleGroups
|
|
11
|
+
*/
|
|
12
|
+
export const EXAMPLE_GROUP_ORDER = [
|
|
13
|
+
'Apps',
|
|
14
|
+
'A2UI',
|
|
15
|
+
'A2A',
|
|
16
|
+
'AG-UI',
|
|
17
|
+
'Assistant',
|
|
18
|
+
'Chat',
|
|
19
|
+
'Document',
|
|
20
|
+
'Notebook',
|
|
21
|
+
'Capabilities',
|
|
22
|
+
'Cell',
|
|
23
|
+
'CopilotKit',
|
|
24
|
+
];
|
|
25
|
+
export const getExampleGroup = (id) => {
|
|
26
|
+
if (id === 'AgentspecsExample' ||
|
|
27
|
+
id.startsWith('Loop') ||
|
|
28
|
+
id === 'DecksAgent') {
|
|
29
|
+
return 'Apps';
|
|
30
|
+
}
|
|
31
|
+
if (id.startsWith('A2Ui'))
|
|
32
|
+
return 'A2UI';
|
|
33
|
+
// Agents reached over the A2A protocol: their own category, after A2UI.
|
|
34
|
+
if (id.startsWith('AgentA2A'))
|
|
35
|
+
return 'A2A';
|
|
36
|
+
if (id.startsWith('AgUi'))
|
|
37
|
+
return 'AG-UI';
|
|
38
|
+
if (id.startsWith('CopilotKit'))
|
|
39
|
+
return 'CopilotKit';
|
|
40
|
+
// Each remaining Agent* example demonstrates one capability of the
|
|
41
|
+
// runtime: checkpoints, hooks, memory, guardrails…
|
|
42
|
+
if (id.startsWith('Agent'))
|
|
43
|
+
return 'Capabilities';
|
|
44
|
+
// The floating assistant, and its gallery: before the chat they wrap.
|
|
45
|
+
if (id.startsWith('Assistant'))
|
|
46
|
+
return 'Assistant';
|
|
47
|
+
if (id.startsWith('Chat') || id === 'VoiceChatExample')
|
|
48
|
+
return 'Chat';
|
|
49
|
+
// The document examples: the ones on the Lexical editor, and the page
|
|
50
|
+
// with a document on it.
|
|
51
|
+
if (id.startsWith('Lexical') || id.startsWith('Document'))
|
|
52
|
+
return 'Document';
|
|
53
|
+
if (id.startsWith('Notebook'))
|
|
54
|
+
return 'Notebook';
|
|
55
|
+
return 'Cell';
|
|
56
|
+
};
|
package/lib/examples/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { default as AgUiHumanInTheLoopExample } from './AgUiHumanInTheLoopExampl
|
|
|
15
15
|
export { default as AgUiSharedStateExample } from './AgUiSharedStateExample';
|
|
16
16
|
export { default as AgUiToolsBasedGenUiExample } from './AgUiToolsBasedGenUiExample';
|
|
17
17
|
export { default as CellExample } from './CellExample';
|
|
18
|
-
export { default as
|
|
18
|
+
export { default as AssistantGalleryExample } from './AssistantGalleryExample';
|
|
19
19
|
export { default as ChatStandaloneExample } from './ChatStandaloneExample';
|
|
20
20
|
export { default as CopilotKitLexicalExample } from './CopilotKitLexicalExample';
|
|
21
21
|
export { default as CopilotKitNotebookExample } from './CopilotKitNotebookExample';
|
package/lib/examples/index.js
CHANGED
|
@@ -21,7 +21,7 @@ export { default as AgUiHumanInTheLoopExample } from './AgUiHumanInTheLoopExampl
|
|
|
21
21
|
export { default as AgUiSharedStateExample } from './AgUiSharedStateExample';
|
|
22
22
|
export { default as AgUiToolsBasedGenUiExample } from './AgUiToolsBasedGenUiExample';
|
|
23
23
|
export { default as CellExample } from './CellExample';
|
|
24
|
-
export { default as
|
|
24
|
+
export { default as AssistantGalleryExample } from './AssistantGalleryExample';
|
|
25
25
|
export { default as ChatStandaloneExample } from './ChatStandaloneExample';
|
|
26
26
|
export { default as CopilotKitLexicalExample } from './CopilotKitLexicalExample';
|
|
27
27
|
export { default as CopilotKitNotebookExample } from './CopilotKitNotebookExample';
|