@jupyter/chat 0.13.0 → 0.15.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/lib/active-cell-manager.d.ts +2 -0
- package/lib/active-cell-manager.js +7 -2
- package/lib/components/avatar.d.ts +20 -0
- package/lib/components/avatar.js +29 -0
- package/lib/components/chat.d.ts +1 -3
- package/lib/components/chat.js +2 -3
- package/lib/components/index.d.ts +2 -3
- package/lib/components/index.js +2 -3
- package/lib/components/input/buttons/send-button.js +15 -5
- package/lib/components/{chat-input.d.ts → input/chat-input.d.ts} +3 -3
- package/lib/components/{chat-input.js → input/chat-input.js} +8 -5
- package/lib/components/input/index.d.ts +1 -0
- package/lib/components/input/index.js +1 -0
- package/lib/components/input/toolbar-registry.d.ts +6 -0
- package/lib/components/input/use-chat-commands.d.ts +1 -1
- package/lib/components/input/use-chat-commands.js +32 -13
- package/lib/components/messages/footer.d.ts +2 -2
- package/lib/components/messages/footer.js +1 -1
- package/lib/components/messages/header.d.ts +16 -0
- package/lib/components/messages/header.js +85 -0
- package/lib/components/messages/index.d.ts +9 -0
- package/lib/components/messages/index.js +13 -0
- package/lib/components/messages/message-renderer.js +1 -1
- package/lib/components/messages/message.d.ts +21 -0
- package/lib/components/messages/message.js +102 -0
- package/lib/components/messages/messages.d.ts +38 -0
- package/lib/components/messages/messages.js +139 -0
- package/lib/components/messages/navigation.d.ts +20 -0
- package/lib/components/messages/navigation.js +98 -0
- package/lib/components/messages/writers.d.ts +16 -0
- package/lib/components/messages/writers.js +39 -0
- package/lib/context.d.ts +1 -1
- package/lib/index.d.ts +2 -6
- package/lib/index.js +2 -6
- package/lib/input-model.js +30 -3
- package/lib/{registry.d.ts → registers/attachment-openers.d.ts} +1 -1
- package/lib/registers/chat-commands.d.ts +108 -0
- package/lib/{chat-commands/registry.js → registers/chat-commands.js} +8 -8
- package/lib/{footers/registry.d.ts → registers/footers.d.ts} +30 -5
- package/lib/registers/index.d.ts +3 -0
- package/lib/{footers → registers}/index.js +3 -2
- package/lib/selection-watcher.d.ts +11 -1
- package/lib/selection-watcher.js +10 -4
- package/lib/types.d.ts +7 -2
- package/lib/utils.js +8 -6
- package/lib/widgets/index.d.ts +3 -0
- package/lib/{chat-commands → widgets}/index.js +3 -2
- package/package.json +3 -1
- package/src/active-cell-manager.ts +10 -1
- package/src/components/avatar.tsx +68 -0
- package/src/components/chat.tsx +11 -6
- package/src/components/index.ts +2 -3
- package/src/components/input/buttons/send-button.tsx +17 -5
- package/src/components/{chat-input.tsx → input/chat-input.tsx} +13 -8
- package/src/components/input/index.ts +1 -0
- package/src/components/input/toolbar-registry.tsx +6 -0
- package/src/components/input/use-chat-commands.tsx +39 -16
- package/src/components/messages/footer.tsx +5 -2
- package/src/components/messages/header.tsx +133 -0
- package/src/components/messages/index.ts +14 -0
- package/src/components/messages/message-renderer.tsx +1 -1
- package/src/components/messages/message.tsx +156 -0
- package/src/components/messages/messages.tsx +218 -0
- package/src/components/messages/navigation.tsx +167 -0
- package/src/components/messages/welcome.tsx +1 -0
- package/src/components/messages/writers.tsx +81 -0
- package/src/context.ts +1 -1
- package/src/index.ts +2 -6
- package/src/input-model.ts +33 -4
- package/src/{registry.ts → registers/attachment-openers.ts} +2 -1
- package/src/registers/chat-commands.ts +142 -0
- package/src/{footers/registry.ts → registers/footers.ts} +35 -8
- package/src/{footers → registers}/index.ts +3 -2
- package/src/selection-watcher.ts +28 -5
- package/src/types.ts +7 -2
- package/src/utils.ts +8 -6
- package/src/{chat-commands → widgets}/index.ts +3 -2
- package/style/chat.css +82 -0
- package/lib/chat-commands/index.d.ts +0 -2
- package/lib/chat-commands/registry.d.ts +0 -28
- package/lib/chat-commands/types.d.ts +0 -52
- package/lib/chat-commands/types.js +0 -5
- package/lib/components/chat-messages.d.ts +0 -119
- package/lib/components/chat-messages.js +0 -446
- package/lib/footers/index.d.ts +0 -2
- package/lib/footers/types.d.ts +0 -26
- package/lib/footers/types.js +0 -5
- package/src/chat-commands/registry.ts +0 -60
- package/src/chat-commands/types.ts +0 -67
- package/src/components/chat-messages.tsx +0 -739
- package/src/footers/types.ts +0 -33
- package/lib/components/{toolbar.d.ts → messages/toolbar.d.ts} +0 -0
- package/lib/components/{toolbar.js → messages/toolbar.js} +0 -0
- package/lib/{registry.js → registers/attachment-openers.js} +0 -0
- package/lib/{footers/registry.js → registers/footers.js} +4 -4
- /package/src/components/{toolbar.tsx → messages/toolbar.tsx} +0 -0
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { Token } from '@lumino/coreutils';
|
|
6
|
+
/**
|
|
7
|
+
* The token for the chat command registry, which can be provided by an extension
|
|
8
|
+
* using @jupyter/chat package.
|
|
9
|
+
*/
|
|
10
|
+
export const IChatCommandRegistry = new Token('@jupyter/chat:IChatCommandRegistry');
|
|
6
11
|
/**
|
|
7
12
|
* Default chat command registry implementation.
|
|
8
13
|
*/
|
|
@@ -16,14 +21,9 @@ export class ChatCommandRegistry {
|
|
|
16
21
|
getProviders() {
|
|
17
22
|
return Array.from(this._providers.values());
|
|
18
23
|
}
|
|
19
|
-
|
|
20
|
-
const provider
|
|
21
|
-
|
|
22
|
-
console.error('Error in handling chat command: No command provider has an ID of ' +
|
|
23
|
-
command.providerId);
|
|
24
|
-
return;
|
|
24
|
+
async onSubmit(inputModel) {
|
|
25
|
+
for (const provider of this._providers.values()) {
|
|
26
|
+
await provider.onSubmit(inputModel);
|
|
25
27
|
}
|
|
26
|
-
provider.handleChatCommand(command, inputModel);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
export const IChatCommandRegistry = new Token('@jupyter/chat:IChatCommandRegistry');
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
1
2
|
import { Token } from '@lumino/coreutils';
|
|
2
|
-
import {
|
|
3
|
+
import { IChatModel } from '../model';
|
|
4
|
+
import { IChatMessage } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* The token providing the chat footer registry.
|
|
7
|
+
*/
|
|
8
|
+
export declare const IMessageFooterRegistry: Token<IMessageFooterRegistry>;
|
|
3
9
|
/**
|
|
4
10
|
* The interface of a registry to provide chat footer.
|
|
5
11
|
*/
|
|
@@ -15,6 +21,29 @@ export interface IMessageFooterRegistry {
|
|
|
15
21
|
*/
|
|
16
22
|
addSection(section: MessageFooterSection): void;
|
|
17
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The props sent passed to each `MessageFooterSection` React component.
|
|
26
|
+
*/
|
|
27
|
+
export type MessageFooterSectionProps = {
|
|
28
|
+
model: IChatModel;
|
|
29
|
+
message: IChatMessage;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A message footer section which can be added to the footer registry.
|
|
33
|
+
*/
|
|
34
|
+
export type MessageFooterSection = {
|
|
35
|
+
component: React.FC<MessageFooterSectionProps>;
|
|
36
|
+
position: 'left' | 'center' | 'right';
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The message footer returned by the registry, composed of 'left', 'center',
|
|
40
|
+
* and 'right' sections.
|
|
41
|
+
*/
|
|
42
|
+
export type MessageFooter = {
|
|
43
|
+
left?: MessageFooterSection;
|
|
44
|
+
center?: MessageFooterSection;
|
|
45
|
+
right?: MessageFooterSection;
|
|
46
|
+
};
|
|
18
47
|
/**
|
|
19
48
|
* The default implementation of the message footer registry.
|
|
20
49
|
*/
|
|
@@ -30,7 +59,3 @@ export declare class MessageFooterRegistry implements IMessageFooterRegistry {
|
|
|
30
59
|
addSection(footer: MessageFooterSection): void;
|
|
31
60
|
private _footers;
|
|
32
61
|
}
|
|
33
|
-
/**
|
|
34
|
-
* The token providing the chat footer registry.
|
|
35
|
-
*/
|
|
36
|
-
export declare const IMessageFooterRegistry: Token<IMessageFooterRegistry>;
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
* Copyright (c) Jupyter Development Team.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
5
|
+
export * from './attachment-openers';
|
|
6
|
+
export * from './chat-commands';
|
|
7
|
+
export * from './footers';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
2
|
import { CodeEditor } from '@jupyterlab/codeeditor';
|
|
3
|
+
import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
|
|
3
4
|
import { Widget } from '@lumino/widgets';
|
|
4
5
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
5
6
|
/**
|
|
@@ -14,6 +15,10 @@ export declare namespace SelectionWatcher {
|
|
|
14
15
|
* The current shell of the application.
|
|
15
16
|
*/
|
|
16
17
|
shell: JupyterFrontEnd.IShell;
|
|
18
|
+
/**
|
|
19
|
+
* Editor language registry.
|
|
20
|
+
*/
|
|
21
|
+
languages?: IEditorLanguageRegistry;
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
24
|
* The selection type.
|
|
@@ -31,6 +36,10 @@ export declare namespace SelectionWatcher {
|
|
|
31
36
|
* The ID of the document widget in which the selection was made.
|
|
32
37
|
*/
|
|
33
38
|
widgetId: string;
|
|
39
|
+
/**
|
|
40
|
+
* The language of the selection.
|
|
41
|
+
*/
|
|
42
|
+
language?: string;
|
|
34
43
|
/**
|
|
35
44
|
* The ID of the cell in which the selection was made, if the original widget
|
|
36
45
|
* was a notebook.
|
|
@@ -54,9 +63,10 @@ export declare class SelectionWatcher {
|
|
|
54
63
|
get selection(): SelectionWatcher.Selection | null;
|
|
55
64
|
get selectionChanged(): ISignal<this, SelectionWatcher.Selection | null>;
|
|
56
65
|
replaceSelection(selection: SelectionWatcher.Selection): void;
|
|
57
|
-
protected _poll(): void
|
|
66
|
+
protected _poll(): Promise<void>;
|
|
58
67
|
protected _shell: JupyterFrontEnd.IShell;
|
|
59
68
|
protected _mainAreaDocumentWidget: Widget | null;
|
|
60
69
|
protected _selection: SelectionWatcher.Selection | null;
|
|
61
70
|
protected _selectionChanged: Signal<this, SelectionWatcher.Selection | null>;
|
|
71
|
+
private _languages;
|
|
62
72
|
}
|
package/lib/selection-watcher.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
6
|
+
import { EditorLanguageRegistry } from '@jupyterlab/codemirror';
|
|
6
7
|
import { Notebook } from '@jupyterlab/notebook';
|
|
7
8
|
import { find } from '@lumino/algorithm';
|
|
8
9
|
import { Signal } from '@lumino/signaling';
|
|
@@ -17,6 +18,7 @@ export class SelectionWatcher {
|
|
|
17
18
|
this._selection = null;
|
|
18
19
|
this._selectionChanged = new Signal(this);
|
|
19
20
|
this._shell = options.shell;
|
|
21
|
+
this._languages = options.languages || new EditorLanguageRegistry();
|
|
20
22
|
(_a = this._shell.currentChanged) === null || _a === void 0 ? void 0 : _a.connect((sender, args) => {
|
|
21
23
|
var _a;
|
|
22
24
|
// Do not change the main area widget if the new one has no editor, for example
|
|
@@ -76,13 +78,13 @@ export class SelectionWatcher {
|
|
|
76
78
|
const newPosition = editor.getPositionAt(editor.getOffsetAt(selection.start) + selection.text.length);
|
|
77
79
|
editor.setSelection({ start: newPosition, end: newPosition });
|
|
78
80
|
}
|
|
79
|
-
_poll() {
|
|
81
|
+
async _poll() {
|
|
80
82
|
var _a;
|
|
81
83
|
let currSelection = null;
|
|
82
84
|
const prevSelection = this._selection;
|
|
83
85
|
// Do not return selected text if the main area widget is hidden.
|
|
84
86
|
if ((_a = this._mainAreaDocumentWidget) === null || _a === void 0 ? void 0 : _a.isVisible) {
|
|
85
|
-
currSelection = getTextSelection(this._mainAreaDocumentWidget);
|
|
87
|
+
currSelection = await getTextSelection(this._mainAreaDocumentWidget, this._languages);
|
|
86
88
|
}
|
|
87
89
|
if ((prevSelection === null || prevSelection === void 0 ? void 0 : prevSelection.text) !== (currSelection === null || currSelection === void 0 ? void 0 : currSelection.text)) {
|
|
88
90
|
this._selection = currSelection;
|
|
@@ -93,8 +95,8 @@ export class SelectionWatcher {
|
|
|
93
95
|
/**
|
|
94
96
|
* Gets a Selection object from a document widget. Returns `null` if unable.
|
|
95
97
|
*/
|
|
96
|
-
function getTextSelection(widget) {
|
|
97
|
-
var _a;
|
|
98
|
+
async function getTextSelection(widget, languages) {
|
|
99
|
+
var _a, _b;
|
|
98
100
|
const editor = getEditor(widget);
|
|
99
101
|
// widget type check is redundant but hints the type to TypeScript
|
|
100
102
|
if (!editor || !(widget instanceof DocumentWidget)) {
|
|
@@ -120,6 +122,7 @@ function getTextSelection(widget) {
|
|
|
120
122
|
if (startOffset > endOffset) {
|
|
121
123
|
[start, end] = [end, start];
|
|
122
124
|
}
|
|
125
|
+
const language = (_b = (await languages.getLanguage(editor === null || editor === void 0 ? void 0 : editor.model.mimeType))) === null || _b === void 0 ? void 0 : _b.name;
|
|
123
126
|
return {
|
|
124
127
|
...selectionObj,
|
|
125
128
|
start,
|
|
@@ -127,6 +130,9 @@ function getTextSelection(widget) {
|
|
|
127
130
|
text,
|
|
128
131
|
numLines: text.split('\n').length,
|
|
129
132
|
widgetId: widget.id,
|
|
133
|
+
...(language && {
|
|
134
|
+
language
|
|
135
|
+
}),
|
|
130
136
|
...(cellId && {
|
|
131
137
|
cellId
|
|
132
138
|
})
|
package/lib/types.d.ts
CHANGED
|
@@ -9,9 +9,14 @@ export interface IUser {
|
|
|
9
9
|
color?: string;
|
|
10
10
|
avatar_url?: string;
|
|
11
11
|
/**
|
|
12
|
-
* The string to use to mention a user in the chat.
|
|
12
|
+
* The string to use to mention a user in the chat. This is computed via the
|
|
13
|
+
* following procedure:
|
|
14
|
+
*
|
|
15
|
+
* 1. Let `mention_name = user.display_name || user.name || user.username`.
|
|
16
|
+
*
|
|
17
|
+
* 2. Replace each ' ' character with '-' in `mention_name`.
|
|
13
18
|
*/
|
|
14
|
-
mention_name
|
|
19
|
+
mention_name: string;
|
|
15
20
|
/**
|
|
16
21
|
* Boolean identifying if user is a bot.
|
|
17
22
|
*/
|
package/lib/utils.js
CHANGED
|
@@ -46,9 +46,10 @@ export function replaceMentionToSpan(content, user) {
|
|
|
46
46
|
if (!user.mention_name) {
|
|
47
47
|
return content;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
|
|
49
|
+
const mention = '@' + user.mention_name;
|
|
50
|
+
const regex = new RegExp(mention, 'g');
|
|
51
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
52
|
+
return content.replace(regex, mentionEl);
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Replace a span to a mentioned to user string (@someone).
|
|
@@ -60,7 +61,8 @@ export function replaceSpanToMention(content, user) {
|
|
|
60
61
|
if (!user.mention_name) {
|
|
61
62
|
return content;
|
|
62
63
|
}
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
64
|
+
const mention = '@' + user.mention_name;
|
|
65
|
+
const mentionEl = `<span class="${MENTION_CLASS}">${mention}</span>`;
|
|
66
|
+
const regex = new RegExp(mentionEl, 'g');
|
|
67
|
+
return content.replace(regex, mention);
|
|
66
68
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "A package that provides UI components that can be used to create a chat in a Jupyterlab extension.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"@jupyter/react-components": "^0.15.2",
|
|
49
49
|
"@jupyterlab/application": "^4.2.0",
|
|
50
50
|
"@jupyterlab/apputils": "^4.3.0",
|
|
51
|
+
"@jupyterlab/codeeditor": "^4.2.0",
|
|
52
|
+
"@jupyterlab/codemirror": "^4.2.0",
|
|
51
53
|
"@jupyterlab/docmanager": "^4.2.0",
|
|
52
54
|
"@jupyterlab/filebrowser": "^4.2.0",
|
|
53
55
|
"@jupyterlab/fileeditor": "^4.2.0",
|
|
@@ -13,11 +13,13 @@ import { ISignal, Signal } from '@lumino/signaling';
|
|
|
13
13
|
type CellContent = {
|
|
14
14
|
type: string;
|
|
15
15
|
source: string;
|
|
16
|
+
language?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
type CellWithErrorContent = {
|
|
19
20
|
type: 'code';
|
|
20
21
|
source: string;
|
|
22
|
+
language?: string;
|
|
21
23
|
error: {
|
|
22
24
|
name: string;
|
|
23
25
|
value: string;
|
|
@@ -148,6 +150,11 @@ export class ActiveCellManager implements IActiveCellManager {
|
|
|
148
150
|
getContent(withError: true): CellWithErrorContent | null;
|
|
149
151
|
getContent(withError = false): CellContent | CellWithErrorContent | null {
|
|
150
152
|
const sharedModel = this._notebookTracker.activeCell?.model.sharedModel;
|
|
153
|
+
const language =
|
|
154
|
+
sharedModel?.cell_type === 'code'
|
|
155
|
+
? this._notebookTracker.currentWidget?.model?.defaultKernelLanguage
|
|
156
|
+
: undefined;
|
|
157
|
+
|
|
151
158
|
if (!sharedModel) {
|
|
152
159
|
return null;
|
|
153
160
|
}
|
|
@@ -156,7 +163,8 @@ export class ActiveCellManager implements IActiveCellManager {
|
|
|
156
163
|
if (!withError) {
|
|
157
164
|
return {
|
|
158
165
|
type: sharedModel.cell_type,
|
|
159
|
-
source: sharedModel.getSource()
|
|
166
|
+
source: sharedModel.getSource(),
|
|
167
|
+
language
|
|
160
168
|
};
|
|
161
169
|
}
|
|
162
170
|
|
|
@@ -166,6 +174,7 @@ export class ActiveCellManager implements IActiveCellManager {
|
|
|
166
174
|
return {
|
|
167
175
|
type: 'code',
|
|
168
176
|
source: sharedModel.getSource(),
|
|
177
|
+
language,
|
|
169
178
|
error: {
|
|
170
179
|
name: error.ename,
|
|
171
180
|
value: error.evalue,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Avatar as MuiAvatar, Typography } from '@mui/material';
|
|
7
|
+
import type { SxProps, Theme } from '@mui/material';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
import { IUser } from '../types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The avatar props.
|
|
14
|
+
*/
|
|
15
|
+
type AvatarProps = {
|
|
16
|
+
/**
|
|
17
|
+
* The user to display an avatar.
|
|
18
|
+
*/
|
|
19
|
+
user: IUser;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the avatar should be small.
|
|
22
|
+
*/
|
|
23
|
+
small?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The avatar component.
|
|
28
|
+
*/
|
|
29
|
+
export function Avatar(props: AvatarProps): JSX.Element | null {
|
|
30
|
+
const { user } = props;
|
|
31
|
+
|
|
32
|
+
const sharedStyles: SxProps<Theme> = {
|
|
33
|
+
height: `${props.small ? '16' : '24'}px`,
|
|
34
|
+
width: `${props.small ? '16' : '24'}px`,
|
|
35
|
+
bgcolor: user.color,
|
|
36
|
+
fontSize: `var(--jp-ui-font-size${props.small ? '0' : '1'})`
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const name =
|
|
40
|
+
user.display_name ?? user.name ?? (user.username || 'User undefined');
|
|
41
|
+
return user.avatar_url ? (
|
|
42
|
+
<MuiAvatar
|
|
43
|
+
sx={{
|
|
44
|
+
...sharedStyles
|
|
45
|
+
}}
|
|
46
|
+
src={user.avatar_url}
|
|
47
|
+
alt={name}
|
|
48
|
+
title={name}
|
|
49
|
+
></MuiAvatar>
|
|
50
|
+
) : user.initials ? (
|
|
51
|
+
<MuiAvatar
|
|
52
|
+
sx={{
|
|
53
|
+
...sharedStyles
|
|
54
|
+
}}
|
|
55
|
+
alt={name}
|
|
56
|
+
title={name}
|
|
57
|
+
>
|
|
58
|
+
<Typography
|
|
59
|
+
sx={{
|
|
60
|
+
fontSize: `var(--jp-ui-font-size${props.small ? '0' : '1'})`,
|
|
61
|
+
color: 'var(--jp-ui-inverse-font-color1)'
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
{user.initials}
|
|
65
|
+
</Typography>
|
|
66
|
+
</MuiAvatar>
|
|
67
|
+
) : null;
|
|
68
|
+
}
|
package/src/components/chat.tsx
CHANGED
|
@@ -11,15 +11,20 @@ import { IconButton } from '@mui/material';
|
|
|
11
11
|
import { Box } from '@mui/system';
|
|
12
12
|
import React, { useState } from 'react';
|
|
13
13
|
|
|
14
|
+
import {
|
|
15
|
+
ChatInput,
|
|
16
|
+
IInputToolbarRegistry,
|
|
17
|
+
InputToolbarRegistry
|
|
18
|
+
} from './input';
|
|
14
19
|
import { JlThemeProvider } from './jl-theme-provider';
|
|
15
|
-
import {
|
|
16
|
-
import { ChatMessages } from './chat-messages';
|
|
17
|
-
import { ChatInput } from './chat-input';
|
|
18
|
-
import { IInputToolbarRegistry, InputToolbarRegistry } from './input';
|
|
20
|
+
import { ChatMessages } from './messages';
|
|
19
21
|
import { AttachmentOpenerContext } from '../context';
|
|
20
|
-
import { IMessageFooterRegistry } from '../footers';
|
|
21
22
|
import { IChatModel } from '../model';
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
IAttachmentOpenerRegistry,
|
|
25
|
+
IChatCommandRegistry,
|
|
26
|
+
IMessageFooterRegistry
|
|
27
|
+
} from '../registers';
|
|
23
28
|
|
|
24
29
|
export function ChatBody(props: Chat.IChatBodyProps): JSX.Element {
|
|
25
30
|
const { model } = props;
|
package/src/components/index.ts
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
export * from './avatar';
|
|
6
7
|
export * from './chat';
|
|
7
|
-
export * from './chat-input';
|
|
8
|
-
export * from './chat-messages';
|
|
9
8
|
export * from './code-blocks';
|
|
10
9
|
export * from './input';
|
|
11
10
|
export * from './jl-theme-provider';
|
|
11
|
+
export * from './messages';
|
|
12
12
|
export * from './mui-extras';
|
|
13
13
|
export * from './scroll-container';
|
|
14
|
-
export * from './toolbar';
|
|
@@ -23,7 +23,7 @@ const SEND_INCLUDE_LI_CLASS = 'jp-chat-send-include';
|
|
|
23
23
|
export function SendButton(
|
|
24
24
|
props: InputToolbarRegistry.IToolbarItemProps
|
|
25
25
|
): JSX.Element {
|
|
26
|
-
const { model } = props;
|
|
26
|
+
const { model, chatCommandRegistry } = props;
|
|
27
27
|
const { activeCellManager, selectionWatcher } = model;
|
|
28
28
|
const hideIncludeSelection = !activeCellManager || !selectionWatcher;
|
|
29
29
|
|
|
@@ -98,21 +98,33 @@ export function SendButton(
|
|
|
98
98
|
};
|
|
99
99
|
}, [activeCellManager, selectionWatcher]);
|
|
100
100
|
|
|
101
|
-
function
|
|
101
|
+
async function send() {
|
|
102
|
+
await chatCommandRegistry?.onSubmit(model);
|
|
103
|
+
model.send(model.value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function sendWithSelection() {
|
|
102
107
|
let source = '';
|
|
103
108
|
|
|
109
|
+
// Run all chat command providers
|
|
110
|
+
await chatCommandRegistry?.onSubmit(model);
|
|
111
|
+
|
|
112
|
+
let language: string | undefined;
|
|
104
113
|
if (selectionWatcher?.selection) {
|
|
105
114
|
// Append the selected text if exists.
|
|
106
115
|
source = selectionWatcher.selection.text;
|
|
116
|
+
language = selectionWatcher.selection.language;
|
|
107
117
|
} else if (activeCellManager?.available) {
|
|
108
118
|
// Append the active cell content if exists.
|
|
109
|
-
|
|
119
|
+
const content = activeCellManager.getContent(false);
|
|
120
|
+
source = content!.source;
|
|
121
|
+
language = content?.language;
|
|
110
122
|
}
|
|
111
123
|
let content = model.value;
|
|
112
124
|
if (source) {
|
|
113
125
|
content += `
|
|
114
126
|
|
|
115
|
-
|
|
127
|
+
\`\`\`${language ?? ''}
|
|
116
128
|
${source}
|
|
117
129
|
\`\`\`
|
|
118
130
|
`;
|
|
@@ -125,7 +137,7 @@ ${source}
|
|
|
125
137
|
return (
|
|
126
138
|
<>
|
|
127
139
|
<TooltippedButton
|
|
128
|
-
onClick={
|
|
140
|
+
onClick={send}
|
|
129
141
|
disabled={disabled}
|
|
130
142
|
tooltip={tooltip}
|
|
131
143
|
buttonProps={{
|
|
@@ -15,15 +15,15 @@ import {
|
|
|
15
15
|
import clsx from 'clsx';
|
|
16
16
|
import React, { useEffect, useRef, useState } from 'react';
|
|
17
17
|
|
|
18
|
-
import { AttachmentPreviewList } from '
|
|
18
|
+
import { AttachmentPreviewList } from '../attachments';
|
|
19
19
|
import {
|
|
20
20
|
IInputToolbarRegistry,
|
|
21
21
|
InputToolbarRegistry,
|
|
22
22
|
useChatCommands
|
|
23
|
-
} from '
|
|
24
|
-
import { IInputModel, InputModel } from '
|
|
25
|
-
import { IChatCommandRegistry } from '
|
|
26
|
-
import { IAttachment } from '
|
|
23
|
+
} from '.';
|
|
24
|
+
import { IInputModel, InputModel } from '../../input-model';
|
|
25
|
+
import { IChatCommandRegistry } from '../../registers';
|
|
26
|
+
import { IAttachment } from '../../types';
|
|
27
27
|
|
|
28
28
|
const INPUT_BOX_CLASS = 'jp-chat-input-container';
|
|
29
29
|
const INPUT_TOOLBAR_CLASS = 'jp-chat-input-toolbar';
|
|
@@ -105,7 +105,7 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
|
|
|
105
105
|
* "Enter". This also handles many of the edge cases in the MUI Autocomplete
|
|
106
106
|
* component.
|
|
107
107
|
*/
|
|
108
|
-
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
|
|
108
|
+
async function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
|
|
109
109
|
/**
|
|
110
110
|
* IMPORTANT: This statement ensures that arrow keys can be used to navigate
|
|
111
111
|
* the multiline input when the chat commands menu is closed.
|
|
@@ -157,7 +157,9 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
|
|
|
157
157
|
(sendWithShiftEnter && event.shiftKey) ||
|
|
158
158
|
(!sendWithShiftEnter && !event.shiftKey)
|
|
159
159
|
) {
|
|
160
|
-
|
|
160
|
+
// Run all command providers
|
|
161
|
+
await props.chatCommandRegistry?.onSubmit(model);
|
|
162
|
+
model.send(model.value);
|
|
161
163
|
event.stopPropagation();
|
|
162
164
|
event.preventDefault();
|
|
163
165
|
}
|
|
@@ -218,7 +220,10 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
|
|
|
218
220
|
endAdornment: (
|
|
219
221
|
<InputAdornment position="end" className={INPUT_TOOLBAR_CLASS}>
|
|
220
222
|
{toolbarElements.map(item => (
|
|
221
|
-
<item.element
|
|
223
|
+
<item.element
|
|
224
|
+
model={model}
|
|
225
|
+
chatCommandRegistry={props.chatCommandRegistry}
|
|
226
|
+
/>
|
|
222
227
|
))}
|
|
223
228
|
</InputAdornment>
|
|
224
229
|
)
|
|
@@ -7,6 +7,7 @@ import * as React from 'react';
|
|
|
7
7
|
import { AttachButton, CancelButton, SendButton } from './buttons';
|
|
8
8
|
import { IInputModel } from '../../input-model';
|
|
9
9
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
10
|
+
import { IChatCommandRegistry } from '../../registers';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* The toolbar registry interface.
|
|
@@ -137,6 +138,11 @@ export namespace InputToolbarRegistry {
|
|
|
137
138
|
* The input model of the input component including the button.
|
|
138
139
|
*/
|
|
139
140
|
model: IInputModel;
|
|
141
|
+
/**
|
|
142
|
+
* Chat command registry. Should be used by the "Send" button to run
|
|
143
|
+
* `onSubmit()` on all command providers before sending the message.
|
|
144
|
+
*/
|
|
145
|
+
chatCommandRegistry?: IChatCommandRegistry;
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
/**
|