jupyter-chat-components 0.4.1 → 0.6.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # jupyter_chat_components
2
2
 
3
- [![Github Actions Status](https://github.com/brichet/jupyter-chat-components/workflows/Build/badge.svg)](https://github.com/brichet/jupyter-chat-components/actions/workflows/build.yml)
3
+ [![Github Actions Status](https://github.com/jupyter-ai-contrib/jupyter-chat-components/workflows/Build/badge.svg)](https://github.com/jupyter-ai-contrib/jupyter-chat-components/actions/workflows/build.yml)
4
4
 
5
5
  A library of React components designed for use in Jupyter chat interfaces, with a focus on AI-powered interactions. These components are intended to be integrated into JupyterLab extensions that provide chat functionality.
6
6
 
@@ -26,7 +26,7 @@ The registry is available directly on the `IComponentsRendererFactory` token as
26
26
 
27
27
  Other JupyterLab extensions can consume the `IComponentsRendererFactory` token and use `registry.add()` to register their own components, which will then be available for rendering via the MIME bundle.
28
28
 
29
- For live end-to-end metadata examples, see the deployed demo notebook at [brichet.github.io/jupyter-chat-components/lab/index.html?path=components_demo.ipynb](https://brichet.github.io/jupyter-chat-components/lab/index.html?path=components_demo.ipynb). The source notebook lives at [demo/contents/components_demo.ipynb](./demo/contents/components_demo.ipynb).
29
+ For live end-to-end metadata examples, see the deployed demo notebook at [jupyter-ai-contrib.github.io/jupyter-chat-components/lab/index.html?path=components_demo.ipynb](https://jupyter-ai-contrib.github.io/jupyter-chat-components/lab/index.html?path=components_demo.ipynb). The source notebook lives at [demo/contents/components_demo.ipynb](./demo/contents/components_demo.ipynb).
30
30
 
31
31
  ## Requirements
32
32
 
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import { IComponentProps } from '../token';
3
+ export interface IErrorProps extends IComponentProps {
4
+ errorMessage: string;
5
+ title?: string;
6
+ }
7
+ export declare const ErrorMessage: React.FC<IErrorProps>;
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { errorIcon } from '../icons';
3
+ export const ErrorMessage = ({ errorMessage, title = 'Error' }) => {
4
+ return (React.createElement("div", { className: "jp-ai-error-message" },
5
+ React.createElement("div", { className: "jp-ai-error-icon" },
6
+ React.createElement(errorIcon.react, { tag: "span", width: "18px", height: "18px" })),
7
+ React.createElement("div", { className: "jp-ai-error-body" },
8
+ React.createElement("div", { className: "jp-ai-error-title" }, title),
9
+ React.createElement("div", { className: "jp-ai-error-text" }, errorMessage))));
10
+ };
@@ -1,11 +1,9 @@
1
1
  import * as React from 'react';
2
- import { IComponentProps, IToolCallsEntry, IToolCallsMetadata, OpenToolCallPath, ToolCallPermissionDecision } from '../token';
2
+ import { IComponentProps, IGroupedToolCallCallbacks, IToolCallsEntry, IToolCallsMetadata } from '../token';
3
3
  /**
4
4
  * Props for rendering grouped tool calls.
5
5
  */
6
- export interface IGroupedToolCallsProps extends IComponentProps, IToolCallsMetadata {
7
- toolCallPermissionDecision?: ToolCallPermissionDecision;
8
- openToolCallPath?: OpenToolCallPath;
6
+ export interface IGroupedToolCallsProps extends IComponentProps, IToolCallsMetadata, IGroupedToolCallCallbacks {
9
7
  }
10
8
  /**
11
9
  * Convert an absolute filesystem path to a server-relative path when possible.
@@ -1,3 +1,4 @@
1
+ export * from './error';
1
2
  export * from './inline-diff';
2
3
  export * from './message-queue';
3
4
  export * from './tool-call';
@@ -1,3 +1,4 @@
1
+ export * from './error';
1
2
  export * from './inline-diff';
2
3
  export * from './message-queue';
3
4
  export * from './tool-call';
@@ -1,13 +1,5 @@
1
1
  import * as React from 'react';
2
- import { IComponentProps, IMessageQueueMetadata, RemoveQueuedMessage } from '../token';
3
- /**
4
- * Props for the MessageQueue component.
5
- */
6
- export interface IMessageQueueProps extends IComponentProps, IMessageQueueMetadata {
7
- removeQueuedMessage?: RemoveQueuedMessage;
2
+ import { IComponentProps, IMessageQueueMetadata, IQueueMessageCallbacks } from '../token';
3
+ export interface IMessageQueueProps extends IComponentProps, IMessageQueueMetadata, IQueueMessageCallbacks {
8
4
  }
9
- /**
10
- * React component that displays a list of queued messages by
11
- * showing each pending message as a bubble in the chat
12
- */
13
5
  export declare const MessageQueue: React.FC<IMessageQueueProps>;
@@ -1,13 +1,128 @@
1
1
  import * as React from 'react';
2
- /**
3
- * React component that displays a list of queued messages by
4
- * showing each pending message as a bubble in the chat
5
- */
6
- export const MessageQueue = ({ messages, targetId, trans, removeQueuedMessage }) => {
2
+ import { PathExt } from '@jupyterlab/coreutils';
3
+ import { checkIcon, closeIcon, editIcon, fileIcon, notebookIcon } from '@jupyterlab/ui-components';
4
+ function AttachmentIcon({ type }) {
5
+ const Icon = type === 'notebook' ? notebookIcon.react : fileIcon.react;
6
+ return React.createElement(Icon, { tag: "span", className: "jp-chat-message-queue-attachment-icon" });
7
+ }
8
+ function attachmentName(attachment) {
9
+ return PathExt.basename(attachment.value) || attachment.value;
10
+ }
11
+ export const MessageQueue = ({ messages, targetId, trans, removeQueuedMessage, reorderQueuedMessages, editQueuedMessage }) => {
12
+ const [expanded, setExpanded] = React.useState(true);
13
+ const [draggedIndex, setDraggedIndex] = React.useState(null);
14
+ const [dropLineIndex, setDropLineIndex] = React.useState(null);
15
+ const [editingId, setEditingId] = React.useState(null);
16
+ const [editBody, setEditBody] = React.useState('');
17
+ const textareaRef = React.useRef(null);
18
+ React.useEffect(() => {
19
+ if (editingId && textareaRef.current) {
20
+ const ta = textareaRef.current;
21
+ ta.focus();
22
+ ta.selectionStart = ta.selectionEnd = ta.value.length;
23
+ ta.style.height = 'auto';
24
+ ta.style.height = ta.scrollHeight + 'px';
25
+ }
26
+ }, [editingId]);
7
27
  if (!messages || messages.length === 0) {
8
28
  return null;
9
29
  }
10
- return (React.createElement("div", { className: "jp-chat-message-queue" }, messages.map(msg => (React.createElement("div", { key: msg.id, className: "jp-chat-message-queue-bubble" },
11
- React.createElement("span", { className: "jp-chat-message-queue-text" }, msg.body),
12
- removeQueuedMessage && targetId && (React.createElement("button", { className: "jp-chat-message-queue-remove", onClick: () => removeQueuedMessage(targetId, msg.id), title: trans.__('Remove from queue'), type: "button" }, "\u2715")))))));
30
+ const canDrag = !!reorderQueuedMessages && !!targetId;
31
+ const handleDragStart = (index) => {
32
+ setDraggedIndex(index);
33
+ };
34
+ // Full-width row dragover: use Y midpoint to decide before or after this item.
35
+ const handleRowDragOver = (e, index) => {
36
+ e.preventDefault();
37
+ const rect = e.currentTarget.getBoundingClientRect();
38
+ setDropLineIndex(e.clientY < rect.top + rect.height / 2 ? index : index + 1);
39
+ };
40
+ const handleDrop = (e) => {
41
+ e.preventDefault();
42
+ if (draggedIndex === null ||
43
+ dropLineIndex === null ||
44
+ !reorderQueuedMessages ||
45
+ !targetId) {
46
+ setDraggedIndex(null);
47
+ setDropLineIndex(null);
48
+ return;
49
+ }
50
+ const reordered = [...messages];
51
+ const [moved] = reordered.splice(draggedIndex, 1);
52
+ const insertAt = draggedIndex < dropLineIndex ? dropLineIndex - 1 : dropLineIndex;
53
+ reordered.splice(insertAt, 0, moved);
54
+ reorderQueuedMessages(targetId, reordered.map(m => m.id));
55
+ setDraggedIndex(null);
56
+ setDropLineIndex(null);
57
+ };
58
+ const handleDragEnd = () => {
59
+ setDraggedIndex(null);
60
+ setDropLineIndex(null);
61
+ };
62
+ const handleEditStart = (id, body) => {
63
+ setEditingId(id);
64
+ setEditBody(body);
65
+ };
66
+ const handleEditSave = () => {
67
+ if (!editQueuedMessage || !targetId || editingId === null) {
68
+ return;
69
+ }
70
+ editQueuedMessage(targetId, editingId, editBody.trim());
71
+ setEditingId(null);
72
+ };
73
+ const handleEditCancel = () => {
74
+ setEditingId(null);
75
+ };
76
+ const handleEditKeyDown = (e) => {
77
+ if (e.key === 'Enter' && !e.shiftKey) {
78
+ e.preventDefault();
79
+ handleEditSave();
80
+ }
81
+ else if (e.key === 'Escape') {
82
+ handleEditCancel();
83
+ }
84
+ };
85
+ const handleTextareaChange = (e) => {
86
+ setEditBody(e.target.value);
87
+ e.target.style.height = 'auto';
88
+ e.target.style.height = e.target.scrollHeight + 'px';
89
+ };
90
+ return (React.createElement("div", { className: "jp-chat-message-queue" },
91
+ React.createElement("div", { className: "jp-chat-message-queue-header" },
92
+ React.createElement("span", { className: "jp-chat-message-queue-count" }, trans.__('%1 queued', messages.length)),
93
+ React.createElement("button", { className: "jp-chat-message-queue-toggle", onClick: () => setExpanded(e => !e), type: "button", title: expanded ? trans.__('Collapse queue') : trans.__('Expand queue'), "aria-expanded": expanded }, expanded ? '▲' : '▼')),
94
+ expanded && (React.createElement("div", { className: "jp-chat-message-queue-list", onDrop: canDrag ? handleDrop : undefined },
95
+ messages.map((msg, index) => (React.createElement(React.Fragment, { key: msg.id },
96
+ dropLineIndex === index && (React.createElement("div", { className: "jp-chat-message-queue-drop-line" })),
97
+ React.createElement("div", { className: "jp-chat-message-queue-row", onDragOver: canDrag ? e => handleRowDragOver(e, index) : undefined },
98
+ React.createElement("div", { className: [
99
+ 'jp-chat-message-queue-bubble',
100
+ draggedIndex === index
101
+ ? 'jp-chat-message-queue-bubble-dragging'
102
+ : ''
103
+ ]
104
+ .filter(Boolean)
105
+ .join(' '), draggable: canDrag && editingId !== msg.id, onDragStart: canDrag && editingId !== msg.id
106
+ ? () => handleDragStart(index)
107
+ : undefined, onDragEnd: canDrag ? handleDragEnd : undefined, title: editingId === msg.id ? undefined : msg.body },
108
+ canDrag && (React.createElement("span", { className: "jp-chat-message-queue-drag-handle", "aria-hidden": "true" })),
109
+ React.createElement("div", { className: "jp-chat-message-queue-content" },
110
+ editingId === msg.id ? (React.createElement("textarea", { ref: textareaRef, className: "jp-chat-message-queue-edit-textarea", value: editBody, onChange: handleTextareaChange, onKeyDown: handleEditKeyDown, rows: 1, title: trans.__('Enter to save, Escape to cancel, Shift+Enter for newline') })) : (msg.body && (React.createElement("span", { className: "jp-chat-message-queue-text" }, msg.body))),
111
+ msg.attachments && msg.attachments.length > 0 && (React.createElement("div", { className: "jp-chat-message-queue-attachments" }, msg.attachments.map((attachment, i) => (React.createElement("span", { key: i, className: "jp-chat-message-queue-attachment-item", title: attachment.value },
112
+ React.createElement(AttachmentIcon, { type: attachment.type }),
113
+ attachmentName(attachment))))))),
114
+ editQueuedMessage && targetId && (React.createElement("button", { className: "jp-chat-message-queue-edit", onClick: editingId === msg.id
115
+ ? handleEditSave
116
+ : () => handleEditStart(msg.id, msg.body), title: editingId === msg.id
117
+ ? trans.__('Save')
118
+ : trans.__('Edit message'), type: "button" }, editingId === msg.id ? (React.createElement(checkIcon.react, { tag: "span", className: "jp-chat-message-queue-btn-icon" })) : (React.createElement(editIcon.react, { tag: "span", className: "jp-chat-message-queue-btn-icon" })))),
119
+ removeQueuedMessage && targetId && editingId !== msg.id && (React.createElement("button", { className: "jp-chat-message-queue-remove", onClick: () => removeQueuedMessage(targetId, msg.id), title: trans.__('Remove from queue'), type: "button" },
120
+ React.createElement(closeIcon.react, { tag: "span", className: "jp-chat-message-queue-btn-icon" })))))))),
121
+ dropLineIndex === messages.length && (React.createElement("div", { className: "jp-chat-message-queue-drop-line" })),
122
+ React.createElement("div", { className: "jp-chat-message-queue-end-zone", onDragOver: canDrag
123
+ ? e => {
124
+ e.preventDefault();
125
+ setDropLineIndex(messages.length);
126
+ }
127
+ : undefined })))));
13
128
  };
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { IComponentProps, ToolCallApproval } from '../token';
2
+ import { IComponentProps, IToolCallCallbacks } from '../token';
3
3
  /**
4
4
  * Tool call status types.
5
5
  */
@@ -19,8 +19,7 @@ export interface IToolCallMetadata {
19
19
  /**
20
20
  * Options for building tool call HTML.
21
21
  */
22
- export interface IToolCallProps extends IComponentProps, IToolCallMetadata {
23
- toolCallApproval?: ToolCallApproval;
22
+ export interface IToolCallProps extends IComponentProps, IToolCallMetadata, IToolCallCallbacks {
24
23
  }
25
24
  export declare function escapeHtml(value: string): string;
26
25
  /**
@@ -28,5 +27,7 @@ export declare function escapeHtml(value: string): string;
28
27
  *
29
28
  * Renders a collapsible details element showing tool execution information
30
29
  * including input, output, and approval buttons if needed.
30
+ *
31
+ * @deprecated - the grouped tool call should be used instead.
31
32
  */
32
33
  export declare const ToolCall: React.FC<IToolCallProps>;
@@ -66,6 +66,8 @@ const getStatusText = (status, trans) => {
66
66
  *
67
67
  * Renders a collapsible details element showing tool execution information
68
68
  * including input, output, and approval buttons if needed.
69
+ *
70
+ * @deprecated - the grouped tool call should be used instead.
69
71
  */
70
72
  export const ToolCall = ({ toolName, input, status, summary, output, targetId, approvalId, trans, toolCallApproval }) => {
71
73
  const config = STATUS_CONFIG[status];
package/lib/factory.d.ts CHANGED
@@ -2,28 +2,24 @@ import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
2
2
  import { ReactWidget } from '@jupyterlab/ui-components';
3
3
  import * as React from 'react';
4
4
  import { ComponentRegistry } from './registry';
5
- import { IComponentRegistry, IComponentsRendererFactory, OpenToolCallPath, RemoveQueuedMessage, ToolCallApproval, ToolCallPermissionDecision } from './token';
5
+ import { IComponentRegistry, IComponentsRendererFactory, IGroupedToolCallCallbacks, IQueueMessageCallbacks, IToolCallCallbacks } from './token';
6
6
  type ReactRenderElement = Array<React.ReactElement<any>> | React.ReactElement<any>;
7
7
  /**
8
8
  * The options for the chat components renderer.
9
9
  */
10
10
  interface IComponentsRendererOptions extends IRenderMime.IRendererOptions {
11
11
  /**
12
- * The callback to approve or reject a tool.
12
+ * Callbacks for the ToolCall component.
13
13
  */
14
- toolCallApproval?: ToolCallApproval;
14
+ toolCallCallbacks?: IToolCallCallbacks;
15
15
  /**
16
- * The callback to remove a queued message.
16
+ * Callbacks for the GroupedToolCalls component.
17
17
  */
18
- removeQueuedMessage?: RemoveQueuedMessage;
18
+ groupedToolCallCallbacks?: IGroupedToolCallCallbacks;
19
19
  /**
20
- * The callback to submit a permission decision for grouped tool calls.
20
+ * Callbacks for the MessageQueue component.
21
21
  */
22
- toolCallPermissionDecision?: ToolCallPermissionDecision;
23
- /**
24
- * The callback to open a path referenced by grouped tool calls.
25
- */
26
- openToolCallPath?: OpenToolCallPath;
22
+ queueMessageCallbacks?: IQueueMessageCallbacks;
27
23
  /**
28
24
  * The component registry.
29
25
  */
@@ -44,10 +40,9 @@ export declare class ComponentsRenderer extends ReactWidget implements IRenderMi
44
40
  protected render(): ReactRenderElement | null;
45
41
  private _trans;
46
42
  private _mimeType;
47
- private _toolCallApproval?;
48
- private _removeQueuedMessage?;
49
- private _toolCallPermissionDecision?;
50
- private _openToolCallPath?;
43
+ private _toolCallCallbacks?;
44
+ private _groupedToolCallCallbacks?;
45
+ private _queueMessageCallbacks?;
51
46
  private _registry;
52
47
  private _data;
53
48
  private _metadata;
@@ -60,10 +55,9 @@ export declare class RendererFactory implements IComponentsRendererFactory {
60
55
  readonly mimeTypes: string[];
61
56
  readonly defaultRank = 100;
62
57
  readonly registry: ComponentRegistry;
63
- toolCallApproval: ToolCallApproval;
64
- removeQueuedMessage: RemoveQueuedMessage;
65
- toolCallPermissionDecision: ToolCallPermissionDecision;
66
- openToolCallPath: OpenToolCallPath;
58
+ toolCallCallbacks?: IToolCallCallbacks;
59
+ groupedToolCallCallbacks?: IGroupedToolCallCallbacks;
60
+ queueMessageCallbacks?: IQueueMessageCallbacks;
67
61
  constructor();
68
62
  createRenderer: (options: IRenderMime.IRendererOptions) => ComponentsRenderer;
69
63
  }
package/lib/factory.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { nullTranslator } from '@jupyterlab/translation';
2
2
  import { ReactWidget } from '@jupyterlab/ui-components';
3
3
  import * as React from 'react';
4
- import { GroupedToolCalls, InlineDiff, MessageQueue, ToolCall } from './components';
4
+ import { GroupedToolCalls, InlineDiff, MessageQueue, ToolCall, ErrorMessage } from './components';
5
5
  import { ComponentRegistry } from './registry';
6
6
  /**
7
7
  * The default mime type for the extension.
@@ -25,10 +25,9 @@ export class ComponentsRenderer extends ReactWidget {
25
25
  this._metadata = null;
26
26
  this._trans = ((_a = options.translator) !== null && _a !== void 0 ? _a : nullTranslator).load('jupyterlab');
27
27
  this._mimeType = options.mimeType;
28
- this._toolCallApproval = options.toolCallApproval;
29
- this._removeQueuedMessage = options.removeQueuedMessage;
30
- this._toolCallPermissionDecision = options.toolCallPermissionDecision;
31
- this._openToolCallPath = options.openToolCallPath;
28
+ this._toolCallCallbacks = options.toolCallCallbacks;
29
+ this._groupedToolCallCallbacks = options.groupedToolCallCallbacks;
30
+ this._queueMessageCallbacks = options.queueMessageCallbacks;
32
31
  this._registry = options.registry;
33
32
  this.addClass(CLASS_NAME);
34
33
  }
@@ -52,17 +51,18 @@ export class ComponentsRenderer extends ReactWidget {
52
51
  if (!Component) {
53
52
  return null;
54
53
  }
55
- const componentsProps = { ...this._metadata };
54
+ let componentsProps = { ...this._metadata };
56
55
  if (this._data === 'tool-call') {
57
- componentsProps.toolCallApproval = this._toolCallApproval;
56
+ componentsProps = { ...componentsProps, ...this._toolCallCallbacks };
58
57
  }
59
- if (this._data === 'message-queue') {
60
- componentsProps.removeQueuedMessage = this._removeQueuedMessage;
58
+ else if (this._data === 'message-queue') {
59
+ componentsProps = { ...componentsProps, ...this._queueMessageCallbacks };
61
60
  }
62
- if (this._data === 'grouped-tool-calls') {
63
- componentsProps.toolCallPermissionDecision =
64
- this._toolCallPermissionDecision;
65
- componentsProps.openToolCallPath = this._openToolCallPath;
61
+ else if (this._data === 'grouped-tool-calls') {
62
+ componentsProps = {
63
+ ...componentsProps,
64
+ ...this._groupedToolCallCallbacks
65
+ };
66
66
  }
67
67
  return React.createElement(Component, { ...componentsProps, trans: this._trans });
68
68
  }
@@ -75,17 +75,12 @@ export class RendererFactory {
75
75
  this.safe = true;
76
76
  this.mimeTypes = [MIME_TYPE];
77
77
  this.defaultRank = 100;
78
- this.toolCallApproval = null;
79
- this.removeQueuedMessage = null;
80
- this.toolCallPermissionDecision = null;
81
- this.openToolCallPath = null;
82
78
  this.createRenderer = (options) => {
83
79
  return new ComponentsRenderer({
84
80
  ...options,
85
- toolCallApproval: this.toolCallApproval,
86
- removeQueuedMessage: this.removeQueuedMessage,
87
- toolCallPermissionDecision: this.toolCallPermissionDecision,
88
- openToolCallPath: this.openToolCallPath,
81
+ toolCallCallbacks: this.toolCallCallbacks,
82
+ groupedToolCallCallbacks: this.groupedToolCallCallbacks,
83
+ queueMessageCallbacks: this.queueMessageCallbacks,
89
84
  registry: this.registry
90
85
  });
91
86
  };
@@ -94,5 +89,6 @@ export class RendererFactory {
94
89
  this.registry.add('grouped-tool-calls', GroupedToolCalls);
95
90
  this.registry.add('inline-diff', InlineDiff);
96
91
  this.registry.add('message-queue', MessageQueue);
92
+ this.registry.add('error', ErrorMessage);
97
93
  }
98
94
  }
package/lib/icons.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { LabIcon } from '@jupyterlab/ui-components';
2
+ export declare const errorIcon: LabIcon;
package/lib/icons.js ADDED
@@ -0,0 +1,5 @@
1
+ import { LabIcon } from '@jupyterlab/ui-components';
2
+ export const errorIcon = new LabIcon({
3
+ name: 'jupyter-chat:error',
4
+ svgstr: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>'
5
+ });
package/lib/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export * from './token';
8
8
  export * from './factory';
9
9
  export * from './registry';
10
10
  export * from './components';
11
+ export * from './icons';
11
12
  export default factory;
package/lib/index.js CHANGED
@@ -20,4 +20,5 @@ export * from './token';
20
20
  export * from './factory';
21
21
  export * from './registry';
22
22
  export * from './components';
23
+ export * from './icons';
23
24
  export default factory;
package/lib/token.d.ts CHANGED
@@ -11,17 +11,64 @@ export declare const IComponentsRendererFactory: Token<IComponentsRendererFactor
11
11
  */
12
12
  export type ToolCallApproval = ((targetId: string, approvalId: string, approve: boolean) => void) | null;
13
13
  /**
14
- * The callback to submit a tool-call permission decision.
14
+ * The callback to submit a tool-call permission decision (grouped tool call component).
15
15
  */
16
16
  export type ToolCallPermissionDecision = ((sessionId: string, toolCallId: string, optionId: string) => Promise<void> | void) | null;
17
+ /**
18
+ * The callback to open a file or resource path referenced by a tool call (grouped tool call component).
19
+ */
20
+ export type OpenToolCallPath = ((path: string) => void) | null;
17
21
  /**
18
22
  * The callback to remove a queued message.
19
23
  */
20
24
  export type RemoveQueuedMessage = ((targetId: string, messageId: string) => void) | null;
21
25
  /**
22
- * The callback to open a file or resource path referenced by a tool call.
26
+ * The callback to reorder queued messages. Receives the new ordered list of message IDs.
23
27
  */
24
- export type OpenToolCallPath = ((path: string) => void) | null;
28
+ export type ReorderQueuedMessages = ((targetId: string, messageIds: string[]) => void) | null;
29
+ /**
30
+ * The callback to edit the body of a queued message.
31
+ */
32
+ export type EditQueuedMessage = ((targetId: string, messageId: string, newBody: string) => void) | null;
33
+ /**
34
+ * Callbacks for the ToolCall component.
35
+ */
36
+ export interface IToolCallCallbacks {
37
+ /**
38
+ * The callback to approve or reject a tool.
39
+ */
40
+ toolCallApproval?: ToolCallApproval;
41
+ }
42
+ /**
43
+ * Callbacks for the GroupedToolCalls component.
44
+ */
45
+ export interface IGroupedToolCallCallbacks {
46
+ /**
47
+ * The callback to submit a permission decision for grouped tool calls.
48
+ */
49
+ toolCallPermissionDecision?: ToolCallPermissionDecision;
50
+ /**
51
+ * The callback to open a path referenced by grouped tool calls.
52
+ */
53
+ openToolCallPath?: OpenToolCallPath;
54
+ }
55
+ /**
56
+ * Callbacks for the MessageQueue component.
57
+ */
58
+ export interface IQueueMessageCallbacks {
59
+ /**
60
+ * The callback to remove a queued message.
61
+ */
62
+ removeQueuedMessage?: RemoveQueuedMessage;
63
+ /**
64
+ * The callback to reorder queued messages.
65
+ */
66
+ reorderQueuedMessages?: ReorderQueuedMessages;
67
+ /**
68
+ * The callback to edit the body of a queued message.
69
+ */
70
+ editQueuedMessage?: EditQueuedMessage;
71
+ }
25
72
  /**
26
73
  * The interface for components renderer factory.
27
74
  */
@@ -31,21 +78,17 @@ export interface IComponentsRendererFactory extends IRenderMime.IRendererFactory
31
78
  */
32
79
  registry: IComponentRegistry;
33
80
  /**
34
- * The callback to approve or reject a tool.
35
- */
36
- toolCallApproval: ToolCallApproval;
37
- /**
38
- * The callback to remove a queued message.
81
+ * Grouped callbacks for the ToolCall component.
39
82
  */
40
- removeQueuedMessage: RemoveQueuedMessage;
83
+ toolCallCallbacks?: IToolCallCallbacks;
41
84
  /**
42
- * The callback to submit a permission decision for grouped tool calls.
85
+ * Grouped callbacks for the GroupedToolCalls component.
43
86
  */
44
- toolCallPermissionDecision: ToolCallPermissionDecision;
87
+ groupedToolCallCallbacks?: IGroupedToolCallCallbacks;
45
88
  /**
46
- * The callback to open a path referenced by grouped tool calls.
89
+ * Grouped callbacks for the MessageQueue component.
47
90
  */
48
- openToolCallPath: OpenToolCallPath;
91
+ queueMessageCallbacks?: IQueueMessageCallbacks;
49
92
  }
50
93
  /**
51
94
  * The interface for the component registry.
@@ -256,12 +299,20 @@ export interface IInlineDiffMetadata {
256
299
  */
257
300
  diffs: IInlineDiff[];
258
301
  }
302
+ /**
303
+ * A single attachment in a queued message.
304
+ */
305
+ export interface IQueuedMessageAttachment {
306
+ type: 'file' | 'notebook';
307
+ value: string;
308
+ }
259
309
  /**
260
310
  * A single queued message entry.
261
311
  */
262
312
  export interface IQueuedMessage {
263
313
  id: string;
264
314
  body: string;
315
+ attachments?: IQueuedMessageAttachment[];
265
316
  }
266
317
  /**
267
318
  * Metadata for the message queue component.
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "jupyter-chat-components",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "Components to displayed in jupyter chat",
5
5
  "keywords": [
6
6
  "jupyter",
7
7
  "jupyterlab",
8
8
  "jupyterlab-extension"
9
9
  ],
10
- "homepage": "https://github.com/brichet/jupyter-chat-components",
10
+ "homepage": "https://github.com/jupyter-ai-contrib/jupyter-chat-components",
11
11
  "bugs": {
12
- "url": "https://github.com/brichet/jupyter-chat-components/issues"
12
+ "url": "https://github.com/jupyter-ai-contrib/jupyter-chat-components/issues"
13
13
  },
14
14
  "license": "BSD-3-Clause",
15
15
  "author": {
@@ -26,7 +26,7 @@
26
26
  "style": "style/index.css",
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "https://github.com/brichet/jupyter-chat-components.git"
29
+ "url": "https://github.com/jupyter-ai-contrib/jupyter-chat-components.git"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "jlpm build:lib && jlpm build:labextension:dev",
@@ -61,6 +61,7 @@
61
61
  "@jupyterlab/rendermime": "^4.5.0",
62
62
  "@jupyterlab/rendermime-interfaces": "^3.8.0",
63
63
  "@jupyterlab/translation": "^4.5.0",
64
+ "@jupyterlab/ui-components": "^4.5.0",
64
65
  "@lumino/coreutils": "^2.2.2",
65
66
  "@lumino/widgets": "^2.1.0",
66
67
  "diff": "^8.0.0"
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+
3
+ import { IComponentProps } from '../token';
4
+ import { errorIcon } from '../icons';
5
+
6
+ export interface IErrorProps extends IComponentProps {
7
+ errorMessage: string;
8
+ title?: string;
9
+ }
10
+
11
+ export const ErrorMessage: React.FC<IErrorProps> = ({
12
+ errorMessage,
13
+ title = 'Error'
14
+ }) => {
15
+ return (
16
+ <div className="jp-ai-error-message">
17
+ <div className="jp-ai-error-icon">
18
+ <errorIcon.react tag="span" width="18px" height="18px" />
19
+ </div>
20
+ <div className="jp-ai-error-body">
21
+ <div className="jp-ai-error-title">{title}</div>
22
+ <div className="jp-ai-error-text">{errorMessage}</div>
23
+ </div>
24
+ </div>
25
+ );
26
+ };
@@ -7,6 +7,7 @@ import type { StructuredPatchHunk } from 'diff';
7
7
 
8
8
  import {
9
9
  IComponentProps,
10
+ IGroupedToolCallCallbacks,
10
11
  IToolCallDiff,
11
12
  IToolCallsEntry,
12
13
  IToolCallsMetadata,
@@ -46,10 +47,7 @@ interface IDiffLineInfo {
46
47
  * Props for rendering grouped tool calls.
47
48
  */
48
49
  export interface IGroupedToolCallsProps
49
- extends IComponentProps, IToolCallsMetadata {
50
- toolCallPermissionDecision?: ToolCallPermissionDecision;
51
- openToolCallPath?: OpenToolCallPath;
52
- }
50
+ extends IComponentProps, IToolCallsMetadata, IGroupedToolCallCallbacks {}
53
51
 
54
52
  function getConfiguredServerRoot(): string | null {
55
53
  const rootUri = PageConfig.getOption('rootUri');
@@ -1,3 +1,4 @@
1
+ export * from './error';
1
2
  export * from './inline-diff';
2
3
  export * from './message-queue';
3
4
  export * from './tool-call';