@datalayer/agent-runtimes 1.3.35 → 1.3.37
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 +8 -2
- package/lib/chat/ChatFloating.js +2 -2
- package/lib/chat/base/ChatBase.js +10 -6
- package/lib/chat/display/EmptyState.js +8 -1
- package/lib/chat/presence/Presence.d.ts +15 -1
- package/lib/chat/presence/Presence.js +0 -13
- package/lib/components/a2ui/datalayer/Chart.d.ts +132 -0
- package/lib/components/a2ui/datalayer/Chart.js +173 -0
- package/lib/components/a2ui/datalayer/Chat.d.ts +21 -0
- package/lib/components/a2ui/datalayer/Chat.js +116 -0
- package/lib/components/a2ui/datalayer/Evidence.d.ts +22 -0
- package/lib/components/a2ui/datalayer/Evidence.js +73 -0
- package/lib/components/a2ui/datalayer/FileUpload.d.ts +22 -0
- package/lib/components/a2ui/datalayer/FileUpload.js +107 -0
- package/lib/components/a2ui/datalayer/Form.d.ts +12 -0
- package/lib/components/a2ui/datalayer/Form.js +46 -0
- package/lib/components/a2ui/datalayer/Table.d.ts +20 -0
- package/lib/components/a2ui/datalayer/Table.js +103 -0
- package/lib/components/a2ui/datalayer/drawn.d.ts +10 -0
- package/lib/components/a2ui/datalayer/drawn.js +20 -0
- package/lib/components/a2ui/datalayer/implementation.d.ts +17 -0
- package/lib/components/a2ui/datalayer/implementation.js +10 -0
- package/lib/components/a2ui/datalayer/index.d.ts +23 -0
- package/lib/components/a2ui/datalayer/index.js +44 -0
- package/lib/components/a2ui/datalayer/parts.d.ts +32 -0
- package/lib/components/a2ui/datalayer/parts.js +44 -0
- package/lib/components/a2ui/datalayer/schema.d.ts +34 -0
- package/lib/components/a2ui/datalayer/schema.js +108 -0
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +4 -2
- package/lib/components/a2ui/visibleWhen.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +8 -0
- package/lib/loop/apps/AppRenderer.js +21 -1
- package/lib/loop/embed/AppEmbed.d.ts +16 -1
- package/lib/loop/embed/AppEmbed.js +38 -9
- package/lib/loop/embed/LoopEmbed.js +2 -0
- package/lib/loop/embed/element.js +10 -1
- package/lib/loop/embed/embedTheme.d.ts +12 -0
- package/lib/loop/embed/embedTheme.js +15 -0
- package/lib/loop/plugins/assistant-characters/index.d.ts +10 -1
- package/lib/loop/plugins/assistant-characters/index.js +16 -1
- package/lib/loop/plugins/chat/ChatView.js +21 -9
- package/lib/loop/plugins/chat/index.d.ts +6 -0
- package/lib/loop/presets.d.ts +3 -0
- package/lib/loop/presets.js +2 -1
- package/lib/types/chat.d.ts +11 -0
- package/package.json +2 -1
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import React from 'react';
|
|
15
15
|
import { type AssistantCharacter } from './assistant/characters';
|
|
16
16
|
import type { AssistantCharacterData } from './assistant/formats/types';
|
|
17
|
-
import type { ChatCommonProps, ChatViewMode } from '../types';
|
|
17
|
+
import type { ChatCommonProps, ChatViewMode, ThemeOverrides } from '../types';
|
|
18
18
|
/**
|
|
19
19
|
* ChatFloating props — extends ChatCommonProps with floating/popup-specific configuration.
|
|
20
20
|
*/
|
|
@@ -28,6 +28,12 @@ export interface ChatFloatingProps extends ChatCommonProps {
|
|
|
28
28
|
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
29
29
|
/** Default open state */
|
|
30
30
|
defaultOpen?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Properties laid over the theme the conversation wears, by mode — an
|
|
33
|
+
* application's accent (LOOP T-05): the chat sets its theme again inside
|
|
34
|
+
* it, so the accent the page around it set does not reach its bubbles.
|
|
35
|
+
*/
|
|
36
|
+
themeOverrides?: ThemeOverrides;
|
|
31
37
|
/** width */
|
|
32
38
|
width?: number | string;
|
|
33
39
|
/** height */
|
|
@@ -109,5 +115,5 @@ export interface ChatFloatingProps extends ChatCommonProps {
|
|
|
109
115
|
* ChatFloating component
|
|
110
116
|
* A floating chat window built on ChatBase
|
|
111
117
|
*/
|
|
112
|
-
export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, buttonGlow, offset, animationDuration, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, onViewModeChange, sidebarMountPoint, showPanelBackdrop, availableModels, showModelSelector, showToolsMenu, showSkillsMenu, showTokenUsage, showContextRing, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching, launchingMessage, assistantCharacter, }: ChatFloatingProps): React.JSX.Element;
|
|
118
|
+
export declare function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode, title, description, position, defaultOpen, width, height, showHeader, showButton, showNewChatButton, showClearButton, showSettingsButton, enableKeyboardShortcuts, toggleShortcut, showPoweredBy, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose, escapeToClose, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip, brandColor, buttonGlow, offset, animationDuration, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick, hideMessagesAfterToolUI, defaultViewMode, onViewModeChange, sidebarMountPoint, showPanelBackdrop, availableModels, showModelSelector, showToolsMenu, showSkillsMenu, showTokenUsage, showContextRing, themeVariant, themeOverrides, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching, launchingMessage, assistantCharacter, }: ChatFloatingProps): React.JSX.Element;
|
|
113
119
|
export default ChatFloating;
|
package/lib/chat/ChatFloating.js
CHANGED
|
@@ -59,7 +59,7 @@ const clamp = (value, max) => Math.max(8, Math.min(value, Math.max(8, max)));
|
|
|
59
59
|
* ChatFloating component
|
|
60
60
|
* A floating chat window built on ChatBase
|
|
61
61
|
*/
|
|
62
|
-
export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose = false, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor, buttonGlow = true, offset = 20, animationDuration = 200, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', onViewModeChange, sidebarMountPoint = null, showPanelBackdrop = false, availableModels, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, showTokenUsage = true, showContextRing = false, themeVariant, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation = false, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching = false, launchingMessage, assistantCharacter = DEFAULT_ASSISTANT_CHARACTER, }) {
|
|
62
|
+
export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useStoreMode = true, title = 'Chat', description = 'Start a conversation with the AI agent.', position = 'bottom-right', defaultOpen = false, width = 400, height = 550, showHeader = true, showButton = true, showNewChatButton = true, showClearButton = true, showSettingsButton = false, enableKeyboardShortcuts = true, toggleShortcut = '/', showPoweredBy = true, promptVariant, mentionableAgents, poweredByProps, clickOutsideToClose = false, escapeToClose = true, className, onSettingsClick, onNewChat, onOpen, onClose, onStateUpdate, children, brandIcon, buttonIcon, buttonTooltip = 'Chat with AI', brandColor, buttonGlow = true, offset = 20, animationDuration = 200, renderToolResult, frontendTools, initialState: _initialState, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, defaultViewMode = 'floating', onViewModeChange, sidebarMountPoint = null, showPanelBackdrop = false, availableModels, showModelSelector = false, showToolsMenu = false, showSkillsMenu = false, showTokenUsage = true, showContextRing = false, themeVariant, themeOverrides, colorMode, runtimeId, historyEndpoint, authToken, historyAuthToken, pendingPrompt, showInformation = false, onInformationClick, onToolCallStart, onToolCallComplete, showToolApprovalBanner, pendingApprovals, onApproveApproval, onRejectApproval, panelProps, launching = false, launchingMessage, assistantCharacter = DEFAULT_ASSISTANT_CHARACTER, }) {
|
|
63
63
|
// Store-based state
|
|
64
64
|
const storeIsOpen = useChatOpen();
|
|
65
65
|
const storeMessages = useChatMessages();
|
|
@@ -698,7 +698,7 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
698
698
|
borderColor: 'border.muted',
|
|
699
699
|
touchAction: 'none',
|
|
700
700
|
'&:active': { cursor: 'grabbing' },
|
|
701
|
-
}, children: _jsx(GrabberIcon, { size: 16 }) })), _jsx(ChatBase, { title: title, showHeader: showHeader, useStore: useStoreMode, protocol: protocol, themeVariant: themeVariant, colorMode: colorMode, autoFocus: isOpen, focusTrigger: focusTrigger, launching: launching, launchingMessage: launchingMessage, brandIcon: brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), headerButtons: {
|
|
701
|
+
}, children: _jsx(GrabberIcon, { size: 16 }) })), _jsx(ChatBase, { title: title, showHeader: showHeader, useStore: useStoreMode, protocol: protocol, themeVariant: themeVariant, themeOverrides: themeOverrides, colorMode: colorMode, autoFocus: isOpen, focusTrigger: focusTrigger, launching: launching, launchingMessage: launchingMessage, brandIcon: brandIcon || _jsx(AiAgentIcon, { colored: true, size: 20 }), headerButtons: {
|
|
702
702
|
showNewChat: showNewChatButton,
|
|
703
703
|
showClear: showClearButton && messages.length > 0,
|
|
704
704
|
showSettings: showSettingsButton && !!onSettingsClick,
|
|
@@ -141,7 +141,7 @@ const ThemedJupyterReactTheme = JupyterReactTheme;
|
|
|
141
141
|
* chat height/flex chain is preserved); CSS custom properties and inherited
|
|
142
142
|
* properties (color, font) still cascade through it.
|
|
143
143
|
*/
|
|
144
|
-
function ThemedChatBoundary({ children, themeVariant, colorMode, }) {
|
|
144
|
+
function ThemedChatBoundary({ children, themeVariant, colorMode, themeOverrides, }) {
|
|
145
145
|
const storeColorMode = useThemeStore(s => s.colorMode);
|
|
146
146
|
const storeThemeVariant = useThemeStore(s => s.theme);
|
|
147
147
|
const resolvedColorMode = colorMode ?? storeColorMode;
|
|
@@ -149,9 +149,13 @@ function ThemedChatBoundary({ children, themeVariant, colorMode, }) {
|
|
|
149
149
|
const systemMode = useSystemColorMode();
|
|
150
150
|
const themeConfig = getThemeConfig(resolvedThemeVariant);
|
|
151
151
|
const resolvedMode = resolvedColorMode === 'auto' ? systemMode : resolvedColorMode;
|
|
152
|
-
const modeStyles =
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
const modeStyles = {
|
|
153
|
+
...(resolvedMode === 'dark'
|
|
154
|
+
? themeConfig.themeStyles.dark
|
|
155
|
+
: themeConfig.themeStyles.light),
|
|
156
|
+
// What the host lays over the theme: an application's accent (T-05).
|
|
157
|
+
...themeOverrides?.[resolvedMode],
|
|
158
|
+
};
|
|
155
159
|
const themeBackground = modeStyles.backgroundColor ?? '';
|
|
156
160
|
return (_jsx(ThemedJupyterReactTheme, { colormode: resolvedMode, theme: themeConfig.primerTheme, backgroundColor: themeBackground, loadJupyterLabCss: false, style: {
|
|
157
161
|
...modeStyles,
|
|
@@ -463,7 +467,7 @@ function parseApprovedMcpToolsByServer(mcpStatusData) {
|
|
|
463
467
|
* ChatBase component — Universal chat panel supporting store, protocol, and custom modes.
|
|
464
468
|
*/
|
|
465
469
|
export function ChatBase(props) {
|
|
466
|
-
const { agentRuntimeConfig, protocol: protocolProp, useStore: useStoreMode = true, disableInternalJupyterTheme = false, themeVariant, colorMode, } = props;
|
|
470
|
+
const { agentRuntimeConfig, protocol: protocolProp, useStore: useStoreMode = true, disableInternalJupyterTheme = false, themeVariant, colorMode, themeOverrides, } = props;
|
|
467
471
|
// Resolve protocol: string Protocol overrides type in agentRuntimeConfig or
|
|
468
472
|
// is combined with a full ProtocolConfig object.
|
|
469
473
|
const protocolType = typeof protocolProp === 'string' ? protocolProp : undefined;
|
|
@@ -490,7 +494,7 @@ export function ChatBase(props) {
|
|
|
490
494
|
useStore: effectiveUseStoreMode,
|
|
491
495
|
};
|
|
492
496
|
const content = _jsx(ChatBaseInner, { ...innerProps });
|
|
493
|
-
const wrappedContent = disableInternalJupyterTheme ? (content) : (_jsx(ThemedChatBoundary, { themeVariant: themeVariant, colorMode: colorMode, children: content }));
|
|
497
|
+
const wrappedContent = disableInternalJupyterTheme ? (content) : (_jsx(ThemedChatBoundary, { themeVariant: themeVariant, colorMode: colorMode, themeOverrides: themeOverrides, children: content }));
|
|
494
498
|
if (!existingQueryClient) {
|
|
495
499
|
return (_jsx(QueryClientProvider, { client: internalQueryClient, children: wrappedContent }));
|
|
496
500
|
}
|
|
@@ -60,7 +60,14 @@ export function ChatEmptyState({ emptyState, brandIcon, description, suggestions
|
|
|
60
60
|
color: 'var(--button-primary-fgColor-rest)',
|
|
61
61
|
borderColor: 'accent.emphasis',
|
|
62
62
|
},
|
|
63
|
-
}, onClick: () => handleSuggestionClick(suggestion),
|
|
63
|
+
}, onClick: () => handleSuggestionClick(suggestion),
|
|
64
|
+
// A chip is a button to the keyboard as well as to the pointer.
|
|
65
|
+
role: "button", tabIndex: 0, onKeyDown: (event) => {
|
|
66
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
handleSuggestionClick(suggestion);
|
|
69
|
+
}
|
|
70
|
+
}, children: _jsx(Truncate, { inline: true, title: suggestion.message, maxWidth: "100%", sx: { minWidth: 0 }, children: suggestion.title }) }, index))) }));
|
|
64
71
|
return (_jsx(ThemeProvider, { children: _jsxs(Box, { sx: {
|
|
65
72
|
display: 'flex',
|
|
66
73
|
flexDirection: 'column',
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An application's presence in its chat (LOOP T-08): its face, with a quiet
|
|
3
|
+
* motion around it while it thinks or works, and one line saying what it is
|
|
4
|
+
* doing. The face is never redrawn; the motion is a soft ring in the accent,
|
|
5
|
+
* still under `prefers-reduced-motion`, and the line changes at the theme's
|
|
6
|
+
* status pace (T-10).
|
|
7
|
+
*
|
|
8
|
+
* @module chat/presence/Presence
|
|
9
|
+
*/
|
|
10
|
+
import type { ReactNode } from 'react';
|
|
1
11
|
import { type PresenceState } from './presenceStatus';
|
|
2
12
|
export declare function PresenceFace({ face, size, state, }: {
|
|
3
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The face: an emoji, drawn at `size`, or a host's own drawing of it — an
|
|
15
|
+
* avatar on its disc (I-07) — as wide as `size`.
|
|
16
|
+
*/
|
|
17
|
+
face: ReactNode;
|
|
4
18
|
size: number;
|
|
5
19
|
state: PresenceState;
|
|
6
20
|
}): import("react").JSX.Element;
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/*
|
|
3
|
-
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
-
* Distributed under the terms of the Modified BSD License.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* An application's presence in its chat (LOOP T-08): its face, with a quiet
|
|
8
|
-
* motion around it while it thinks or works, and one line saying what it is
|
|
9
|
-
* doing. The face is never redrawn; the motion is a soft ring in the accent,
|
|
10
|
-
* still under `prefers-reduced-motion`, and the line changes at the theme's
|
|
11
|
-
* status pace (T-10).
|
|
12
|
-
*
|
|
13
|
-
* @module chat/presence/Presence
|
|
14
|
-
*/
|
|
15
2
|
import { Box, Text } from '@primer/react';
|
|
16
3
|
import { PRESENCE_LINES } from './presenceStatus';
|
|
17
4
|
const ACCENT = 'var(--loop-accent, var(--fgColor-accent))';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { type OwnCommon } from './implementation';
|
|
2
|
+
export type ChartKind = 'bar' | 'line' | 'scatter' | 'area';
|
|
3
|
+
export type ChartProps = OwnCommon & {
|
|
4
|
+
title?: string;
|
|
5
|
+
kind: ChartKind;
|
|
6
|
+
x: string;
|
|
7
|
+
y: string;
|
|
8
|
+
series?: string;
|
|
9
|
+
points?: unknown;
|
|
10
|
+
};
|
|
11
|
+
/** The page's colours, read from Primer's variables where the chart is drawn. */
|
|
12
|
+
export type ChartColours = {
|
|
13
|
+
series: string[];
|
|
14
|
+
text: string;
|
|
15
|
+
muted: string;
|
|
16
|
+
grid: string;
|
|
17
|
+
};
|
|
18
|
+
/** The points a chart can draw, or why it cannot. */
|
|
19
|
+
export declare function chartPoints(points: unknown): {
|
|
20
|
+
points: Record<string, unknown>[];
|
|
21
|
+
} | {
|
|
22
|
+
problem: string;
|
|
23
|
+
};
|
|
24
|
+
/** The series a chart draws: one per value of `series`, or one. */
|
|
25
|
+
export declare function chartSeries(points: Record<string, unknown>[], props: Pick<ChartProps, 'x' | 'y' | 'series'>): {
|
|
26
|
+
name: string;
|
|
27
|
+
points: Record<string, unknown>[];
|
|
28
|
+
}[];
|
|
29
|
+
/**
|
|
30
|
+
* The ECharts option a chart draws: categories along the bottom for a bar,
|
|
31
|
+
* a line or an area (in the order the points give them), numbers both ways
|
|
32
|
+
* for a scatter.
|
|
33
|
+
*/
|
|
34
|
+
export declare function chartOption(points: Record<string, unknown>[], props: Pick<ChartProps, 'kind' | 'x' | 'y' | 'series'>, colours: ChartColours): {
|
|
35
|
+
aria: {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
};
|
|
38
|
+
animation: boolean;
|
|
39
|
+
color: string[];
|
|
40
|
+
textStyle: {
|
|
41
|
+
color: string;
|
|
42
|
+
};
|
|
43
|
+
grid: {
|
|
44
|
+
left: number;
|
|
45
|
+
right: number;
|
|
46
|
+
top: number;
|
|
47
|
+
bottom: number;
|
|
48
|
+
};
|
|
49
|
+
legend: {
|
|
50
|
+
top: number;
|
|
51
|
+
textStyle: {
|
|
52
|
+
color: string;
|
|
53
|
+
};
|
|
54
|
+
} | undefined;
|
|
55
|
+
tooltip: {
|
|
56
|
+
trigger: string;
|
|
57
|
+
};
|
|
58
|
+
xAxis: {
|
|
59
|
+
axisLine: {
|
|
60
|
+
lineStyle: {
|
|
61
|
+
color: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
axisLabel: {
|
|
65
|
+
color: string;
|
|
66
|
+
};
|
|
67
|
+
splitLine: {
|
|
68
|
+
lineStyle: {
|
|
69
|
+
color: string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
nameTextStyle: {
|
|
73
|
+
color: string;
|
|
74
|
+
};
|
|
75
|
+
type: string;
|
|
76
|
+
name: string;
|
|
77
|
+
nameLocation: string;
|
|
78
|
+
nameGap: number;
|
|
79
|
+
} | {
|
|
80
|
+
axisLine: {
|
|
81
|
+
lineStyle: {
|
|
82
|
+
color: string;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
axisLabel: {
|
|
86
|
+
color: string;
|
|
87
|
+
};
|
|
88
|
+
splitLine: {
|
|
89
|
+
lineStyle: {
|
|
90
|
+
color: string;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
nameTextStyle: {
|
|
94
|
+
color: string;
|
|
95
|
+
};
|
|
96
|
+
type: string;
|
|
97
|
+
data: string[];
|
|
98
|
+
name: string;
|
|
99
|
+
nameLocation: string;
|
|
100
|
+
nameGap: number;
|
|
101
|
+
};
|
|
102
|
+
yAxis: {
|
|
103
|
+
axisLine: {
|
|
104
|
+
lineStyle: {
|
|
105
|
+
color: string;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
axisLabel: {
|
|
109
|
+
color: string;
|
|
110
|
+
};
|
|
111
|
+
splitLine: {
|
|
112
|
+
lineStyle: {
|
|
113
|
+
color: string;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
nameTextStyle: {
|
|
117
|
+
color: string;
|
|
118
|
+
};
|
|
119
|
+
type: string;
|
|
120
|
+
name: string;
|
|
121
|
+
};
|
|
122
|
+
series: {
|
|
123
|
+
name: string;
|
|
124
|
+
type: "line" | "bar" | "scatter";
|
|
125
|
+
areaStyle: {} | undefined;
|
|
126
|
+
data: number[][] | (number | null)[];
|
|
127
|
+
}[];
|
|
128
|
+
};
|
|
129
|
+
export declare function ChartView({ props }: {
|
|
130
|
+
props: ChartProps;
|
|
131
|
+
}): import("react").JSX.Element;
|
|
132
|
+
export declare const Chart: import("@a2ui/react/v0_9").ReactComponentImplementation;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Chart (LOOP C-18): the points an application publishes, drawn as a bar, a
|
|
8
|
+
* line, an area or a scatter — one field along the bottom, one measured, a
|
|
9
|
+
* series apart for each value of a third. Drawn with ECharts, the chart
|
|
10
|
+
* library agent-runtimes already draws its usage and context charts with,
|
|
11
|
+
* in the page's colours (read from Primer's variables, so the chart follows
|
|
12
|
+
* the theme and the colour mode). Its numbers are also a table under *The
|
|
13
|
+
* numbers*, reached by keyboard and read by assistive technologies, which a
|
|
14
|
+
* drawing is not.
|
|
15
|
+
*
|
|
16
|
+
* @module components/a2ui/datalayer/Chart
|
|
17
|
+
*/
|
|
18
|
+
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
19
|
+
import ReactECharts from 'echarts-for-react';
|
|
20
|
+
import { Text } from '@primer/react';
|
|
21
|
+
import { Box } from '@datalayer/primer-addons';
|
|
22
|
+
import { BlockFrame, Problem, Quiet, asWords, isRecord } from './parts';
|
|
23
|
+
import { ownImplementation } from './implementation';
|
|
24
|
+
/** Primer's light colours, for a page that sets no variables. */
|
|
25
|
+
const FALLBACK = {
|
|
26
|
+
series: ['#0969da', '#1a7f37', '#9a6700', '#8250df', '#cf222e', '#bc4c00'],
|
|
27
|
+
text: '#1f2328',
|
|
28
|
+
muted: '#59636e',
|
|
29
|
+
grid: '#d1d9e0',
|
|
30
|
+
};
|
|
31
|
+
const SERIES_VARIABLES = [
|
|
32
|
+
'--fgColor-accent',
|
|
33
|
+
'--fgColor-success',
|
|
34
|
+
'--fgColor-attention',
|
|
35
|
+
'--fgColor-done',
|
|
36
|
+
'--fgColor-danger',
|
|
37
|
+
'--fgColor-severe',
|
|
38
|
+
];
|
|
39
|
+
function readColours(element) {
|
|
40
|
+
if (!element) {
|
|
41
|
+
return FALLBACK;
|
|
42
|
+
}
|
|
43
|
+
const style = getComputedStyle(element);
|
|
44
|
+
const read = (name, fallback) => style.getPropertyValue(name).trim() || fallback;
|
|
45
|
+
return {
|
|
46
|
+
series: SERIES_VARIABLES.map((name, index) => read(name, FALLBACK.series[index])),
|
|
47
|
+
text: read('--fgColor-default', FALLBACK.text),
|
|
48
|
+
muted: read('--fgColor-muted', FALLBACK.muted),
|
|
49
|
+
grid: read('--borderColor-muted', FALLBACK.grid),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** The points a chart can draw, or why it cannot. */
|
|
53
|
+
export function chartPoints(points) {
|
|
54
|
+
if (points === undefined || points === null) {
|
|
55
|
+
return { points: [] };
|
|
56
|
+
}
|
|
57
|
+
if (!Array.isArray(points) || points.some(point => !isRecord(point))) {
|
|
58
|
+
return { problem: 'What it shows is not a list of points.' };
|
|
59
|
+
}
|
|
60
|
+
return { points: points };
|
|
61
|
+
}
|
|
62
|
+
/** The series a chart draws: one per value of `series`, or one. */
|
|
63
|
+
export function chartSeries(points, props) {
|
|
64
|
+
if (!props.series) {
|
|
65
|
+
return [{ name: props.y, points }];
|
|
66
|
+
}
|
|
67
|
+
const names = [];
|
|
68
|
+
for (const point of points) {
|
|
69
|
+
const name = asWords(point[props.series]);
|
|
70
|
+
if (!names.includes(name)) {
|
|
71
|
+
names.push(name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return names.map(name => ({
|
|
75
|
+
name,
|
|
76
|
+
points: points.filter(point => asWords(point[props.series]) === name),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The ECharts option a chart draws: categories along the bottom for a bar,
|
|
81
|
+
* a line or an area (in the order the points give them), numbers both ways
|
|
82
|
+
* for a scatter.
|
|
83
|
+
*/
|
|
84
|
+
export function chartOption(points, props, colours) {
|
|
85
|
+
const series = chartSeries(points, props);
|
|
86
|
+
const scatter = props.kind === 'scatter';
|
|
87
|
+
const categories = [];
|
|
88
|
+
for (const point of points) {
|
|
89
|
+
const name = asWords(point[props.x]);
|
|
90
|
+
if (!categories.includes(name)) {
|
|
91
|
+
categories.push(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const axis = {
|
|
95
|
+
axisLine: { lineStyle: { color: colours.grid } },
|
|
96
|
+
axisLabel: { color: colours.muted },
|
|
97
|
+
splitLine: { lineStyle: { color: colours.grid } },
|
|
98
|
+
nameTextStyle: { color: colours.muted },
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
aria: { enabled: true },
|
|
102
|
+
animation: false,
|
|
103
|
+
color: colours.series,
|
|
104
|
+
textStyle: { color: colours.text },
|
|
105
|
+
grid: { left: 48, right: 16, top: series.length > 1 ? 48 : 32, bottom: 40 },
|
|
106
|
+
legend: series.length > 1
|
|
107
|
+
? { top: 0, textStyle: { color: colours.text } }
|
|
108
|
+
: undefined,
|
|
109
|
+
tooltip: { trigger: scatter ? 'item' : 'axis' },
|
|
110
|
+
xAxis: scatter
|
|
111
|
+
? {
|
|
112
|
+
type: 'value',
|
|
113
|
+
name: props.x,
|
|
114
|
+
nameLocation: 'middle',
|
|
115
|
+
nameGap: 26,
|
|
116
|
+
...axis,
|
|
117
|
+
}
|
|
118
|
+
: {
|
|
119
|
+
type: 'category',
|
|
120
|
+
data: categories,
|
|
121
|
+
name: props.x,
|
|
122
|
+
nameLocation: 'middle',
|
|
123
|
+
nameGap: 26,
|
|
124
|
+
...axis,
|
|
125
|
+
},
|
|
126
|
+
yAxis: { type: 'value', name: props.y, ...axis },
|
|
127
|
+
series: series.map(one => ({
|
|
128
|
+
name: one.name,
|
|
129
|
+
type: props.kind === 'area' ? 'line' : props.kind,
|
|
130
|
+
areaStyle: props.kind === 'area' ? {} : undefined,
|
|
131
|
+
data: scatter
|
|
132
|
+
? one.points.map(point => [
|
|
133
|
+
Number(point[props.x]),
|
|
134
|
+
Number(point[props.y]),
|
|
135
|
+
])
|
|
136
|
+
: categories.map(category => {
|
|
137
|
+
const point = one.points.find(candidate => asWords(candidate[props.x]) === category);
|
|
138
|
+
return point ? Number(point[props.y]) : null;
|
|
139
|
+
}),
|
|
140
|
+
})),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function ChartView({ props }) {
|
|
144
|
+
const { title, kind, x, y, series } = props;
|
|
145
|
+
const read = useMemo(() => chartPoints(props.points), [props.points]);
|
|
146
|
+
const frame = useRef(null);
|
|
147
|
+
const [colours, setColours] = useState(FALLBACK);
|
|
148
|
+
useLayoutEffect(() => {
|
|
149
|
+
setColours(readColours(frame.current));
|
|
150
|
+
}, []);
|
|
151
|
+
const points = 'points' in read ? read.points : [];
|
|
152
|
+
const option = useMemo(() => chartOption(points, { kind, x, y, series }, colours), [points, kind, x, y, series, colours]);
|
|
153
|
+
const columns = series ? [x, series, y] : [x, y];
|
|
154
|
+
const label = `${title || 'Chart'}: ${y} by ${x}, as a ${kind}`;
|
|
155
|
+
return (_jsx(BlockFrame, { title: title, label: "Chart", weight: props.weight, testId: "a2ui-chart", children: _jsx(Box, { ref: frame, children: 'problem' in read ? (_jsx(Problem, { children: read.problem })) : points.length === 0 ? (_jsx(Quiet, { children: "No points yet." })) : (_jsxs(Box, { as: "figure", "aria-label": label, sx: { m: 0 }, children: [_jsx("div", { "aria-hidden": "true", children: _jsx(ReactECharts, { option: option, style: { height: 240, width: '100%' }, opts: { renderer: 'svg' }, notMerge: true, lazyUpdate: true }) }), _jsxs(Box, { as: "details", sx: { fontSize: 1 }, children: [_jsx(Box, { as: "summary", sx: {
|
|
156
|
+
cursor: 'pointer',
|
|
157
|
+
color: 'fg.muted',
|
|
158
|
+
'&:focus-visible': {
|
|
159
|
+
outline: '2px solid',
|
|
160
|
+
outlineColor: 'accent.fg',
|
|
161
|
+
},
|
|
162
|
+
}, children: _jsx(Text, { children: "The numbers" }) }), _jsxs(Box, { as: "table", sx: {
|
|
163
|
+
mt: 1,
|
|
164
|
+
borderCollapse: 'collapse',
|
|
165
|
+
'& th, & td': {
|
|
166
|
+
textAlign: 'left',
|
|
167
|
+
pr: 3,
|
|
168
|
+
py: '2px',
|
|
169
|
+
},
|
|
170
|
+
'& th': { color: 'fg.muted', fontWeight: 'semibold' },
|
|
171
|
+
}, children: [_jsx("thead", { children: _jsx("tr", { children: columns.map(column => (_jsx("th", { scope: "col", children: column }, column))) }) }), _jsx("tbody", { children: points.map((point, index) => (_jsx("tr", { children: columns.map(column => (_jsx("td", { children: asWords(point[column]) }, column))) }, index))) })] })] })] })) }) }));
|
|
172
|
+
}
|
|
173
|
+
export const Chart = ownImplementation('Chart', ChartView);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DisplayItem } from '../../../types/chat';
|
|
2
|
+
import { type OwnCommon } from './implementation';
|
|
3
|
+
export type ChatProps = OwnCommon & {
|
|
4
|
+
welcome?: string;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
starters?: string[];
|
|
7
|
+
show_tools?: boolean;
|
|
8
|
+
messages?: unknown;
|
|
9
|
+
message?: unknown;
|
|
10
|
+
setMessage: (message: string) => void;
|
|
11
|
+
};
|
|
12
|
+
/** The transcript a chat draws from what is published, or why it cannot. */
|
|
13
|
+
export declare function chatItems(messages: unknown, showTools: boolean): {
|
|
14
|
+
items: DisplayItem[];
|
|
15
|
+
} | {
|
|
16
|
+
problem: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function ChatView({ props }: {
|
|
19
|
+
props: ChatProps;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
export declare const Chat: import("@a2ui/react/v0_9").ReactComponentImplementation;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Chat (LOOP C-18): the conversation with the application, drawn by the
|
|
8
|
+
* chat's own parts — its transcript (`ChatMessageList`, the one every chat
|
|
9
|
+
* draws its turns with), its welcome and starters (`ChatEmptyState`) and its
|
|
10
|
+
* composer (`InputPromptText`) — from what the application publishes.
|
|
11
|
+
*
|
|
12
|
+
* It shows the messages its `messages` binding points at, each
|
|
13
|
+
* `{role: 'user' | 'assistant', text}`, or `{role: 'tool', name, args,
|
|
14
|
+
* result}` for a tool call, shown only while `show_tools` holds. Before the
|
|
15
|
+
* first message it says its welcome and offers its starters. A message
|
|
16
|
+
* written in the composer — or a starter chosen — is written where its
|
|
17
|
+
* `message` binding points, then its action is dispatched (the application's
|
|
18
|
+
* `send`), so the action's context reads the message just written, as a
|
|
19
|
+
* basic input's action reads its value.
|
|
20
|
+
*
|
|
21
|
+
* @module components/a2ui/datalayer/Chat
|
|
22
|
+
*/
|
|
23
|
+
import { createRef, useMemo, useState } from 'react';
|
|
24
|
+
import { IconButton } from '@primer/react';
|
|
25
|
+
import { DependabotIcon, PaperAirplaneIcon, PersonIcon, } from '@primer/octicons-react';
|
|
26
|
+
import { Box } from '@datalayer/primer-addons';
|
|
27
|
+
import { ChatMessageList } from '../../../chat/messages/ChatMessageList';
|
|
28
|
+
import { ChatEmptyState } from '../../../chat/display/EmptyState';
|
|
29
|
+
import { InputPromptText } from '../../../chat/prompt/InputPromptText';
|
|
30
|
+
import { BlockFrame, CARD_RADIUS, Problem, asWords, isRecord } from './parts';
|
|
31
|
+
import { ownImplementation } from './implementation';
|
|
32
|
+
/** The defaults the catalog gives. */
|
|
33
|
+
const PLACEHOLDER = 'Ask anything';
|
|
34
|
+
const AVATARS = {
|
|
35
|
+
userAvatar: _jsx(PersonIcon, { size: 14 }),
|
|
36
|
+
assistantAvatar: _jsx(DependabotIcon, { size: 14 }),
|
|
37
|
+
showAvatars: true,
|
|
38
|
+
avatarSize: 24,
|
|
39
|
+
userAvatarBg: 'neutral.muted',
|
|
40
|
+
assistantAvatarBg: 'accent.emphasis',
|
|
41
|
+
};
|
|
42
|
+
/** The epoch: a published message carries no time, and the transcript shows none. */
|
|
43
|
+
const NO_TIME = new Date(0);
|
|
44
|
+
/** The transcript a chat draws from what is published, or why it cannot. */
|
|
45
|
+
export function chatItems(messages, showTools) {
|
|
46
|
+
if (messages === undefined || messages === null) {
|
|
47
|
+
return { items: [] };
|
|
48
|
+
}
|
|
49
|
+
if (!Array.isArray(messages)) {
|
|
50
|
+
return { problem: 'What it shows is not a list of messages.' };
|
|
51
|
+
}
|
|
52
|
+
const items = [];
|
|
53
|
+
for (const [index, message] of messages.entries()) {
|
|
54
|
+
const id = `m${index}`;
|
|
55
|
+
if (!isRecord(message)) {
|
|
56
|
+
return { problem: `Message ${index + 1} is not a message.` };
|
|
57
|
+
}
|
|
58
|
+
if (message.role === 'tool') {
|
|
59
|
+
if (showTools) {
|
|
60
|
+
items.push({
|
|
61
|
+
id,
|
|
62
|
+
type: 'tool-call',
|
|
63
|
+
toolCallId: id,
|
|
64
|
+
toolName: asWords(message.name) || 'tool',
|
|
65
|
+
args: isRecord(message.args) ? message.args : {},
|
|
66
|
+
result: message.result,
|
|
67
|
+
status: 'complete',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (message.role !== 'user' && message.role !== 'assistant') {
|
|
73
|
+
return {
|
|
74
|
+
problem: `Message ${index + 1} is from ${asWords(message.role) || 'nobody'}: a message is the user's, the assistant's or a tool's.`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
items.push({
|
|
78
|
+
id,
|
|
79
|
+
role: message.role,
|
|
80
|
+
content: asWords(message.text ?? message.content),
|
|
81
|
+
createdAt: NO_TIME,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { items };
|
|
85
|
+
}
|
|
86
|
+
export function ChatView({ props }) {
|
|
87
|
+
const { welcome, starters = [], setMessage, action } = props;
|
|
88
|
+
const placeholder = props.placeholder ?? PLACEHOLDER;
|
|
89
|
+
const showTools = props.show_tools ?? true;
|
|
90
|
+
const read = useMemo(() => chatItems(props.messages, showTools), [props.messages, showTools]);
|
|
91
|
+
const [draft, setDraft] = useState('');
|
|
92
|
+
const messagesEnd = useMemo(() => createRef(), []);
|
|
93
|
+
const inputRef = useMemo(() => createRef(), []);
|
|
94
|
+
const send = (text) => {
|
|
95
|
+
const message = text.trim();
|
|
96
|
+
if (!message) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
setMessage(message);
|
|
100
|
+
action?.();
|
|
101
|
+
setDraft('');
|
|
102
|
+
};
|
|
103
|
+
return (_jsxs(BlockFrame, { label: "Chat", weight: props.weight, testId: "a2ui-chat", children: ['problem' in read ? (_jsx(Problem, { children: read.problem })) : (_jsx(Box, { sx: {
|
|
104
|
+
maxHeight: 420,
|
|
105
|
+
overflowY: 'auto',
|
|
106
|
+
borderRadius: CARD_RADIUS,
|
|
107
|
+
}, children: _jsx(ChatMessageList, { displayItems: read.items, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: AVATARS, padding: 2, emptyContent: _jsx(ChatEmptyState, { description: "", emptyState: { title: welcome || 'Start a conversation' }, suggestions: starters.map(starter => ({
|
|
108
|
+
title: starter,
|
|
109
|
+
message: starter,
|
|
110
|
+
})), onSuggestionSubmit: suggestion => send(suggestion.message) }), messagesEndRef: messagesEnd, onRespond: async () => undefined }) })), _jsxs(Box, { sx: {
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'flex-end',
|
|
113
|
+
gap: 2,
|
|
114
|
+
}, children: [_jsx(Box, { sx: { flex: 1, minWidth: 0 }, children: _jsx(InputPromptText, { value: draft, onChange: setDraft, placeholder: placeholder, onSubmit: () => send(draft), inputRef: inputRef }) }), _jsx(IconButton, { icon: PaperAirplaneIcon, "aria-label": "Send", variant: "primary", disabled: !draft.trim(), onClick: () => send(draft) })] })] }));
|
|
115
|
+
}
|
|
116
|
+
export const Chat = ownImplementation('Chat', ChatView);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type OwnCommon } from './implementation';
|
|
2
|
+
export type EvidenceProps = OwnCommon & {
|
|
3
|
+
title?: string;
|
|
4
|
+
show_passages?: boolean;
|
|
5
|
+
max_items?: number;
|
|
6
|
+
sources?: unknown;
|
|
7
|
+
};
|
|
8
|
+
export type Source = {
|
|
9
|
+
title: string;
|
|
10
|
+
url: string;
|
|
11
|
+
passage: string;
|
|
12
|
+
};
|
|
13
|
+
/** The sources a block can show, or why it cannot. */
|
|
14
|
+
export declare function evidenceSources(sources: unknown): {
|
|
15
|
+
sources: Source[];
|
|
16
|
+
} | {
|
|
17
|
+
problem: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function EvidenceView({ props }: {
|
|
20
|
+
props: EvidenceProps;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
22
|
+
export declare const Evidence: import("@a2ui/react/v0_9").ReactComponentImplementation;
|