@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.
Files changed (41) hide show
  1. package/lib/Agent.js +1 -2
  2. package/lib/AgentLexical.d.ts +0 -1
  3. package/lib/AgentLexical.js +1 -3
  4. package/lib/AgentNotebook.js +1 -2
  5. package/lib/components/chat/components/ChatFloating.d.ts +4 -2
  6. package/lib/components/chat/components/ChatFloating.js +1 -1
  7. package/lib/examples/A2UiRestaurantExample.js +2 -2
  8. package/lib/examples/AgUiAgenticExample.js +3 -3
  9. package/lib/examples/AgUiBackendToolRenderingExample.js +4 -3
  10. package/lib/examples/AgUiHaikuGenUIExample.js +4 -3
  11. package/lib/examples/AgUiHumanInTheLoopExample.js +4 -3
  12. package/lib/examples/AgUiSharedStateExample.js +4 -3
  13. package/lib/examples/AgUiToolsBasedGenUIExample.js +4 -3
  14. package/lib/examples/AgentRuntimeChatExample.js +3 -3
  15. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  16. package/lib/examples/AgentRuntimeFormExample.js +2 -2
  17. package/lib/examples/AgentRuntimeLexical2Example.d.ts +0 -1
  18. package/lib/examples/AgentRuntimeLexical2Example.js +3 -3
  19. package/lib/examples/AgentRuntimeLexicalExample.d.ts +0 -1
  20. package/lib/examples/AgentRuntimeLexicalExample.js +4 -4
  21. package/lib/examples/AgentRuntimeLexicalSidebarExample.d.ts +0 -1
  22. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +3 -3
  23. package/lib/examples/AgentRuntimeNotebookExample.js +4 -3
  24. package/lib/examples/AgentRuntimeNotebookSidebarExample.js +3 -2
  25. package/lib/examples/AgentRuntimeStandaloneExample.js +3 -3
  26. package/lib/examples/CopilotKitLexicalExample.d.ts +0 -1
  27. package/lib/examples/CopilotKitLexicalExample.js +3 -3
  28. package/lib/examples/CopilotKitNotebookExample.js +3 -2
  29. package/lib/examples/DatalayerNotebookExample.js +3 -2
  30. package/lib/examples/JupyterCellExample.js +3 -2
  31. package/lib/examples/JupyterNotebookExample.js +3 -2
  32. package/lib/examples/main.js +68 -18
  33. package/lib/examples/stores/themeStore.d.ts +33 -0
  34. package/lib/examples/stores/themeStore.js +38 -0
  35. package/lib/examples/stores/themedProvider.d.ts +45 -0
  36. package/lib/examples/stores/themedProvider.js +54 -0
  37. package/lib/lexical/ChatInlinePlugin.js +28 -7
  38. package/lib/lexical/index.d.ts +1 -1
  39. package/lib/lexical/useChatInlineToolbarItems.d.ts +9 -2
  40. package/lib/lexical/useChatInlineToolbarItems.js +22 -94
  41. 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 { JupyterReactTheme, useJupyter } from '@datalayer/jupyter-react';
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(JupyterReactTheme, { children: _jsx(SimpleKernelPluginsInner, {}) }), floatingAnchorElem && (_jsxs(_Fragment, { children: [_jsx(DraggableBlockPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem, setIsLinkEditMode: setIsLinkEditMode }), _jsx(CodeActionMenuPlugin, { anchorElem: floatingAnchorElem })] }))] }) }) }) })] }) }));
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 { JupyterReactTheme, Notebook } from '@datalayer/jupyter-react';
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(JupyterReactTheme, { 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
+ }, 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, JupyterReactTheme, Notebook, } from '@datalayer/jupyter-react';
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(JupyterReactTheme, { 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
+ 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 { JupyterReactTheme, Cell, KernelIndicator, useJupyter, useKernelsStore, useCellsStore, } from '@datalayer/jupyter-react';
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(JupyterReactTheme, { children: _jsx(JupyterCellExampleContent, {}) }));
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 { JupyterReactTheme, Notebook, NotebookToolbar, CellSidebarExtension, CellSidebarButton, } from '@datalayer/jupyter-react';
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(JupyterReactTheme, { children: _jsx(Notebook, { id: NOTEBOOK_ID, nbformat: nbformatExample, serviceManager: serviceManager, startDefaultKernel: true, extensions: extensions, Toolbar: NotebookToolbar }) }))] }));
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;
@@ -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 { coreStore, iamStore, createDatalayerServiceManager, DatalayerThemeProvider, } from '@datalayer/core';
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(DatalayerThemeProvider, { children: _jsxs("div", { style: { width: '100vw', height: '100vh', overflow: 'hidden' }, children: [_jsxs("div", { style: {
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
- padding: '10px 20px',
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: '15px',
308
- fontSize: '14px',
309
- fontFamily: 'system-ui, -apple-system, sans-serif',
325
+ gap: 3,
310
326
  height: '50px',
311
- boxSizing: 'border-box',
312
- }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '15px' }, children: [_jsx("label", { style: { fontWeight: 500, color: '#333' }, children: "Select Example:" }), _jsx("select", { value: selectedExample, onChange: e => handleExampleChange(e.target.value), disabled: isChangingExample, style: {
313
- padding: '6px 12px',
314
- fontSize: '14px',
315
- border: '1px solid #ccc',
316
- borderRadius: '4px',
317
- background: 'white',
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", { style: { color: '#666', fontSize: '12px' }, children: "Loading..." })), error && (_jsxs("span", { style: { color: '#dc3545', fontSize: '12px' }, children: ["Error: ", error] }))] }), _jsx("img", { src: "https://assets.datalayer.tech/datalayer-25.svg", alt: "Datalayer", style: { height: '24px' } })] }), _jsx("div", { style: {
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("div", { style: { padding: '40px', textAlign: 'center', color: '#666' }, children: [_jsxs("h3", { children: ["Loading ", selectedExample, "..."] }), _jsx("p", { children: "Please wait while the example loads." })] })) : ExampleComponent ? (_jsx(ExampleComponent, { ...exampleProps })) : null })] }) }));
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
- // Update floating reference position based on selection
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: () => range?.getBoundingClientRect() || new DOMRect(),
206
+ getBoundingClientRect: () => effectiveRect || new DOMRect(),
186
207
  });
187
- }, [setReference, range]);
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 no selection
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: selectedText, protocol: protocol, onReplaceSelection: handleReplaceSelection, onInsertInline: handleInsertInline, onInsertBelow: handleInsertBelow, onClose: onClose, onSaveSelection: saveSelection, onRestoreSelection: restoreSelection, pendingPrompt: pendingPrompt, onPendingPromptConsumed: onPendingPromptConsumed }) }), portalTarget);
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;
@@ -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 dropdown + sparkle button) and
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;