@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.
Files changed (33) hide show
  1. package/lib/chat/ChatFloating.d.ts +9 -2
  2. package/lib/chat/ChatFloating.js +30 -9
  3. package/lib/chat/assistant/AssistantStage.d.ts +29 -5
  4. package/lib/chat/assistant/AssistantStage.js +20 -2
  5. package/lib/chat/assistant/BalloonVisual.d.ts +57 -0
  6. package/lib/chat/assistant/BalloonVisual.js +58 -0
  7. package/lib/chat/assistant/SpeechBalloon.d.ts +22 -2
  8. package/lib/chat/assistant/SpeechBalloon.js +61 -7
  9. package/lib/chat/assistant/state.d.ts +19 -0
  10. package/lib/chat/assistant/state.js +44 -17
  11. package/lib/components/teams/A2ATeamGraph.d.ts +12 -2
  12. package/lib/components/teams/A2ATeamGraph.js +14 -5
  13. package/lib/components/teams/NotebookPreview.d.ts +11 -2
  14. package/lib/components/teams/NotebookPreview.js +6 -3
  15. package/lib/components/teams/TeamNotebook.d.ts +7 -0
  16. package/lib/components/teams/TeamNotebook.js +13 -1
  17. package/lib/examples/AgentA2ATeamExample.d.ts +3 -2
  18. package/lib/examples/AgentA2ATeamExample.js +6 -2
  19. package/lib/examples/{ChatAssistantExample.d.ts → AssistantExample.d.ts} +2 -2
  20. package/lib/examples/{ChatAssistantExample.js → AssistantExample.js} +7 -5
  21. package/lib/examples/{ChatAssistantGalleryExample.d.ts → AssistantGalleryExample.d.ts} +2 -2
  22. package/lib/examples/{ChatAssistantGalleryExample.js → AssistantGalleryExample.js} +45 -13
  23. package/lib/examples/example-selector.js +4 -2
  24. package/lib/examples/exampleGroups.d.ts +9 -0
  25. package/lib/examples/exampleGroups.js +56 -0
  26. package/lib/examples/index.d.ts +1 -1
  27. package/lib/examples/index.js +1 -1
  28. package/lib/examples/main.js +3 -43
  29. package/lib/examples/utils/AssistantGalleryGrid.d.ts +6 -1
  30. package/lib/examples/utils/AssistantGalleryGrid.js +16 -3
  31. package/lib/examples/utils/assistantGallery.d.ts +51 -2
  32. package/lib/examples/utils/assistantGallery.js +32 -7
  33. 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, the page's
55
- * `TeamNotebook` is scrolled to and focused.
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, focusTeamNotebook } from './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
- // A notebook it was given, read-only: the one that
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
- /** Takes the reader to the notebook that runs; the line under it says so. */
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: "Open it below to run it" })) : null] }));
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 (`focusTeamNotebook`).
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); a click on the one in the balloon
28
- * comes to it.
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, TeamNotebook, teamConnectionsOf, useA2ATeam, } from '../components/teams';
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' }) }), team.notebook && (_jsx(Box, { sx: { mb: 3 }, "data-team-notebook-placement": "under-graph", children: _jsx(TeamNotebook, { notebook: team.notebook, title: `${ACCOUNTING.name}\u2019s notebook` }) })), _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 => {
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 ChatAssistantExample: React.FC;
14
- export default ChatAssistantExample;
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 ChatAssistantExample = () => {
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: "Chat 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: () => {
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" }), ": open, the whole conversation, counted in its header, 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."] }), _jsx(Box, { sx: { display: 'flex', gap: 2 }, children: ['history', 'current'].map(option => (_jsx(Button, { variant: option === balloon ? 'primary' : 'default', "aria-pressed": option === balloon, "data-assistant-balloon-choice": option, onClick: () => setBalloon(option), children: option === 'history' ? 'History' : 'Current' }, option))) })] }), _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 => {
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 ChatAssistantExample;
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 ChatAssistantGalleryExample: React.FC;
17
- export default ChatAssistantGalleryExample;
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 = 520;
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 ChatAssistantGalleryExample = () => {
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 && display === 'current' ? (_jsx(GalleryNotebook, { onOpen: () => setDecision('The notebook would open below to run.') })) : undefined,
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: "Chat 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: {
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(SegmentedControl, { "aria-label": "Balloon", children: ['history', 'current'].map(option => (_jsx(SegmentedControl.Button, { selected: display === option, onClick: () => setDisplay(option), "data-gallery-balloon": option, children: option === 'history' ? 'History' : 'Current' }, option))) }), _jsx(Button, { onClick: say, "data-gallery-say": "", children: "Say something" }), _jsx(Button, { "data-gallery-tool": toolPhase ?? 'none', onClick: () => setToolPhase(toolPhase === undefined
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 ChatAssistantGalleryExample;
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('ChatAssistantExample', () => import('./ChatAssistantExample'), 'The floating assistant: the chat as a character on the page.'),
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('ChatAssistantGalleryExample', () => import('./ChatAssistantGalleryExample'), '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.'),
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
+ };
@@ -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 ChatAssistantGalleryExample } from './ChatAssistantGalleryExample';
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';
@@ -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 ChatAssistantGalleryExample } from './ChatAssistantGalleryExample';
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';