@datalayer/agent-runtimes 0.0.12 → 1.0.0

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.
@@ -28,7 +28,6 @@ import 'prismjs/components/prism-swift';
28
28
  import React from 'react';
29
29
  import '@datalayer/jupyter-lexical/style/index.css';
30
30
  import './examples/lexical/lexical-theme.css';
31
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
32
31
  import '../style/primer-primitives.css';
33
32
  export declare const AgentLexical: React.FC;
34
33
  export default AgentLexical;
@@ -61,7 +61,6 @@ import { editorConfig } from './examples/lexical/editorConfig';
61
61
  import { DEFAULT_MODEL } from './specs';
62
62
  import '@datalayer/jupyter-lexical/style/index.css';
63
63
  import './examples/lexical/lexical-theme.css';
64
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
65
64
  import '../style/primer-primitives.css';
66
65
  setupPrimerPortals();
67
66
  const BASE_URL = window.location.origin;
@@ -27,7 +27,6 @@ import 'prismjs/components/prism-rust';
27
27
  import 'prismjs/components/prism-swift';
28
28
  import type { ServiceManager } from '@jupyterlab/services';
29
29
  import '@datalayer/jupyter-lexical/style/index.css';
30
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
31
30
  import './lexical/lexical-theme.css';
32
31
  /**
33
32
  * Chat Lexical Example with Simple integration
@@ -54,7 +54,6 @@ import { ChatFloating, useChatStore, useFrontendTool, DatalayerInferenceProvider
54
54
  import { useLexicalToolActions, ActionRegistrar, } from '../tools/adapters/copilotkit/lexicalHooks';
55
55
  import { editorConfig } from './lexical/editorConfig';
56
56
  import '@datalayer/jupyter-lexical/style/index.css';
57
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
58
57
  import './lexical/lexical-theme.css';
59
58
  // Fixed lexical document ID
60
59
  const LEXICAL_ID = 'chat-popup-lexical-example';
@@ -25,7 +25,6 @@ import 'prismjs/components/prism-swift';
25
25
  import type { ServiceManager } from '@jupyterlab/services';
26
26
  import '@datalayer/jupyter-lexical/style/index.css';
27
27
  import './lexical/lexical-theme.css';
28
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
29
28
  /**
30
29
  * Main Agent Runtime lexical example component
31
30
  */
@@ -54,7 +54,6 @@ import { useLexicalTools } from '../tools/adapters/agent-runtimes/lexicalHooks';
54
54
  import { editorConfig } from './lexical/editorConfig';
55
55
  import '@datalayer/jupyter-lexical/style/index.css';
56
56
  import './lexical/lexical-theme.css';
57
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
58
57
  // Fixed lexical document ID
59
58
  const LEXICAL_ID = 'agui-lexical-example';
60
59
  // Base URL for agent-runtimes server
@@ -26,7 +26,6 @@ import 'prismjs/components/prism-rust';
26
26
  import 'prismjs/components/prism-swift';
27
27
  import type { ServiceManager } from '@jupyterlab/services';
28
28
  import '@datalayer/jupyter-lexical/style/index.css';
29
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
30
29
  import './lexical/lexical-theme.css';
31
30
  /**
32
31
  * Agent Runtime Lexical Sidebar Example with Simple integration
@@ -54,7 +54,6 @@ import { useChatInlineToolbarItems } from '../lexical/useChatInlineToolbarItems'
54
54
  import { useLexicalTools } from '../tools/adapters/agent-runtimes/lexicalHooks';
55
55
  import { editorConfig } from './lexical/editorConfig';
56
56
  import '@datalayer/jupyter-lexical/style/index.css';
57
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
58
57
  import './lexical/lexical-theme.css';
59
58
  // Fixed lexical document ID
60
59
  const LEXICAL_ID = 'chat-lexical-example';
@@ -26,7 +26,6 @@ import 'prismjs/components/prism-swift';
26
26
  import type { ServiceManager } from '@jupyterlab/services';
27
27
  import '@datalayer/jupyter-lexical/style/index.css';
28
28
  import '@copilotkit/react-ui/styles.css';
29
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
30
29
  import './lexical/lexical-theme.css';
31
30
  /**
32
31
  * Main CopilotKit lexical example component
@@ -53,7 +53,6 @@ import { ActionRegistrar, useLexicalToolActions, } from '../tools/adapters/copil
53
53
  import { editorConfig } from './lexical/editorConfig';
54
54
  import '@datalayer/jupyter-lexical/style/index.css';
55
55
  import '@copilotkit/react-ui/styles.css';
56
- import '@datalayer/jupyter-lexical/style/modal-overrides.css';
57
56
  import './lexical/lexical-theme.css';
58
57
  // Fixed lexical document ID
59
58
  const LEXICAL_ID = 'agui-lexical-example';
@@ -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;
@@ -6,13 +6,14 @@
6
6
  * useChatInlineToolbarItems - Hook that creates ToolbarItem[] for the
7
7
  * FloatingTextFormatToolbarPlugin's extraItems prop.
8
8
  *
9
- * Registers an AI sparkle button + dropdown with AI actions
10
- * (Improve, Fix, Simplify, Add detail, Summarise, Explain, Translate)
11
- * into the floating inline toolbar.
9
+ * Registers an AI sparkle button in the floating inline toolbar.
10
+ * Clicking the sparkle button directly opens the ChatInlinePlugin
11
+ * floating panel, where users can type free-form AI prompts.
12
12
  *
13
13
  * Usage:
14
14
  * ```tsx
15
- * const { toolbarItems, isAiOpen, submitPrompt, closeAi } = useChatInlineToolbarItems();
15
+ * const { toolbarItems, isAiOpen, pendingPrompt, clearPendingPrompt, closeAi } =
16
+ * useChatInlineToolbarItems();
16
17
  *
17
18
  * <FloatingTextFormatToolbarPlugin
18
19
  * anchorElem={floatingAnchorElem}
@@ -20,82 +21,25 @@
20
21
  * extraItems={toolbarItems}
21
22
  * />
22
23
  *
23
- * {isAiOpen && <ChatInlinePlugin ... />}
24
+ * <ChatInlinePlugin
25
+ * isOpen={isAiOpen}
26
+ * onClose={closeAi}
27
+ * pendingPrompt={pendingPrompt}
28
+ * onPendingPromptConsumed={clearPendingPrompt}
29
+ * />
24
30
  * ```
25
31
  *
26
32
  * @module lexical/useChatInlineToolbarItems
27
33
  */
