@datalayer/core 0.0.15 → 0.0.17

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 (46) hide show
  1. package/lib/components/chat/ChatComponent.d.ts +4 -0
  2. package/lib/components/chat/ChatComponent.js +139 -0
  3. package/lib/components/chat/MessagePart.d.ts +11 -0
  4. package/lib/components/chat/MessagePart.js +23 -0
  5. package/lib/components/chat/display/DynamicToolPart.d.ts +6 -0
  6. package/lib/components/chat/display/DynamicToolPart.js +5 -0
  7. package/lib/components/chat/display/ReasoningPart.d.ts +6 -0
  8. package/lib/components/chat/display/ReasoningPart.js +54 -0
  9. package/lib/components/chat/display/TextPart.d.ts +9 -0
  10. package/lib/components/chat/display/TextPart.js +93 -0
  11. package/lib/components/chat/display/ToolPart.d.ts +6 -0
  12. package/lib/components/chat/display/ToolPart.js +144 -0
  13. package/lib/components/chat/display/index.d.ts +4 -0
  14. package/lib/components/chat/display/index.js +9 -0
  15. package/lib/components/chat/handler.d.ts +8 -0
  16. package/lib/components/chat/handler.js +39 -0
  17. package/lib/components/chat/index.d.ts +4 -0
  18. package/lib/components/chat/index.js +9 -0
  19. package/lib/components/index.d.ts +1 -1
  20. package/lib/components/index.js +1 -1
  21. package/lib/components/runtimes/RuntimeSimplePicker.d.ts +4 -0
  22. package/lib/components/runtimes/RuntimeSimplePicker.js +3 -3
  23. package/lib/examples/ChatExample.d.ts +8 -0
  24. package/lib/examples/ChatExample.js +51 -0
  25. package/lib/examples/example-selector.js +1 -0
  26. package/lib/hooks/useAIJupyterChat.d.ts +36 -0
  27. package/lib/hooks/useAIJupyterChat.js +49 -0
  28. package/lib/hooks/useCache.js +105 -45
  29. package/lib/hooks/useMobile.d.ts +1 -0
  30. package/lib/hooks/useMobile.js +22 -0
  31. package/lib/hooks/useUpload.js +29 -21
  32. package/lib/index.d.ts +1 -0
  33. package/lib/index.js +8 -4
  34. package/lib/stateful/index.d.ts +0 -1
  35. package/lib/stateful/index.js +0 -1
  36. package/lib/stateful/runtimes/actions.d.ts +1 -1
  37. package/lib/stateful/runtimes/actions.js +1 -1
  38. package/lib/theme/DatalayerTheme.d.ts +2 -2
  39. package/lib/theme/DatalayerTheme.js +4 -4
  40. package/lib/theme/DatalayerThemeProvider.js +2 -2
  41. package/lib/types.d.ts +5 -0
  42. package/lib/types.js +6 -0
  43. package/package.json +16 -2
  44. package/style/base.css +4 -0
  45. package/lib/sdk/index.d.ts +0 -27
  46. package/lib/sdk/index.js +0 -33
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Main Chat component using Primer React
3
+ */
4
+ export declare const ChatComponent: React.FC;
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2024-2025 Datalayer, Inc.
4
+ *
5
+ * BSD 3-Clause License
6
+ */
7
+ import { useState, useEffect, useMemo, useRef } from 'react';
8
+ import { useQuery } from '@tanstack/react-query';
9
+ import { Box, Button, IconButton, Textarea, FormControl, Spinner, ActionMenu, ActionList, Text, } from '@primer/react';
10
+ import { ToolsIcon, ArrowUpIcon, ArrowDownIcon, AiModelIcon, } from '@primer/octicons-react';
11
+ import { useAIJupyterChat } from '../../hooks/useAIJupyterChat';
12
+ import { requestAPI } from './handler';
13
+ import { MessagePart } from './MessagePart';
14
+ async function getModels() {
15
+ return await requestAPI('configure');
16
+ }
17
+ /**
18
+ * Main Chat component using Primer React
19
+ */
20
+ export const ChatComponent = () => {
21
+ const [model, setModel] = useState('');
22
+ const [enabledTools, setEnabledTools] = useState([]);
23
+ const [inputValue, setInputValue] = useState('');
24
+ const [showScrollButton, setShowScrollButton] = useState(false);
25
+ const { messages, sendMessage, status, regenerate } = useAIJupyterChat();
26
+ const textareaRef = useRef(null);
27
+ const messagesEndRef = useRef(null);
28
+ const scrollContainerRef = useRef(null);
29
+ const configQuery = useQuery({
30
+ queryFn: getModels,
31
+ queryKey: ['models'],
32
+ });
33
+ useEffect(() => {
34
+ if (configQuery.data) {
35
+ setModel(configQuery.data.models[0].id);
36
+ // Enable all builtin tools by default
37
+ const allToolIds = configQuery.data.builtinTools?.map(tool => tool.id) || [];
38
+ setEnabledTools(allToolIds);
39
+ }
40
+ }, [configQuery.data]);
41
+ // Auto-scroll to bottom when new messages arrive
42
+ useEffect(() => {
43
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
44
+ }, [messages]);
45
+ // Handle scroll to show/hide scroll button
46
+ useEffect(() => {
47
+ const container = scrollContainerRef.current;
48
+ if (!container)
49
+ return;
50
+ const handleScroll = () => {
51
+ const isScrolledUp = container.scrollHeight - container.scrollTop - container.clientHeight >
52
+ 100;
53
+ setShowScrollButton(isScrolledUp);
54
+ };
55
+ container.addEventListener('scroll', handleScroll);
56
+ return () => container.removeEventListener('scroll', handleScroll);
57
+ }, []);
58
+ const handleSubmit = (e) => {
59
+ e.preventDefault();
60
+ if (inputValue.trim() && status !== 'streaming') {
61
+ sendMessage({ text: inputValue }, {
62
+ body: { model, builtinTools: enabledTools },
63
+ }).catch((error) => {
64
+ console.error('Error sending message:', error);
65
+ });
66
+ setInputValue('');
67
+ textareaRef.current?.focus();
68
+ }
69
+ };
70
+ const handleKeyDown = (e) => {
71
+ if (e.key === 'Enter' && !e.shiftKey) {
72
+ e.preventDefault();
73
+ handleSubmit(e);
74
+ }
75
+ };
76
+ const regen = (id) => {
77
+ regenerate({ messageId: id }).catch((error) => {
78
+ console.error('Error regenerating message:', error);
79
+ });
80
+ };
81
+ const scrollToBottom = () => {
82
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
83
+ };
84
+ const availableTools = useMemo(() => {
85
+ if (!configQuery.data)
86
+ return [];
87
+ const selectedModel = configQuery.data.models.find(entry => entry.id === model);
88
+ const enabledToolIds = selectedModel?.builtinTools ?? [];
89
+ // If model doesn't specify tools, show all builtin tools
90
+ const tools = enabledToolIds.length > 0
91
+ ? (configQuery.data.builtinTools?.filter(tool => enabledToolIds.includes(tool.id)) ?? [])
92
+ : (configQuery.data.builtinTools ?? []);
93
+ return tools;
94
+ }, [configQuery.data, model]);
95
+ return (_jsxs(Box, { className: "dla-chat-root", sx: {
96
+ position: 'absolute',
97
+ top: 0,
98
+ left: 0,
99
+ right: 0,
100
+ bottom: 0,
101
+ display: 'flex',
102
+ flexDirection: 'column',
103
+ }, children: [_jsxs(Box, { ref: scrollContainerRef, className: "dla-chat-messages", sx: {
104
+ position: 'absolute',
105
+ top: 0,
106
+ left: 0,
107
+ right: 0,
108
+ bottom: 0,
109
+ overflowY: 'scroll',
110
+ overflowX: 'hidden',
111
+ padding: 3,
112
+ paddingBottom: '180px',
113
+ }, children: [messages.map(message => (_jsx(Box, { sx: { marginBottom: 3 }, children: message.parts.map((part, index) => (_jsx(MessagePart, { part: part, message: message, status: status, index: index, regen: regen, lastMessage: message.id === messages.at(-1)?.id }, `${message.id}-${index}`))) }, message.id))), status === 'submitted' && (_jsx(Box, { sx: { display: 'flex', justifyContent: 'center', padding: 3 }, children: _jsx(Spinner, { size: "medium" }) })), _jsx("div", { ref: messagesEndRef })] }), showScrollButton && (_jsx(Box, { sx: {
114
+ position: 'absolute',
115
+ bottom: 160,
116
+ right: 16,
117
+ zIndex: 1,
118
+ }, children: _jsx(IconButton, { icon: ArrowDownIcon, "aria-label": "Scroll to bottom", onClick: scrollToBottom, variant: "default", size: "medium" }) })), _jsx(Box, { sx: {
119
+ position: 'absolute',
120
+ bottom: 0,
121
+ left: 0,
122
+ right: 0,
123
+ borderTop: '1px solid',
124
+ borderColor: 'border.default',
125
+ padding: 3,
126
+ backgroundColor: 'canvas.default',
127
+ zIndex: 0,
128
+ }, children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(FormControl, { sx: { width: '100%' }, children: _jsx(Textarea, { ref: textareaRef, value: inputValue, onChange: e => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: "Ask me anything...", rows: 3, resize: "vertical", sx: { marginBottom: 2, width: '100%' } }) }), _jsxs(Box, { sx: {
129
+ display: 'flex',
130
+ justifyContent: 'space-between',
131
+ alignItems: 'center',
132
+ }, children: [_jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center' }, children: [availableTools.length > 0 && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(IconButton, { icon: ToolsIcon, "aria-label": "Tools", variant: "invisible", size: "small" }) }), _jsx(ActionMenu.Overlay, { children: _jsx(ActionList, { children: _jsx(ActionList.Group, { title: "Available Tools", children: availableTools.map(tool => (_jsxs(ActionList.Item, { disabled: true, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(Box, { sx: {
133
+ width: 8,
134
+ height: 8,
135
+ borderRadius: '50%',
136
+ backgroundColor: 'success.emphasis',
137
+ } }) }), tool.name] }, tool.id))) }) }) })] })), configQuery.data && model && (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsx(Button, { type: "button", variant: "invisible", size: "small", leadingVisual: AiModelIcon, children: _jsx(Text, { sx: { fontSize: 0 }, children: configQuery.data.models.find(m => m.id === model)
138
+ ?.name || 'Select Model' }) }) }), _jsx(ActionMenu.Overlay, { children: _jsx(ActionList, { selectionVariant: "single", children: configQuery.data.models.map(modelItem => (_jsx(ActionList.Item, { selected: model === modelItem.id, onSelect: () => setModel(modelItem.id), children: modelItem.name }, modelItem.id))) }) })] }))] }), _jsx(Button, { type: "submit", variant: "primary", size: "small", disabled: !inputValue.trim() || status === 'streaming', leadingVisual: ArrowUpIcon, children: status === 'streaming' ? 'Sending...' : 'Send' })] })] }) })] }));
139
+ };
@@ -0,0 +1,11 @@
1
+ import type { UIDataTypes, UIMessagePart, UITools, UIMessage } from 'ai';
2
+ interface IMessagePartProps {
3
+ part: UIMessagePart<UIDataTypes, UITools>;
4
+ message: UIMessage;
5
+ status: string;
6
+ regen: (id: string) => void;
7
+ index: number;
8
+ lastMessage: boolean;
9
+ }
10
+ export declare function MessagePart({ part, message, status, regen, index, lastMessage, }: IMessagePartProps): import("react/jsx-runtime").JSX.Element | null;
11
+ export {};
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { TextPart } from './display/TextPart';
3
+ import { ReasoningPart } from './display/ReasoningPart';
4
+ import { ToolPart } from './display/ToolPart';
5
+ import { DynamicToolPart } from './display/DynamicToolPart';
6
+ export function MessagePart({ part, message, status, regen, index, lastMessage, }) {
7
+ if (part.type === 'text') {
8
+ return (_jsx(TextPart, { text: part.text, message: message, isLastPart: index === message.parts.length - 1, onRegenerate: regen }));
9
+ }
10
+ else if (part.type === 'reasoning') {
11
+ const isStreaming = status === 'streaming' &&
12
+ index === message.parts.length - 1 &&
13
+ lastMessage;
14
+ return _jsx(ReasoningPart, { text: part.text, isStreaming: isStreaming });
15
+ }
16
+ else if (part.type === 'dynamic-tool') {
17
+ return _jsx(DynamicToolPart, { part: part });
18
+ }
19
+ else if ('toolCallId' in part) {
20
+ return _jsx(ToolPart, { part: part });
21
+ }
22
+ return null;
23
+ }
@@ -0,0 +1,6 @@
1
+ import type { DynamicToolUIPart } from 'ai';
2
+ interface IDynamicToolPartProps {
3
+ part: DynamicToolUIPart;
4
+ }
5
+ export declare function DynamicToolPart({ part }: IDynamicToolPartProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,5 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text, Flash } from '@primer/react';
3
+ export function DynamicToolPart({ part }) {
4
+ return (_jsx(Flash, { variant: "warning", sx: { marginBottom: 2 }, children: _jsxs(Text, { children: ["Dynamic Tool: ", JSON.stringify(part)] }) }));
5
+ }
@@ -0,0 +1,6 @@
1
+ interface IReasoningPartProps {
2
+ text: string;
3
+ isStreaming: boolean;
4
+ }
5
+ export declare function ReasoningPart({ text, isStreaming }: IReasoningPartProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2024-2025 Datalayer, Inc.
4
+ *
5
+ * BSD 3-Clause License
6
+ */
7
+ import React from 'react';
8
+ import { Text, Button } from '@primer/react';
9
+ import { Box } from '@datalayer/primer-addons';
10
+ import { ChevronDownIcon } from '@primer/octicons-react';
11
+ import { Streamdown } from 'streamdown';
12
+ export function ReasoningPart({ text, isStreaming }) {
13
+ const [isExpanded, setIsExpanded] = React.useState(false);
14
+ // Auto-close after streaming ends (with delay)
15
+ React.useEffect(() => {
16
+ if (!isStreaming && isExpanded) {
17
+ const timer = setTimeout(() => {
18
+ setIsExpanded(false);
19
+ }, 1000);
20
+ return () => clearTimeout(timer);
21
+ }
22
+ }, [isStreaming, isExpanded]);
23
+ return (_jsxs(Box, { sx: { marginBottom: 3 }, children: [_jsxs(Button, { variant: "invisible", size: "small", onClick: () => setIsExpanded(!isExpanded), sx: {
24
+ width: '100%',
25
+ display: 'flex',
26
+ gap: 2,
27
+ justifyContent: 'flex-start',
28
+ alignItems: 'center',
29
+ paddingX: 0,
30
+ paddingY: 1,
31
+ color: 'fg.muted',
32
+ border: 'none',
33
+ '&:hover': {
34
+ color: 'fg.default',
35
+ },
36
+ }, children: [_jsx(Text, { sx: { fontSize: 1 }, children: "\uD83E\uDDE0" }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'normal' }, children: isStreaming ? 'Thinking...' : 'Reasoning' }), _jsx(Box, { as: "span", sx: {
37
+ display: 'inline-flex',
38
+ marginLeft: 'auto',
39
+ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
40
+ transition: 'transform 0.2s',
41
+ }, children: _jsx(ChevronDownIcon, {}) })] }), isExpanded && (_jsx(Box, { sx: {
42
+ marginTop: 2,
43
+ padding: 3,
44
+ backgroundColor: 'canvas.inset',
45
+ borderRadius: 2,
46
+ border: '1px solid',
47
+ borderColor: 'border.default',
48
+ fontSize: 1,
49
+ lineHeight: 1.6,
50
+ color: 'fg.muted',
51
+ '& > *:first-child': { marginTop: 0 },
52
+ '& > *:last-child': { marginBottom: 0 },
53
+ }, children: _jsx(Streamdown, { children: text }) }))] }));
54
+ }
@@ -0,0 +1,9 @@
1
+ import type { UIMessage } from 'ai';
2
+ interface ITextPartProps {
3
+ text: string;
4
+ message: UIMessage;
5
+ isLastPart: boolean;
6
+ onRegenerate: (id: string) => void;
7
+ }
8
+ export declare function TextPart({ text, message, isLastPart, onRegenerate, }: ITextPartProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, IconButton } from '@primer/react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ import { CopyIcon, SyncIcon } from '@primer/octicons-react';
5
+ import { Streamdown } from 'streamdown';
6
+ export function TextPart({ text, message, isLastPart, onRegenerate, }) {
7
+ const copy = (text) => {
8
+ navigator.clipboard.writeText(text).catch((error) => {
9
+ console.error('Error copying text:', error);
10
+ });
11
+ };
12
+ return (_jsxs(Box, { sx: {
13
+ padding: 3,
14
+ borderRadius: 2,
15
+ backgroundColor: message.role === 'user' ? 'accent.subtle' : 'canvas.subtle',
16
+ marginBottom: 2,
17
+ }, children: [_jsxs(Box, { sx: {
18
+ display: 'flex',
19
+ justifyContent: 'space-between',
20
+ alignItems: 'flex-start',
21
+ marginBottom: 2,
22
+ }, children: [_jsx(Text, { sx: {
23
+ fontWeight: 'bold',
24
+ fontSize: 1,
25
+ color: 'fg.muted',
26
+ textTransform: 'uppercase',
27
+ }, children: message.role === 'user' ? 'You' : 'Assistant' }), message.role === 'assistant' && isLastPart && (_jsxs(Box, { sx: { display: 'flex', gap: 1 }, children: [_jsx(IconButton, { icon: SyncIcon, "aria-label": "Regenerate", size: "small", variant: "invisible", onClick: () => onRegenerate(message.id) }), _jsx(IconButton, { icon: CopyIcon, "aria-label": "Copy", size: "small", variant: "invisible", onClick: () => copy(text) })] }))] }), _jsx(Box, { sx: {
28
+ fontSize: 1,
29
+ lineHeight: 1.6,
30
+ '& > *:first-child': { marginTop: 0 },
31
+ '& > *:last-child': { marginBottom: 0 },
32
+ '& p': { marginTop: 0, marginBottom: '1em' },
33
+ '& h1, & h2, & h3, & h4, & h5, & h6': {
34
+ marginTop: '1em',
35
+ marginBottom: '0.5em',
36
+ fontWeight: 'bold',
37
+ },
38
+ '& ul, & ol': {
39
+ marginTop: '0.5em',
40
+ marginBottom: '0.5em',
41
+ paddingLeft: '1.5em',
42
+ },
43
+ '& code': {
44
+ backgroundColor: 'neutral.muted',
45
+ padding: '2px 4px',
46
+ borderRadius: 1,
47
+ fontSize: '0.9em',
48
+ fontFamily: 'mono',
49
+ },
50
+ '& pre': {
51
+ backgroundColor: 'canvas.inset',
52
+ padding: 3,
53
+ borderRadius: 2,
54
+ overflow: 'auto',
55
+ marginTop: '1em',
56
+ marginBottom: '1em',
57
+ border: '1px solid',
58
+ borderColor: 'border.default',
59
+ },
60
+ '& pre code': {
61
+ backgroundColor: 'transparent',
62
+ padding: 0,
63
+ fontSize: '0.875em',
64
+ },
65
+ '& blockquote': {
66
+ borderLeft: '3px solid',
67
+ borderColor: 'border.default',
68
+ paddingLeft: 3,
69
+ marginLeft: 0,
70
+ color: 'fg.muted',
71
+ },
72
+ '& a': {
73
+ color: 'accent.fg',
74
+ textDecoration: 'underline',
75
+ },
76
+ '& table': {
77
+ width: '100%',
78
+ borderCollapse: 'collapse',
79
+ marginTop: '1em',
80
+ marginBottom: '1em',
81
+ },
82
+ '& th, & td': {
83
+ border: '1px solid',
84
+ borderColor: 'border.default',
85
+ padding: 2,
86
+ textAlign: 'left',
87
+ },
88
+ '& th': {
89
+ backgroundColor: 'canvas.subtle',
90
+ fontWeight: 'bold',
91
+ },
92
+ }, children: _jsx(Streamdown, { children: text }) })] }));
93
+ }
@@ -0,0 +1,6 @@
1
+ import type { ToolUIPart } from 'ai';
2
+ interface IToolPartProps {
3
+ part: ToolUIPart;
4
+ }
5
+ export declare function ToolPart({ part }: IToolPartProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,144 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2024-2025 Datalayer, Inc.
4
+ *
5
+ * BSD 3-Clause License
6
+ */
7
+ import React from 'react';
8
+ import { Text, Button } from '@primer/react';
9
+ import { Box } from '@datalayer/primer-addons';
10
+ import { ChevronDownIcon } from '@primer/octicons-react';
11
+ export function ToolPart({ part }) {
12
+ const [isExpanded, setIsExpanded] = React.useState(true);
13
+ const getStatusInfo = (state) => {
14
+ const statusMap = {
15
+ call: { label: 'Pending', color: 'accent.fg', icon: '○' },
16
+ 'input-streaming': { label: 'Pending', color: 'accent.fg', icon: '○' },
17
+ 'input-available': {
18
+ label: 'Running',
19
+ color: 'attention.fg',
20
+ icon: '⏱',
21
+ },
22
+ executing: { label: 'Running', color: 'attention.fg', icon: '⏱' },
23
+ 'output-available': {
24
+ label: 'Completed',
25
+ color: 'success.fg',
26
+ icon: '✓',
27
+ },
28
+ 'output-error': { label: 'Error', color: 'danger.fg', icon: '✕' },
29
+ error: { label: 'Error', color: 'danger.fg', icon: '✕' },
30
+ };
31
+ return (statusMap[state] || {
32
+ label: state,
33
+ color: 'fg.muted',
34
+ icon: '•',
35
+ });
36
+ };
37
+ const statusInfo = getStatusInfo(part.state);
38
+ const toolName = part.type.split('-').slice(1).join('-') || part.type;
39
+ return (_jsxs(Box, { sx: {
40
+ marginBottom: 2,
41
+ border: '1px solid',
42
+ borderColor: 'border.default',
43
+ borderRadius: 2,
44
+ overflow: 'hidden',
45
+ }, children: [_jsxs(Button, { variant: "invisible", onClick: () => setIsExpanded(!isExpanded), sx: {
46
+ width: '100%',
47
+ display: 'flex',
48
+ justifyContent: 'space-between',
49
+ alignItems: 'center',
50
+ padding: 3,
51
+ backgroundColor: 'canvas.subtle',
52
+ border: 'none',
53
+ borderBottom: isExpanded ? '1px solid' : 'none',
54
+ borderColor: 'border.default',
55
+ textAlign: 'left',
56
+ cursor: 'pointer',
57
+ '&:hover': {
58
+ backgroundColor: 'neutral.muted',
59
+ },
60
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "\uD83D\uDD27" }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: toolName }), _jsxs(Box, { sx: {
61
+ display: 'inline-flex',
62
+ alignItems: 'center',
63
+ gap: 1,
64
+ paddingX: 2,
65
+ paddingY: 1,
66
+ borderRadius: 2,
67
+ backgroundColor: 'neutral.subtle',
68
+ fontSize: 0,
69
+ }, children: [_jsx(Text, { sx: { color: statusInfo.color }, children: statusInfo.icon }), _jsx(Text, { sx: { color: statusInfo.color, fontWeight: 'semibold' }, children: statusInfo.label })] })] }), _jsx(Box, { as: "span", sx: {
70
+ display: 'inline-flex',
71
+ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
72
+ transition: 'transform 0.2s',
73
+ }, children: _jsx(ChevronDownIcon, {}) })] }), isExpanded && (_jsxs(Box, { children: [_jsxs(Box, { sx: {
74
+ padding: 3,
75
+ borderBottom: part.state === 'output-available' ||
76
+ part.state === 'output-error'
77
+ ? '1px solid'
78
+ : 'none',
79
+ borderColor: 'border.default',
80
+ }, children: [_jsx(Text, { sx: {
81
+ display: 'block',
82
+ fontSize: 0,
83
+ fontWeight: 'semibold',
84
+ color: 'fg.muted',
85
+ textTransform: 'uppercase',
86
+ letterSpacing: '0.05em',
87
+ marginBottom: 2,
88
+ }, children: "Parameters" }), _jsx(Box, { sx: {
89
+ backgroundColor: 'canvas.inset',
90
+ borderRadius: 2,
91
+ overflow: 'auto',
92
+ border: '1px solid',
93
+ borderColor: 'border.default',
94
+ }, children: _jsx("pre", { style: {
95
+ margin: 0,
96
+ padding: '12px',
97
+ fontSize: '12px',
98
+ fontFamily: 'monospace',
99
+ lineHeight: 1.5,
100
+ overflow: 'auto',
101
+ }, children: JSON.stringify(part.input, null, 2) }) })] }), part.state === 'output-available' && (_jsxs(Box, { sx: { padding: 3 }, children: [_jsx(Text, { sx: {
102
+ display: 'block',
103
+ fontSize: 0,
104
+ fontWeight: 'semibold',
105
+ color: 'fg.muted',
106
+ textTransform: 'uppercase',
107
+ letterSpacing: '0.05em',
108
+ marginBottom: 2,
109
+ }, children: "Result" }), _jsx(Box, { sx: {
110
+ backgroundColor: 'canvas.default',
111
+ borderRadius: 2,
112
+ overflow: 'auto',
113
+ border: '1px solid',
114
+ borderColor: 'border.default',
115
+ }, children: _jsx("pre", { style: {
116
+ margin: 0,
117
+ padding: '12px',
118
+ fontSize: '12px',
119
+ fontFamily: 'monospace',
120
+ lineHeight: 1.5,
121
+ overflow: 'auto',
122
+ }, children: part.output
123
+ ? typeof part.output === 'string'
124
+ ? part.output
125
+ : JSON.stringify(part.output, null, 2)
126
+ : 'No output' }) })] })), part.state === 'output-error' &&
127
+ 'errorText' in part &&
128
+ part.errorText && (_jsxs(Box, { sx: { padding: 3 }, children: [_jsx(Text, { sx: {
129
+ display: 'block',
130
+ fontSize: 0,
131
+ fontWeight: 'semibold',
132
+ color: 'danger.fg',
133
+ textTransform: 'uppercase',
134
+ letterSpacing: '0.05em',
135
+ marginBottom: 2,
136
+ }, children: "Error" }), _jsx(Box, { sx: {
137
+ backgroundColor: 'danger.subtle',
138
+ borderRadius: 2,
139
+ overflow: 'auto',
140
+ border: '1px solid',
141
+ borderColor: 'danger.muted',
142
+ padding: 2,
143
+ }, children: _jsx(Text, { sx: { fontSize: 0, color: 'danger.fg' }, children: part.errorText }) })] }))] }))] }));
144
+ }
@@ -0,0 +1,4 @@
1
+ export { TextPart } from './TextPart';
2
+ export { ReasoningPart } from './ReasoningPart';
3
+ export { ToolPart } from './ToolPart';
4
+ export { DynamicToolPart } from './DynamicToolPart';
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Copyright (c) 2024-2025 Datalayer, Inc.
3
+ *
4
+ * BSD 3-Clause License
5
+ */
6
+ export { TextPart } from './TextPart';
7
+ export { ReasoningPart } from './ReasoningPart';
8
+ export { ToolPart } from './ToolPart';
9
+ export { DynamicToolPart } from './DynamicToolPart';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Call the API extension
3
+ *
4
+ * @param endPoint API REST end point for the extension
5
+ * @param init Initial values for the request
6
+ * @returns The response body interpreted as JSON
7
+ */
8
+ export declare function requestAPI<T>(endPoint?: string, init?: RequestInit): Promise<T>;
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (c) 2024-2025 Datalayer, Inc.
3
+ *
4
+ * BSD 3-Clause License
5
+ */
6
+ import { URLExt } from '@jupyterlab/coreutils';
7
+ import { ServerConnection } from '@jupyterlab/services';
8
+ /**
9
+ * Call the API extension
10
+ *
11
+ * @param endPoint API REST end point for the extension
12
+ * @param init Initial values for the request
13
+ * @returns The response body interpreted as JSON
14
+ */
15
+ export async function requestAPI(endPoint = '', init = {}) {
16
+ // Make request to Jupyter API
17
+ const settings = ServerConnection.makeSettings();
18
+ const requestUrl = URLExt.join(settings.baseUrl, 'datalayer', endPoint);
19
+ let response;
20
+ try {
21
+ response = await ServerConnection.makeRequest(requestUrl, init, settings);
22
+ }
23
+ catch (error) {
24
+ throw new ServerConnection.NetworkError(error);
25
+ }
26
+ let data = await response.text();
27
+ if (data.length > 0) {
28
+ try {
29
+ data = JSON.parse(data);
30
+ }
31
+ catch {
32
+ // Not a JSON response body
33
+ }
34
+ }
35
+ if (!response.ok) {
36
+ throw new ServerConnection.ResponseError(response, data.message || data);
37
+ }
38
+ return data;
39
+ }
@@ -0,0 +1,4 @@
1
+ export * from './ChatComponent';
2
+ export * from './MessagePart';
3
+ export * from './handler';
4
+ export * from './display';
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Copyright (c) 2024-2025 Datalayer, Inc.
3
+ *
4
+ * BSD 3-Clause License
5
+ */
6
+ export * from './ChatComponent';
7
+ export * from './MessagePart';
8
+ export * from './handler';
9
+ export * from './display';
@@ -1 +1 @@
1
- export {};
1
+ export * from './chat';
@@ -1,5 +1,5 @@
1
- export {};
2
1
  /*
3
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
3
  * Distributed under the terms of the Modified BSD License.
5
4
  */
5
+ export * from './chat';
@@ -26,6 +26,10 @@ interface IRuntimeSimplePickerProps {
26
26
  * Connection to the active runtime.
27
27
  */
28
28
  sessionConnection?: Session.ISessionConnection;
29
+ /**
30
+ * Whether browser runtime is available.
31
+ */
32
+ browserRuntimeAvailable?: boolean;
29
33
  }
30
34
  /**
31
35
  * Runtime simple picker component.