@datalayer/agent-runtimes 0.0.12 → 1.0.1
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/Agent.js +1 -2
- package/lib/AgentLexical.d.ts +0 -1
- package/lib/AgentLexical.js +1 -3
- package/lib/AgentNotebook.js +1 -2
- package/lib/components/chat/components/ChatFloating.d.ts +4 -2
- package/lib/components/chat/components/ChatFloating.js +1 -1
- package/lib/examples/A2UiRestaurantExample.js +2 -2
- package/lib/examples/AgUiAgenticExample.js +3 -3
- package/lib/examples/AgUiBackendToolRenderingExample.js +4 -3
- package/lib/examples/AgUiHaikuGenUIExample.js +4 -3
- package/lib/examples/AgUiHumanInTheLoopExample.js +4 -3
- package/lib/examples/AgUiSharedStateExample.js +4 -3
- package/lib/examples/AgUiToolsBasedGenUIExample.js +4 -3
- package/lib/examples/AgentRuntimeChatExample.js +3 -3
- package/lib/examples/AgentRuntimeCustomExample.js +2 -2
- package/lib/examples/AgentRuntimeFormExample.js +2 -2
- package/lib/examples/AgentRuntimeLexical2Example.d.ts +0 -1
- package/lib/examples/AgentRuntimeLexical2Example.js +3 -3
- package/lib/examples/AgentRuntimeLexicalExample.d.ts +0 -1
- package/lib/examples/AgentRuntimeLexicalExample.js +4 -4
- package/lib/examples/AgentRuntimeLexicalSidebarExample.d.ts +0 -1
- package/lib/examples/AgentRuntimeLexicalSidebarExample.js +3 -3
- package/lib/examples/AgentRuntimeNotebookExample.js +4 -3
- package/lib/examples/AgentRuntimeNotebookSidebarExample.js +3 -2
- package/lib/examples/AgentRuntimeStandaloneExample.js +3 -3
- package/lib/examples/CopilotKitLexicalExample.d.ts +0 -1
- package/lib/examples/CopilotKitLexicalExample.js +3 -3
- package/lib/examples/CopilotKitNotebookExample.js +3 -2
- package/lib/examples/DatalayerNotebookExample.js +3 -2
- package/lib/examples/JupyterCellExample.js +3 -2
- package/lib/examples/JupyterNotebookExample.js +3 -2
- package/lib/examples/main.js +68 -18
- package/lib/examples/stores/themeStore.d.ts +33 -0
- package/lib/examples/stores/themeStore.js +38 -0
- package/lib/examples/stores/themedProvider.d.ts +45 -0
- package/lib/examples/stores/themedProvider.js +54 -0
- package/lib/lexical/ChatInlinePlugin.js +28 -7
- package/lib/lexical/index.d.ts +1 -1
- package/lib/lexical/useChatInlineToolbarItems.d.ts +9 -2
- package/lib/lexical/useChatInlineToolbarItems.js +22 -94
- package/package.json +4 -3
|
@@ -47,13 +47,13 @@ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
|
|
|
47
47
|
import { CopilotKit, useFrontendTool } from '@copilotkit/react-core';
|
|
48
48
|
import { CopilotSidebar } from '@copilotkit/react-ui';
|
|
49
49
|
import { Box } from '@datalayer/primer-addons';
|
|
50
|
-
import {
|
|
50
|
+
import { useJupyter } from '@datalayer/jupyter-react';
|
|
51
|
+
import { ThemedJupyterProvider } from './stores/themedProvider';
|
|
51
52
|
import { ComponentPickerMenuPlugin, JupyterCellPlugin, JupyterInputOutputPlugin, DraggableBlockPlugin, ImagesPlugin, HorizontalRulePlugin, EquationsPlugin, YouTubePlugin, ExcalidrawPlugin, CollapsiblePlugin, AutoLinkPlugin, AutoEmbedPlugin, LexicalConfigProvider, LexicalStatePlugin, FloatingTextFormatToolbarPlugin, CodeActionMenuPlugin, ListMaxIndentLevelPlugin, TableCellResizerPlugin, TablePlugin, } from '@datalayer/jupyter-lexical';
|
|
52
53
|
import { ActionRegistrar, useLexicalToolActions, } from '../tools/adapters/copilotkit/lexicalHooks';
|
|
53
54
|
import { editorConfig } from './lexical/editorConfig';
|
|
54
55
|
import '@datalayer/jupyter-lexical/style/index.css';
|
|
55
56
|
import '@copilotkit/react-ui/styles.css';
|
|
56
|
-
import '@datalayer/jupyter-lexical/style/modal-overrides.css';
|
|
57
57
|
import './lexical/lexical-theme.css';
|
|
58
58
|
// Fixed lexical document ID
|
|
59
59
|
const LEXICAL_ID = 'agui-lexical-example';
|
|
@@ -147,7 +147,7 @@ const LexicalUI = React.memo(function LexicalUI({ content = INITIAL_CONTENT, ser
|
|
|
147
147
|
padding: 3,
|
|
148
148
|
backgroundColor: 'canvas.default',
|
|
149
149
|
minHeight: '600px',
|
|
150
|
-
}, children: _jsx(LexicalConfigProvider, { lexicalId: LEXICAL_ID, serviceManager: serviceManager, children: _jsx(LexicalComposer, { initialConfig: editorConfig, children: _jsxs("div", { className: "lexical-editor-inner", ref: onRef, children: [_jsx(LexicalStatePlugin, {}), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "lexical-editor-content", "aria-label": "Lexical Editor" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(OnChangePlugin, { onChange: handleChange }), _jsx(HistoryPlugin, {}), _jsx(AutoFocusPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(LinkPlugin, {}), _jsx(AutoLinkPlugin, {}), _jsx(TablePlugin, {}), _jsx(TableCellResizerPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), _jsx(MarkdownShortcutPlugin, { transformers: TRANSFORMERS }), _jsx(LoadContentPlugin, { content: content }), _jsx(CodeHighlightPlugin, {}), _jsx(ImagesPlugin, { captionsEnabled: false }), _jsx(HorizontalRulePlugin, {}), _jsx(EquationsPlugin, {}), _jsx(YouTubePlugin, {}), _jsx(ExcalidrawPlugin, {}), _jsx(CollapsiblePlugin, {}), _jsx(AutoEmbedPlugin, {}), _jsx(JupyterCellPlugin, {}), _jsx(
|
|
150
|
+
}, children: _jsx(LexicalConfigProvider, { lexicalId: LEXICAL_ID, serviceManager: serviceManager, children: _jsx(LexicalComposer, { initialConfig: editorConfig, children: _jsxs("div", { className: "lexical-editor-inner", ref: onRef, children: [_jsx(LexicalStatePlugin, {}), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "lexical-editor-content", "aria-label": "Lexical Editor" }), ErrorBoundary: LexicalErrorBoundary }), _jsx(OnChangePlugin, { onChange: handleChange }), _jsx(HistoryPlugin, {}), _jsx(AutoFocusPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(LinkPlugin, {}), _jsx(AutoLinkPlugin, {}), _jsx(TablePlugin, {}), _jsx(TableCellResizerPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 7 }), _jsx(MarkdownShortcutPlugin, { transformers: TRANSFORMERS }), _jsx(LoadContentPlugin, { content: content }), _jsx(CodeHighlightPlugin, {}), _jsx(ImagesPlugin, { captionsEnabled: false }), _jsx(HorizontalRulePlugin, {}), _jsx(EquationsPlugin, {}), _jsx(YouTubePlugin, {}), _jsx(ExcalidrawPlugin, {}), _jsx(CollapsiblePlugin, {}), _jsx(AutoEmbedPlugin, {}), _jsx(JupyterCellPlugin, {}), _jsx(ThemedJupyterProvider, { children: _jsx(SimpleKernelPluginsInner, {}) }), floatingAnchorElem && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem, setIsLinkEditMode: setIsLinkEditMode }), _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem })] }))] }) }) }) })] }) }));
|
|
151
151
|
});
|
|
152
152
|
function LexicalWithTools({ content, serviceManager, }) {
|
|
153
153
|
// Get all actions for this lexical document
|
|
@@ -16,7 +16,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
16
16
|
*/
|
|
17
17
|
import React from 'react';
|
|
18
18
|
import { Box } from '@datalayer/primer-addons';
|
|
19
|
-
import {
|
|
19
|
+
import { Notebook } from '@datalayer/jupyter-react';
|
|
20
|
+
import { ThemedJupyterProvider } from './stores/themedProvider';
|
|
20
21
|
// CopilotKit imports
|
|
21
22
|
import { CopilotKit, useFrontendTool } from '@copilotkit/react-core';
|
|
22
23
|
import { CopilotSidebar } from '@copilotkit/react-ui';
|
|
@@ -50,7 +51,7 @@ const NotebookUI = React.memo(function NotebookUI({ serviceManager, }) {
|
|
|
50
51
|
borderRadius: 8,
|
|
51
52
|
padding: 24,
|
|
52
53
|
backgroundColor: 'var(--bgColor-default)',
|
|
53
|
-
}, children: serviceManager ? (_jsx(
|
|
54
|
+
}, children: serviceManager ? (_jsx(ThemedJupyterProvider, { children: _jsx(Notebook, { nbformat: NOTEBOOK_CONTENT, id: NOTEBOOK_ID, serviceManager: serviceManager, height: "600px", cellSidebarMargin: 120, startDefaultKernel: true }) })) : (_jsx(Box, { style: { padding: 24 }, children: _jsx("p", { children: "Loading service manager..." }) })) })] }) }));
|
|
54
55
|
});
|
|
55
56
|
/**
|
|
56
57
|
* Component to register actions with CopilotKit.
|
|
@@ -7,7 +7,8 @@ import { useState, useEffect, useMemo } from 'react';
|
|
|
7
7
|
import { Checkbox, FormControl, Heading } from '@primer/react';
|
|
8
8
|
import { Box } from '@datalayer/primer-addons';
|
|
9
9
|
import { useCoreStore, createDatalayerServiceManager, DatalayerCollaborationProvider, } from '@datalayer/core';
|
|
10
|
-
import { loadJupyterConfig,
|
|
10
|
+
import { loadJupyterConfig, Notebook } from '@datalayer/jupyter-react';
|
|
11
|
+
import { ThemedJupyterProvider } from './stores/themedProvider';
|
|
11
12
|
import nbformatExample from './stores/notebooks/NotebookExample1.ipynb.json';
|
|
12
13
|
// This corresponds to the notebook ID in the URL when you open an existing notbook in your library
|
|
13
14
|
const NOTEBOOK_ID = '01JZQRQ35GG871QQCZW9TB1A8J';
|
|
@@ -64,7 +65,7 @@ const DatalayerNotebookExample = (props) => {
|
|
|
64
65
|
token,
|
|
65
66
|
});
|
|
66
67
|
}, [enableCollaboration, configuration]);
|
|
67
|
-
return (_jsx(
|
|
68
|
+
return (_jsx(ThemedJupyterProvider, { children: _jsxs(Box, { p: 3, children: [_jsx(Heading, { as: "h2", sx: { mb: 3 }, children: "Datalayer Notebook Collaboration Example" }), _jsx(Box, { sx: { mb: 3 }, children: _jsxs(FormControl, { children: [_jsx(Checkbox, { checked: enableCollaboration, onChange: e => setEnableCollaboration(e.target.checked) }), _jsx(FormControl.Label, { children: "Enable Datalayer Collaboration" })] }) }), (!configuration?.runUrl || !configuration?.token) && (_jsx(Box, { sx: { mb: 2, p: 2, bg: 'danger.subtle' }, children: "Warning: Datalayer configuration is missing. Please configure runUrl and token to use DatalayerServiceManager and collaboration features." })), !serviceManager && (_jsx(Box, { sx: { mb: 2, p: 2, bg: 'attention.subtle' }, children: "Note: DatalayerServiceManager is not available. Notebook functionality will be limited." })), enableCollaboration ? (_jsxs(Box, { sx: {
|
|
68
69
|
display: 'flex',
|
|
69
70
|
gap: 2,
|
|
70
71
|
flexDirection: 'row',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Cell, KernelIndicator, useJupyter, useKernelsStore, useCellsStore, } from '@datalayer/jupyter-react';
|
|
3
|
+
import { ThemedJupyterProvider } from './stores/themedProvider';
|
|
3
4
|
import { Button, Label } from '@primer/react';
|
|
4
5
|
import { Box } from '@datalayer/primer-addons';
|
|
5
6
|
const CELL_ID = 'cell-example-1';
|
|
@@ -14,6 +15,6 @@ const JupyterCellExampleContent = () => {
|
|
|
14
15
|
return (_jsxs(Box, { p: 4, children: [_jsx(Box, { as: "h1", children: "Jupyter Cell Example" }), _jsxs(Box, { children: ["Source: ", cellsStore.getSource(CELL_ID)] }), _jsxs(Box, { children: ["Outputs Count: ", cellsStore.getOutputsCount(CELL_ID)] }), _jsxs(Box, { children: ["Kernel State:", ' ', _jsx(Label, { children: defaultKernel && kernelsStore.getExecutionState(defaultKernel.id) })] }), _jsxs(Box, { children: ["Kernel Phase:", ' ', _jsx(Label, { children: defaultKernel && kernelsStore.getExecutionPhase(defaultKernel.id) })] }), _jsxs(Box, { display: "flex", children: [_jsx(Box, { children: "Kernel Indicator:" }), _jsx(Box, { ml: 3, children: _jsx(KernelIndicator, { kernel: defaultKernel && defaultKernel.connection }) })] }), _jsx(Box, { children: _jsx(Button, { onClick: () => cellsStore.execute(CELL_ID), children: "Run cell" }) }), _jsx(Cell, { source: DEFAULT_SOURCE, id: CELL_ID, kernel: defaultKernel })] }));
|
|
15
16
|
};
|
|
16
17
|
export const JupyterCellExample = (props) => {
|
|
17
|
-
return (_jsx(
|
|
18
|
+
return (_jsx(ThemedJupyterProvider, { children: _jsx(JupyterCellExampleContent, {}) }));
|
|
18
19
|
};
|
|
19
20
|
export default JupyterCellExample;
|
|
@@ -10,12 +10,13 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
10
10
|
*/
|
|
11
11
|
import { useMemo } from 'react';
|
|
12
12
|
import { Box } from '@datalayer/primer-addons';
|
|
13
|
-
import {
|
|
13
|
+
import { Notebook, NotebookToolbar, CellSidebarExtension, CellSidebarButton, } from '@datalayer/jupyter-react';
|
|
14
|
+
import { ThemedJupyterProvider } from './stores/themedProvider';
|
|
14
15
|
import nbformatExample from './stores/notebooks/NotebookExample1.ipynb.json';
|
|
15
16
|
const NOTEBOOK_ID = 'notebook-example-1';
|
|
16
17
|
export const JupyterNotebookExample = (props) => {
|
|
17
18
|
const { serviceManager } = props;
|
|
18
19
|
const extensions = useMemo(() => [new CellSidebarExtension({ factory: CellSidebarButton })], []);
|
|
19
|
-
return (_jsxs(_Fragment, { children: [_jsx(Box, { as: "h1", children: "Jupyter Notebook Example" }), serviceManager && (_jsx(
|
|
20
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { as: "h1", children: "Jupyter Notebook Example" }), serviceManager && (_jsx(ThemedJupyterProvider, { children: _jsx(Notebook, { id: NOTEBOOK_ID, nbformat: nbformatExample, serviceManager: serviceManager, startDefaultKernel: true, extensions: extensions, Toolbar: NotebookToolbar }) }))] }));
|
|
20
21
|
};
|
|
21
22
|
export default JupyterNotebookExample;
|
package/lib/examples/main.js
CHANGED
|
@@ -8,10 +8,15 @@ import { useEffect, useState } from 'react';
|
|
|
8
8
|
import { createRoot } from 'react-dom/client';
|
|
9
9
|
import { loadJupyterConfig, JupyterReactTheme, createServerSettings, setJupyterServerUrl, setJupyterServerToken, getJupyterServerUrl, getJupyterServerToken, } from '@datalayer/jupyter-react';
|
|
10
10
|
import { ServiceManager } from '@jupyterlab/services';
|
|
11
|
-
import {
|
|
11
|
+
import { DatalayerThemeProvider, themeConfigs, themeVariants, } from '@datalayer/primer-addons';
|
|
12
|
+
import { SegmentedControl } from '@primer/react';
|
|
13
|
+
import { Box } from '@datalayer/primer-addons';
|
|
14
|
+
import { MoonIcon, SunIcon, DeviceDesktopIcon } from '@primer/octicons-react';
|
|
15
|
+
import { coreStore, iamStore, createDatalayerServiceManager, } from '@datalayer/core';
|
|
12
16
|
import { useChatStore } from '../components/chat/store';
|
|
13
17
|
import { OAuthCallback } from '../identity';
|
|
14
18
|
import { EXAMPLES } from './example-selector';
|
|
19
|
+
import { useExampleThemeStore } from './stores/themeStore';
|
|
15
20
|
import nbformatExample from './stores/notebooks/NotebookExample1.ipynb.json';
|
|
16
21
|
// Load configurations from DOM
|
|
17
22
|
const loadConfigurations = () => {
|
|
@@ -292,39 +297,84 @@ export const ExampleApp = () => {
|
|
|
292
297
|
if (serviceManager) {
|
|
293
298
|
exampleProps.serviceManager = serviceManager;
|
|
294
299
|
}
|
|
295
|
-
return (_jsx(
|
|
300
|
+
return (_jsx(ExampleAppThemed, { selectedExample: selectedExample, isChangingExample: isChangingExample, error: error, ExampleComponent: ExampleComponent, exampleProps: exampleProps, onExampleChange: handleExampleChange }));
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Inner shell that reads from the theme store and wires
|
|
304
|
+
* DatalayerThemeProvider + the header bar with selectors.
|
|
305
|
+
*/
|
|
306
|
+
const ExampleAppThemed = ({ selectedExample, isChangingExample, error, ExampleComponent, exampleProps, onExampleChange, }) => {
|
|
307
|
+
const { colorMode, theme: themeVariant } = useExampleThemeStore();
|
|
308
|
+
const cfg = themeConfigs[themeVariant];
|
|
309
|
+
return (_jsx(DatalayerThemeProvider, { colorMode: colorMode, theme: cfg.primerTheme, themeStyles: cfg.themeStyles, children: _jsxs(Box, { sx: {
|
|
310
|
+
width: '100vw',
|
|
311
|
+
height: '100vh',
|
|
312
|
+
overflow: 'hidden',
|
|
313
|
+
bg: 'canvas.default',
|
|
314
|
+
color: 'fg.default',
|
|
315
|
+
}, children: [_jsxs(Box, { sx: {
|
|
296
316
|
position: 'fixed',
|
|
297
317
|
top: 0,
|
|
298
318
|
left: 0,
|
|
299
319
|
right: 0,
|
|
300
320
|
zIndex: 100,
|
|
301
|
-
|
|
302
|
-
background: '#f0f0f0',
|
|
303
|
-
borderBottom: '1px solid #ccc',
|
|
321
|
+
px: 3,
|
|
304
322
|
display: 'flex',
|
|
305
323
|
alignItems: 'center',
|
|
306
324
|
justifyContent: 'space-between',
|
|
307
|
-
gap:
|
|
308
|
-
fontSize: '14px',
|
|
309
|
-
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
325
|
+
gap: 3,
|
|
310
326
|
height: '50px',
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
327
|
+
bg: 'canvas.subtle',
|
|
328
|
+
borderBottom: '1px solid',
|
|
329
|
+
borderColor: 'border.default',
|
|
330
|
+
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { as: "select", value: selectedExample, onChange: (e) => onExampleChange(e.target.value), disabled: isChangingExample, sx: {
|
|
331
|
+
px: 2,
|
|
332
|
+
py: '6px',
|
|
333
|
+
fontSize: 1,
|
|
334
|
+
fontFamily: 'mono',
|
|
335
|
+
border: '1px solid',
|
|
336
|
+
borderColor: 'border.default',
|
|
337
|
+
borderRadius: 2,
|
|
338
|
+
bg: 'canvas.default',
|
|
339
|
+
color: 'fg.default',
|
|
318
340
|
cursor: isChangingExample ? 'not-allowed' : 'pointer',
|
|
319
|
-
fontFamily: 'monospace',
|
|
320
341
|
minWidth: '250px',
|
|
342
|
+
outline: 'none',
|
|
343
|
+
'&:focus-visible': {
|
|
344
|
+
boxShadow: '0 0 0 2px var(--bgColor-accent-muted, rgba(9,105,218,0.3))',
|
|
345
|
+
},
|
|
321
346
|
}, children: getExampleNames()
|
|
322
347
|
.sort()
|
|
323
|
-
.map(name => (_jsx("option", { value: name, children: name }, name))) }), isChangingExample && (_jsx("span",
|
|
348
|
+
.map(name => (_jsx("option", { value: name, children: name }, name))) }), isChangingExample && (_jsx(Box, { as: "span", sx: { color: 'fg.muted', fontSize: 0 }, children: "Loading\u2026" })), error && (_jsxs(Box, { as: "span", sx: { color: 'danger.fg', fontSize: 0 }, children: ["Error: ", error] }))] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 3 }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: themeVariants.map(variant => {
|
|
349
|
+
const tcfg = themeConfigs[variant];
|
|
350
|
+
const isSelected = themeVariant === variant;
|
|
351
|
+
return (_jsx(Box, { as: "button", "aria-label": tcfg.label, "aria-pressed": isSelected, onClick: () => useExampleThemeStore.getState().setTheme(variant, false), sx: {
|
|
352
|
+
width: 24,
|
|
353
|
+
height: 24,
|
|
354
|
+
borderRadius: '50%',
|
|
355
|
+
backgroundColor: tcfg.brandColor,
|
|
356
|
+
border: '2px solid',
|
|
357
|
+
borderColor: isSelected ? 'accent.fg' : 'border.default',
|
|
358
|
+
cursor: 'pointer',
|
|
359
|
+
padding: 0,
|
|
360
|
+
outline: 'none',
|
|
361
|
+
transition: 'border-color 0.15s ease',
|
|
362
|
+
boxShadow: isSelected
|
|
363
|
+
? '0 0 0 2px var(--bgColor-accent-muted, rgba(9,105,218,0.3))'
|
|
364
|
+
: 'none',
|
|
365
|
+
'&:hover': { borderColor: 'accent.fg' },
|
|
366
|
+
'&:focus-visible': {
|
|
367
|
+
boxShadow: '0 0 0 2px var(--bgColor-accent-muted, rgba(9,105,218,0.3))',
|
|
368
|
+
},
|
|
369
|
+
} }, variant));
|
|
370
|
+
}) }), _jsxs(SegmentedControl, { "aria-label": "Color mode", size: "small", onChange: (index) => {
|
|
371
|
+
const modes = ['light', 'dark', 'auto'];
|
|
372
|
+
useExampleThemeStore.getState().setColorMode(modes[index]);
|
|
373
|
+
}, children: [_jsx(SegmentedControl.IconButton, { selected: colorMode === 'light', icon: SunIcon, "aria-label": "Light" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'dark', icon: MoonIcon, "aria-label": "Dark" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'auto', icon: DeviceDesktopIcon, "aria-label": "Auto" })] }), _jsx("img", { src: "https://assets.datalayer.tech/datalayer-25.svg", alt: "Datalayer", style: { height: '24px' } })] })] }), _jsx(Box, { sx: {
|
|
324
374
|
marginTop: '50px',
|
|
325
375
|
height: 'calc(100vh - 50px)',
|
|
326
376
|
overflow: 'auto',
|
|
327
|
-
}, children: isChangingExample ? (_jsxs(
|
|
377
|
+
}, children: isChangingExample ? (_jsxs(Box, { sx: { p: 5, textAlign: 'center', color: 'fg.muted' }, children: [_jsxs("h3", { children: ["Loading ", selectedExample, "\u2026"] }), _jsx("p", { children: "Please wait while the example loads." })] })) : ExampleComponent ? (_jsx(ExampleComponent, { ...exampleProps })) : null })] }) }));
|
|
328
378
|
};
|
|
329
379
|
// Mount the app - check route to determine which app to render
|
|
330
380
|
const root = document.getElementById('root');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ThemeVariant, type ColorMode } from '@datalayer/primer-addons';
|
|
2
|
+
export type { ThemeVariant, ColorMode };
|
|
3
|
+
export interface ThemeState {
|
|
4
|
+
/** Current color mode (light, dark, or auto = follow OS). */
|
|
5
|
+
colorMode: ColorMode;
|
|
6
|
+
/** Current theme variant. */
|
|
7
|
+
theme: ThemeVariant;
|
|
8
|
+
/** Cycle through light → dark → auto. */
|
|
9
|
+
toggleColorMode: () => void;
|
|
10
|
+
/** Set a specific color mode. */
|
|
11
|
+
setColorMode: (mode: ColorMode) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Set the active theme variant.
|
|
14
|
+
* @param applyDefaultColorMode When true (default), also switches the
|
|
15
|
+
* color mode to the theme's configured default.
|
|
16
|
+
*/
|
|
17
|
+
setTheme: (theme: ThemeVariant, applyDefaultColorMode?: boolean) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Zustand store for theme preferences in the examples app.
|
|
21
|
+
* Persisted to localStorage under 'agent-runtimes-theme' key.
|
|
22
|
+
*/
|
|
23
|
+
export declare const useExampleThemeStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ThemeState>, "persist"> & {
|
|
24
|
+
persist: {
|
|
25
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ThemeState, unknown>>) => void;
|
|
26
|
+
clearStorage: () => void;
|
|
27
|
+
rehydrate: () => Promise<void> | void;
|
|
28
|
+
hasHydrated: () => boolean;
|
|
29
|
+
onHydrate: (fn: (state: ThemeState) => void) => () => void;
|
|
30
|
+
onFinishHydration: (fn: (state: ThemeState) => void) => () => void;
|
|
31
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<ThemeState, unknown>>;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { create } from 'zustand';
|
|
6
|
+
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
7
|
+
import { themeConfigs, } from '@datalayer/primer-addons';
|
|
8
|
+
/**
|
|
9
|
+
* Zustand store for theme preferences in the examples app.
|
|
10
|
+
* Persisted to localStorage under 'agent-runtimes-theme' key.
|
|
11
|
+
*/
|
|
12
|
+
export const useExampleThemeStore = create()(persist(set => ({
|
|
13
|
+
colorMode: 'light',
|
|
14
|
+
theme: 'datalayer',
|
|
15
|
+
toggleColorMode: () => set(state => {
|
|
16
|
+
const cycle = {
|
|
17
|
+
light: 'dark',
|
|
18
|
+
dark: 'auto',
|
|
19
|
+
auto: 'light',
|
|
20
|
+
};
|
|
21
|
+
return { colorMode: cycle[state.colorMode] };
|
|
22
|
+
}),
|
|
23
|
+
setColorMode: (mode) => set({ colorMode: mode }),
|
|
24
|
+
setTheme: (theme, applyDefaultColorMode = true) => set(() => {
|
|
25
|
+
const next = { theme };
|
|
26
|
+
if (applyDefaultColorMode) {
|
|
27
|
+
next.colorMode = themeConfigs[theme].defaultColorMode;
|
|
28
|
+
}
|
|
29
|
+
return next;
|
|
30
|
+
}),
|
|
31
|
+
}), {
|
|
32
|
+
name: 'agent-runtimes-theme',
|
|
33
|
+
storage: createJSONStorage(() => localStorage),
|
|
34
|
+
partialize: state => ({
|
|
35
|
+
colorMode: state.colorMode,
|
|
36
|
+
theme: state.theme,
|
|
37
|
+
}),
|
|
38
|
+
}));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-aware wrappers for example components.
|
|
3
|
+
*
|
|
4
|
+
* These are drop-in replacements for `DatalayerThemeProvider` and
|
|
5
|
+
* `JupyterReactTheme` that automatically read theme / color-mode
|
|
6
|
+
* from the shared `useExampleThemeStore`.
|
|
7
|
+
*
|
|
8
|
+
* Usage: replace
|
|
9
|
+
* import { DatalayerThemeProvider } from '@datalayer/primer-addons';
|
|
10
|
+
* with
|
|
11
|
+
* import { ThemedProvider } from './stores/themedProvider';
|
|
12
|
+
*
|
|
13
|
+
* and swap `<DatalayerThemeProvider>` → `<ThemedProvider>`.
|
|
14
|
+
*/
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import { type IDatalayerThemeProviderProps } from '@datalayer/primer-addons';
|
|
17
|
+
/**
|
|
18
|
+
* Drop-in replacement for `<DatalayerThemeProvider>`.
|
|
19
|
+
* Reads theme/colorMode from the example theme store and
|
|
20
|
+
* forwards them to the real provider. Any explicit props
|
|
21
|
+
* (colorMode, theme, themeStyles) are still respected as overrides.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ThemedProvider: React.FC<React.PropsWithChildren<Omit<IDatalayerThemeProviderProps, 'ref'>>>;
|
|
24
|
+
/**
|
|
25
|
+
* Drop-in replacement for `<JupyterReactTheme>`.
|
|
26
|
+
* Wraps children in `ThemedProvider` so Jupyter components also
|
|
27
|
+
* pick up the selected theme/color-mode.
|
|
28
|
+
*
|
|
29
|
+
* The wrapper automatically derives `colormode` and `backgroundColor`
|
|
30
|
+
* from the shared theme store so every Jupyter component inherits
|
|
31
|
+
* the correct palette — mirroring the pattern used by
|
|
32
|
+
* `ProjectNotebookEditor`.
|
|
33
|
+
*
|
|
34
|
+
* @param useJupyterReactTheme - When `true`, wraps children in
|
|
35
|
+
* `<JupyterReactTheme>` inside the themed provider. Defaults to `true`.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* Hook that returns the `brandColor` for the currently selected theme.
|
|
39
|
+
* Use this in example components to pass a dynamic brand color to
|
|
40
|
+
* `<ChatFloating>` or any other component that accepts a `brandColor` prop.
|
|
41
|
+
*/
|
|
42
|
+
export declare const useThemeBrandColor: () => string;
|
|
43
|
+
export declare const ThemedJupyterProvider: React.FC<React.PropsWithChildren<{
|
|
44
|
+
useJupyterReactTheme?: boolean;
|
|
45
|
+
}>>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { DatalayerThemeProvider, themeConfigs, } from '@datalayer/primer-addons';
|
|
3
|
+
import { JupyterReactTheme } from '@datalayer/jupyter-react';
|
|
4
|
+
import { useExampleThemeStore } from './themeStore';
|
|
5
|
+
/**
|
|
6
|
+
* Drop-in replacement for `<DatalayerThemeProvider>`.
|
|
7
|
+
* Reads theme/colorMode from the example theme store and
|
|
8
|
+
* forwards them to the real provider. Any explicit props
|
|
9
|
+
* (colorMode, theme, themeStyles) are still respected as overrides.
|
|
10
|
+
*/
|
|
11
|
+
export const ThemedProvider = ({ children, ...rest }) => {
|
|
12
|
+
const { colorMode, theme: themeVariant } = useExampleThemeStore();
|
|
13
|
+
const cfg = themeConfigs[themeVariant];
|
|
14
|
+
return (_jsx(DatalayerThemeProvider, { colorMode: rest.colorMode ?? colorMode, theme: rest.theme ?? cfg.primerTheme, themeStyles: rest.themeStyles ?? cfg.themeStyles, ...rest, children: children }));
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Drop-in replacement for `<JupyterReactTheme>`.
|
|
18
|
+
* Wraps children in `ThemedProvider` so Jupyter components also
|
|
19
|
+
* pick up the selected theme/color-mode.
|
|
20
|
+
*
|
|
21
|
+
* The wrapper automatically derives `colormode` and `backgroundColor`
|
|
22
|
+
* from the shared theme store so every Jupyter component inherits
|
|
23
|
+
* the correct palette — mirroring the pattern used by
|
|
24
|
+
* `ProjectNotebookEditor`.
|
|
25
|
+
*
|
|
26
|
+
* @param useJupyterReactTheme - When `true`, wraps children in
|
|
27
|
+
* `<JupyterReactTheme>` inside the themed provider. Defaults to `true`.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Hook that returns the `brandColor` for the currently selected theme.
|
|
31
|
+
* Use this in example components to pass a dynamic brand color to
|
|
32
|
+
* `<ChatFloating>` or any other component that accepts a `brandColor` prop.
|
|
33
|
+
*/
|
|
34
|
+
export const useThemeBrandColor = () => {
|
|
35
|
+
const { theme: themeVariant } = useExampleThemeStore();
|
|
36
|
+
return themeConfigs[themeVariant].brandColor;
|
|
37
|
+
};
|
|
38
|
+
export const ThemedJupyterProvider = ({ children, useJupyterReactTheme = true }) => {
|
|
39
|
+
const { colorMode, theme: themeVariant } = useExampleThemeStore();
|
|
40
|
+
const cfg = themeConfigs[themeVariant];
|
|
41
|
+
// Resolve 'auto' to an actual mode so we can pick the right style set.
|
|
42
|
+
const resolvedMode = colorMode === 'auto'
|
|
43
|
+
? typeof window !== 'undefined' &&
|
|
44
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
45
|
+
? 'dark'
|
|
46
|
+
: 'light'
|
|
47
|
+
: colorMode === 'dark'
|
|
48
|
+
? 'dark'
|
|
49
|
+
: 'light';
|
|
50
|
+
// Extract the canvas background from the theme's CSS-var overrides.
|
|
51
|
+
const modeStyles = resolvedMode === 'dark' ? cfg.themeStyles.dark : cfg.themeStyles.light;
|
|
52
|
+
const backgroundColor = modeStyles['--bgColor-default'];
|
|
53
|
+
return (_jsx(ThemedProvider, { children: useJupyterReactTheme ? (_jsx(JupyterReactTheme, { colormode: colorMode, backgroundColor: backgroundColor, children: children })) : (children) }));
|
|
54
|
+
};
|
|
@@ -35,7 +35,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
35
35
|
*
|
|
36
36
|
* @module lexical/ChatInlinePlugin
|
|
37
37
|
*/
|
|
38
|
-
import { useCallback, useEffect, useLayoutEffect, useRef, useState, } from 'react';
|
|
38
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
39
39
|
import { createPortal } from 'react-dom';
|
|
40
40
|
import { $getSelection, $isRangeSelection, $createParagraphNode, $createTextNode, TextNode, COMMAND_PRIORITY_LOW, createCommand, } from 'lexical';
|
|
41
41
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
@@ -179,12 +179,33 @@ export function ChatInlinePlugin({ protocol, isOpen, onClose, pendingPrompt, onP
|
|
|
179
179
|
// Selection tracking
|
|
180
180
|
const { range } = useRange();
|
|
181
181
|
const selectedText = useSelectionText();
|
|
182
|
-
//
|
|
182
|
+
// ---------------------------------------------------------------
|
|
183
|
+
// Latch: save position & text when AI panel opens so that a
|
|
184
|
+
// transient selection loss (e.g. portal render triggering
|
|
185
|
+
// selectionchange) does not unmount the panel.
|
|
186
|
+
// ---------------------------------------------------------------
|
|
187
|
+
const savedRectRef = useRef(null);
|
|
188
|
+
const savedTextRef = useRef('');
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (isOpen && range) {
|
|
191
|
+
// Keep saving the latest rect/text while open and range is valid
|
|
192
|
+
savedRectRef.current = range.getBoundingClientRect();
|
|
193
|
+
savedTextRef.current = selectedText || '';
|
|
194
|
+
}
|
|
195
|
+
if (!isOpen) {
|
|
196
|
+
savedRectRef.current = null;
|
|
197
|
+
savedTextRef.current = '';
|
|
198
|
+
}
|
|
199
|
+
}, [isOpen, range, selectedText]);
|
|
200
|
+
// Effective values: prefer live selection, fall back to saved snapshot
|
|
201
|
+
const effectiveRect = useMemo(() => range?.getBoundingClientRect() ?? savedRectRef.current ?? null, [range]);
|
|
202
|
+
const effectiveText = range ? selectedText : savedTextRef.current;
|
|
203
|
+
// Update floating reference position based on selection (or saved rect)
|
|
183
204
|
useLayoutEffect(() => {
|
|
184
205
|
setReference({
|
|
185
|
-
getBoundingClientRect: () =>
|
|
206
|
+
getBoundingClientRect: () => effectiveRect || new DOMRect(),
|
|
186
207
|
});
|
|
187
|
-
}, [setReference,
|
|
208
|
+
}, [setReference, effectiveRect]);
|
|
188
209
|
// Handle replace selection
|
|
189
210
|
const handleReplaceSelection = useCallback((text) => {
|
|
190
211
|
editor.update(() => {
|
|
@@ -219,8 +240,8 @@ export function ChatInlinePlugin({ protocol, isOpen, onClose, pendingPrompt, onP
|
|
|
219
240
|
}
|
|
220
241
|
});
|
|
221
242
|
}, [editor]);
|
|
222
|
-
// Don't render if not open or
|
|
223
|
-
if (!isOpen || range === null) {
|
|
243
|
+
// Don't render if not open, or if we have neither a live range nor a saved rect
|
|
244
|
+
if (!isOpen || (range === null && savedRectRef.current === null)) {
|
|
224
245
|
return null;
|
|
225
246
|
}
|
|
226
247
|
const portalTarget = portalContainer || document.body;
|
|
@@ -236,6 +257,6 @@ export function ChatInlinePlugin({ protocol, isOpen, onClose, pendingPrompt, onP
|
|
|
236
257
|
width: editor._rootElement
|
|
237
258
|
? editor._rootElement.getBoundingClientRect().width - MARGIN_X * 2
|
|
238
259
|
: 'auto',
|
|
239
|
-
}, children: _jsx(ChatInline, { selectedText:
|
|
260
|
+
}, children: _jsx(ChatInline, { selectedText: effectiveText, protocol: protocol, onReplaceSelection: handleReplaceSelection, onInsertInline: handleInsertInline, onInsertBelow: handleInsertBelow, onClose: onClose, onSaveSelection: saveSelection, onRestoreSelection: restoreSelection, pendingPrompt: pendingPrompt, onPendingPromptConsumed: onPendingPromptConsumed }) }), portalTarget);
|
|
240
261
|
}
|
|
241
262
|
export default ChatInlinePlugin;
|
package/lib/lexical/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* @module lexical
|
|
5
5
|
*/
|
|
6
6
|
export { ChatInlinePlugin, type ChatInlinePluginProps, SAVE_SELECTION_COMMAND, RESTORE_SELECTION_COMMAND, } from './ChatInlinePlugin';
|
|
7
|
-
export { useChatInlineToolbarItems, type ChatInlineToolbarState, } from './useChatInlineToolbarItems';
|
|
7
|
+
export { useChatInlineToolbarItems, type ChatInlineToolbarOptions, type ChatInlineToolbarState, } from './useChatInlineToolbarItems';
|
|
@@ -18,11 +18,18 @@ export interface ChatInlineToolbarState {
|
|
|
18
18
|
/** Close the AI panel */
|
|
19
19
|
closeAi: () => void;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Options for useChatInlineToolbarItems.
|
|
23
|
+
*/
|
|
24
|
+
export interface ChatInlineToolbarOptions {
|
|
25
|
+
/** When true the AI sparkle button is rendered in a disabled state. */
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
}
|
|
21
28
|
/**
|
|
22
29
|
* Hook that creates ToolbarItem[] for AI actions in the floating toolbar.
|
|
23
30
|
*
|
|
24
|
-
* Returns toolbar items (divider + AI
|
|
31
|
+
* Returns toolbar items (divider + AI sparkle button) and
|
|
25
32
|
* state for controlling the ChatInline panel.
|
|
26
33
|
*/
|
|
27
|
-
export declare function useChatInlineToolbarItems(): ChatInlineToolbarState;
|
|
34
|
+
export declare function useChatInlineToolbarItems(options?: ChatInlineToolbarOptions): ChatInlineToolbarState;
|
|
28
35
|
export default useChatInlineToolbarItems;
|