28
34
  import { useState, useMemo, useCallback } from 'react';
29
- import { SparkleFillIcon, PencilIcon, CheckIcon, XIcon, PlusIcon, CopyIcon, SyncIcon, InfoIcon, } from '@primer/octicons-react';
30
- /**
31
- * AI action groups for the toolbar dropdown.
32
- */
33
- const AI_ACTIONS = {
34
- modify: [
35
- {
36
- key: 'ai-improve',
37
- label: 'Improve writing',
38
- prompt: 'Improve the quality of the text',
39
- icon: PencilIcon,
40
- },
41
- {
42
- key: 'ai-fix',
43
- label: 'Fix mistakes',
44
- prompt: 'Fix any typos or general errors in the text',
45
- icon: CheckIcon,
46
- },
47
- {
48
- key: 'ai-simplify',
49
- label: 'Simplify',
50
- prompt: 'Shorten the text, simplifying it',
51
- icon: XIcon,
52
- },
53
- {
54
- key: 'ai-detail',
55
- label: 'Add more detail',
56
- prompt: 'Lengthen the text, going into more detail',
57
- icon: PlusIcon,
58
- },
59
- ],
60
- generate: [
61
- {
62
- key: 'ai-summarise',
63
- label: 'Summarise',
64
- prompt: 'Summarise the text',
65
- icon: CopyIcon,
66
- },
67
- {
68
- key: 'ai-explain',
69
- label: 'Explain',
70
- prompt: 'Explain what the text is about',
71
- icon: InfoIcon,
72
- },
73
- ],
74
- translate: [
75
- 'Arabic',
76
- 'Chinese',
77
- 'Dutch',
78
- 'English',
79
- 'French',
80
- 'German',
81
- 'Japanese',
82
- 'Korean',
83
- 'Portuguese',
84
- 'Spanish',
85
- ].map(lang => ({
86
- key: `ai-translate-${lang.toLowerCase()}`,
87
- label: lang,
88
- prompt: `Translate text into the ${lang} language`,
89
- icon: SyncIcon,
90
- })),
91
- };
35
+ import { SparkleFillIcon } from '@primer/octicons-react';
92
36
  /**
93
37
  * Hook that creates ToolbarItem[] for AI actions in the floating toolbar.
94
38
  *
95
- * Returns toolbar items (divider + AI dropdown + sparkle button) and
39
+ * Returns toolbar items (divider + AI sparkle button) and
96
40
  * state for controlling the ChatInline panel.
97
41
  */
98
- export function useChatInlineToolbarItems() {
42
+ export function useChatInlineToolbarItems(options) {
99
43
  const [isAiOpen, setIsAiOpen] = useState(false);
100
44
  const [pendingPrompt, setPendingPrompt] = useState(null);
101
45
  const openAi = useCallback(() => {
@@ -112,27 +56,8 @@ export function useChatInlineToolbarItems() {
112
56
  const clearPendingPrompt = useCallback(() => {
113
57
  setPendingPrompt(null);
114
58
  }, []);
59
+ const isDisabled = options?.disabled ?? false;
115
60
  const toolbarItems = useMemo(() => {
116
- const allOptions = [
117
- ...AI_ACTIONS.modify.map(action => ({
118
- key: action.key,
119
- label: action.label,
120
- icon: action.icon,
121
- onClick: () => submitPrompt(action.prompt),
122
- })),
123
- ...AI_ACTIONS.generate.map(action => ({
124
- key: action.key,
125
- label: action.label,
126
- icon: action.icon,
127
- onClick: () => submitPrompt(action.prompt),
128
- })),
129
- ...AI_ACTIONS.translate.map(action => ({
130
- key: action.key,
131
- label: action.label,
132
- icon: action.icon,
133
- onClick: () => submitPrompt(action.prompt),
134
- })),
135
- ];
136
61
  return [
137
62
  {
138
63
  key: 'ai-divider',
@@ -141,15 +66,18 @@ export function useChatInlineToolbarItems() {
141
66
  },
142
67
  {
143
68
  key: 'ai-actions',
144
- type: 'dropdown',
69
+ type: 'button',
145
70
  order: 901,
146
71
  ariaLabel: 'AI Actions',
147
- title: 'AI Actions',
72
+ title: isDisabled
73
+ ? 'Assign an agent to enable AI actions'
74
+ : 'AI Actions',
148
75
  icon: SparkleFillIcon,
149
- options: allOptions,
76
+ onClick: openAi,
77
+ disabled: isDisabled,
150
78
  },
151
79
  ];
152
- }, [submitPrompt]);
80
+ }, [openAi, isDisabled]);
153
81
  return {
154
82
  toolbarItems,
155
83
  isAiOpen,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "0.0.12",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",