@jupyter/chat 0.9.0 → 0.10.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/__tests__/mocks.d.ts +9 -0
- package/lib/__tests__/mocks.js +18 -0
- package/lib/__tests__/model.spec.js +17 -10
- package/lib/__tests__/widgets.spec.js +4 -4
- package/lib/chat-commands/types.d.ts +2 -1
- package/lib/components/chat-input.d.ts +1 -1
- package/lib/components/chat-messages.js +23 -10
- package/lib/components/input/use-chat-commands.js +3 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/input-model.d.ts +53 -2
- package/lib/input-model.js +38 -0
- package/lib/model.d.ts +54 -9
- package/lib/model.js +26 -12
- package/lib/types.d.ts +5 -0
- package/lib/utils.d.ts +15 -0
- package/lib/utils.js +29 -0
- package/package.json +1 -1
- package/src/__tests__/mocks.ts +31 -0
- package/src/__tests__/model.spec.ts +21 -11
- package/src/__tests__/widgets.spec.ts +5 -4
- package/src/chat-commands/types.ts +1 -1
- package/src/components/chat-input.tsx +2 -2
- package/src/components/chat-messages.tsx +16 -6
- package/src/components/input/use-chat-commands.tsx +6 -4
- package/src/index.ts +1 -1
- package/src/input-model.ts +83 -2
- package/src/model.ts +74 -10
- package/src/types.ts +5 -0
- package/src/utils.ts +34 -0
- package/style/chat.css +6 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AbstractChatContext, AbstractChatModel, IChatModel, IChatContext } from '../model';
|
|
2
|
+
import { INewMessage } from '../types';
|
|
3
|
+
export declare class MockChatContext extends AbstractChatContext implements IChatContext {
|
|
4
|
+
get users(): never[];
|
|
5
|
+
}
|
|
6
|
+
export declare class MockChatModel extends AbstractChatModel implements IChatModel {
|
|
7
|
+
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
8
|
+
createChatContext(): IChatContext;
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { AbstractChatContext, AbstractChatModel } from '../model';
|
|
6
|
+
export class MockChatContext extends AbstractChatContext {
|
|
7
|
+
get users() {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class MockChatModel extends AbstractChatModel {
|
|
12
|
+
sendMessage(message) {
|
|
13
|
+
// No-op
|
|
14
|
+
}
|
|
15
|
+
createChatContext() {
|
|
16
|
+
return new MockChatContext({ model: this });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -5,25 +5,32 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { AbstractChatModel } from '../model';
|
|
9
|
+
import { MockChatModel, MockChatContext } from './mocks';
|
|
9
10
|
describe('test chat model', () => {
|
|
10
11
|
describe('model instantiation', () => {
|
|
11
|
-
it('should create
|
|
12
|
-
const model = new
|
|
13
|
-
expect(model).toBeInstanceOf(
|
|
12
|
+
it('should create an AbstractChatModel', () => {
|
|
13
|
+
const model = new MockChatModel();
|
|
14
|
+
expect(model).toBeInstanceOf(AbstractChatModel);
|
|
14
15
|
});
|
|
15
|
-
it('should dispose
|
|
16
|
-
const model = new
|
|
16
|
+
it('should dispose an AbstractChatModel', () => {
|
|
17
|
+
const model = new MockChatModel();
|
|
17
18
|
model.dispose();
|
|
18
19
|
expect(model.isDisposed).toBeTruthy();
|
|
19
20
|
});
|
|
20
21
|
});
|
|
21
22
|
describe('incoming message', () => {
|
|
22
|
-
class TestChat extends
|
|
23
|
+
class TestChat extends AbstractChatModel {
|
|
23
24
|
formatChatMessage(message) {
|
|
24
25
|
message.body = 'formatted msg';
|
|
25
26
|
return message;
|
|
26
27
|
}
|
|
28
|
+
sendMessage(message) {
|
|
29
|
+
// No-op
|
|
30
|
+
}
|
|
31
|
+
createChatContext() {
|
|
32
|
+
return new MockChatContext({ model: this });
|
|
33
|
+
}
|
|
27
34
|
}
|
|
28
35
|
let model;
|
|
29
36
|
let messages;
|
|
@@ -38,7 +45,7 @@ describe('test chat model', () => {
|
|
|
38
45
|
messages = [];
|
|
39
46
|
});
|
|
40
47
|
it('should signal incoming message', () => {
|
|
41
|
-
model = new
|
|
48
|
+
model = new MockChatModel();
|
|
42
49
|
model.messagesUpdated.connect((sender) => {
|
|
43
50
|
expect(sender).toBe(model);
|
|
44
51
|
messages = model.messages;
|
|
@@ -61,11 +68,11 @@ describe('test chat model', () => {
|
|
|
61
68
|
});
|
|
62
69
|
describe('model config', () => {
|
|
63
70
|
it('should have empty config', () => {
|
|
64
|
-
const model = new
|
|
71
|
+
const model = new MockChatModel();
|
|
65
72
|
expect(model.config.sendWithShiftEnter).toBeUndefined();
|
|
66
73
|
});
|
|
67
74
|
it('should allow config', () => {
|
|
68
|
-
const model = new
|
|
75
|
+
const model = new MockChatModel({ config: { sendWithShiftEnter: true } });
|
|
69
76
|
expect(model.config.sendWithShiftEnter).toBeTruthy();
|
|
70
77
|
});
|
|
71
78
|
});
|
|
@@ -6,21 +6,21 @@
|
|
|
6
6
|
* Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
|
|
7
7
|
*/
|
|
8
8
|
import { RenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
9
|
-
import { ChatModel } from '../model';
|
|
10
9
|
import { ChatWidget } from '../widgets/chat-widget';
|
|
10
|
+
import { MockChatModel } from './mocks';
|
|
11
11
|
describe('test chat widget', () => {
|
|
12
12
|
let model;
|
|
13
13
|
let rmRegistry;
|
|
14
14
|
beforeEach(() => {
|
|
15
|
-
model = new
|
|
15
|
+
model = new MockChatModel();
|
|
16
16
|
rmRegistry = new RenderMimeRegistry();
|
|
17
17
|
});
|
|
18
18
|
describe('model instantiation', () => {
|
|
19
|
-
it('should create
|
|
19
|
+
it('should create an AbstractChatModel', () => {
|
|
20
20
|
const widget = new ChatWidget({ model, rmRegistry });
|
|
21
21
|
expect(widget).toBeInstanceOf(ChatWidget);
|
|
22
22
|
});
|
|
23
|
-
it('should dispose
|
|
23
|
+
it('should dispose an AbstractChatModel', () => {
|
|
24
24
|
const widget = new ChatWidget({ model, rmRegistry });
|
|
25
25
|
widget.dispose();
|
|
26
26
|
expect(widget.isDisposed).toBeTruthy();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
1
2
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
3
|
import { IInputModel } from '../input-model';
|
|
3
4
|
export type ChatCommand = {
|
|
@@ -14,7 +15,7 @@ export type ChatCommand = {
|
|
|
14
15
|
* If set, this will be rendered as the icon for the command in the chat
|
|
15
16
|
* commands menu. Jupyter Chat will choose a default if this is unset.
|
|
16
17
|
*/
|
|
17
|
-
icon?: LabIcon | string;
|
|
18
|
+
icon?: LabIcon | JSX.Element | string | null;
|
|
18
19
|
/**
|
|
19
20
|
* If set, this will be rendered as the description for the command in the
|
|
20
21
|
* chat commands menu. Jupyter Chat will choose a default if this is unset.
|
|
@@ -13,6 +13,7 @@ import { ChatInput } from './chat-input';
|
|
|
13
13
|
import { MarkdownRenderer } from './markdown-renderer';
|
|
14
14
|
import { ScrollContainer } from './scroll-container';
|
|
15
15
|
import { InputModel } from '../input-model';
|
|
16
|
+
import { replaceSpanToMention } from '../utils';
|
|
16
17
|
const MESSAGES_BOX_CLASS = 'jp-chat-messages-container';
|
|
17
18
|
const MESSAGE_CLASS = 'jp-chat-message';
|
|
18
19
|
const MESSAGE_STACKED_CLASS = 'jp-chat-message-stacked';
|
|
@@ -246,16 +247,27 @@ export const ChatMessage = forwardRef((props, ref) => {
|
|
|
246
247
|
// Create an input model only if the message is edited.
|
|
247
248
|
useEffect(() => {
|
|
248
249
|
if (edit && canEdit) {
|
|
249
|
-
setInputModel(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
250
|
+
setInputModel(() => {
|
|
251
|
+
var _a;
|
|
252
|
+
let body = message.body;
|
|
253
|
+
(_a = message.mentions) === null || _a === void 0 ? void 0 : _a.forEach(user => {
|
|
254
|
+
body = replaceSpanToMention(body, user);
|
|
255
|
+
});
|
|
256
|
+
return new InputModel({
|
|
257
|
+
chatContext: model.createChatContext(),
|
|
258
|
+
onSend: (input, model) => updateMessage(message.id, input, model),
|
|
259
|
+
onCancel: () => cancelEdition(),
|
|
260
|
+
value: body,
|
|
261
|
+
activeCellManager: model.activeCellManager,
|
|
262
|
+
selectionWatcher: model.selectionWatcher,
|
|
263
|
+
documentManager: model.documentManager,
|
|
264
|
+
config: {
|
|
265
|
+
sendWithShiftEnter: model.config.sendWithShiftEnter
|
|
266
|
+
},
|
|
267
|
+
attachments: message.attachments,
|
|
268
|
+
mentions: message.mentions
|
|
269
|
+
});
|
|
270
|
+
});
|
|
259
271
|
}
|
|
260
272
|
else {
|
|
261
273
|
setInputModel(null);
|
|
@@ -274,6 +286,7 @@ export const ChatMessage = forwardRef((props, ref) => {
|
|
|
274
286
|
const updatedMessage = { ...message };
|
|
275
287
|
updatedMessage.body = input;
|
|
276
288
|
updatedMessage.attachments = inputModel.attachments;
|
|
289
|
+
updatedMessage.mentions = inputModel.mentions;
|
|
277
290
|
model.updateMessage(id, updatedMessage);
|
|
278
291
|
setEdit(false);
|
|
279
292
|
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Jupyter Development Team.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
6
|
-
import { useEffect, useState } from 'react';
|
|
5
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
7
6
|
import { Box } from '@mui/material';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
8
8
|
/**
|
|
9
9
|
* A hook which automatically returns the list of command options given the
|
|
10
10
|
* current input and chat command registry.
|
|
@@ -83,7 +83,7 @@ export function useChatCommands(inputModel, chatCommandRegistry) {
|
|
|
83
83
|
getOptionLabel: (command) => command.name,
|
|
84
84
|
renderOption: (defaultProps, command, __, ___) => {
|
|
85
85
|
const { key, ...listItemProps } = defaultProps;
|
|
86
|
-
const commandIcon = (React.createElement("span", null,
|
|
86
|
+
const commandIcon = React.isValidElement(command.icon) ? (command.icon) : (React.createElement("span", null, command.icon instanceof LabIcon ? (React.createElement(command.icon.react, null)) : (command.icon)));
|
|
87
87
|
return (React.createElement(Box, { key: key, component: "li", ...listItemProps },
|
|
88
88
|
commandIcon,
|
|
89
89
|
React.createElement("p", { className: "jp-chat-command-name" }, command.name),
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from './active-cell-manager';
|
|
2
|
+
export * from './chat-commands';
|
|
2
3
|
export * from './components';
|
|
3
4
|
export * from './icons';
|
|
4
5
|
export * from './input-model';
|
|
@@ -9,4 +10,3 @@ export * from './types';
|
|
|
9
10
|
export * from './widgets/chat-error';
|
|
10
11
|
export * from './widgets/chat-sidebar';
|
|
11
12
|
export * from './widgets/chat-widget';
|
|
12
|
-
export * from './chat-commands';
|
package/lib/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
export * from './active-cell-manager';
|
|
6
|
+
export * from './chat-commands';
|
|
6
7
|
export * from './components';
|
|
7
8
|
export * from './icons';
|
|
8
9
|
export * from './input-model';
|
|
@@ -13,4 +14,3 @@ export * from './types';
|
|
|
13
14
|
export * from './widgets/chat-error';
|
|
14
15
|
export * from './widgets/chat-sidebar';
|
|
15
16
|
export * from './widgets/chat-widget';
|
|
16
|
-
export * from './chat-commands';
|
package/lib/input-model.d.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
1
2
|
import { IDisposable } from '@lumino/disposable';
|
|
2
3
|
import { ISignal } from '@lumino/signaling';
|
|
3
4
|
import { IActiveCellManager } from './active-cell-manager';
|
|
4
5
|
import { ISelectionWatcher } from './selection-watcher';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { IChatContext } from './model';
|
|
7
|
+
import { IAttachment, IUser } from './types';
|
|
7
8
|
/**
|
|
8
9
|
* The chat input interface.
|
|
9
10
|
*/
|
|
10
11
|
export interface IInputModel extends IDisposable {
|
|
12
|
+
/**
|
|
13
|
+
* The chat context (a readonly subset of the chat model).
|
|
14
|
+
*/
|
|
15
|
+
readonly chatContext: IChatContext;
|
|
11
16
|
/**
|
|
12
17
|
* Function to send a message.
|
|
13
18
|
*/
|
|
@@ -93,12 +98,32 @@ export interface IInputModel extends IDisposable {
|
|
|
93
98
|
* Replace the current word in the input with a new one.
|
|
94
99
|
*/
|
|
95
100
|
replaceCurrentWord(newWord: string): void;
|
|
101
|
+
/**
|
|
102
|
+
* The mentioned user list.
|
|
103
|
+
*/
|
|
104
|
+
readonly mentions: IUser[];
|
|
105
|
+
/**
|
|
106
|
+
* Add user mention.
|
|
107
|
+
*/
|
|
108
|
+
addMention?(user: IUser): void;
|
|
109
|
+
/**
|
|
110
|
+
* Remove a user mention.
|
|
111
|
+
*/
|
|
112
|
+
removeMention(user: IUser): void;
|
|
113
|
+
/**
|
|
114
|
+
* Clear mentions list.
|
|
115
|
+
*/
|
|
116
|
+
clearMentions(): void;
|
|
96
117
|
}
|
|
97
118
|
/**
|
|
98
119
|
* The input model.
|
|
99
120
|
*/
|
|
100
121
|
export declare class InputModel implements IInputModel {
|
|
101
122
|
constructor(options: InputModel.IOptions);
|
|
123
|
+
/**
|
|
124
|
+
* The chat context (a readonly subset of the chat model);
|
|
125
|
+
*/
|
|
126
|
+
get chatContext(): IChatContext;
|
|
102
127
|
/**
|
|
103
128
|
* Function to send a message.
|
|
104
129
|
*/
|
|
@@ -186,6 +211,22 @@ export declare class InputModel implements IInputModel {
|
|
|
186
211
|
* Replace the current word in the input with a new one.
|
|
187
212
|
*/
|
|
188
213
|
replaceCurrentWord(newWord: string): void;
|
|
214
|
+
/**
|
|
215
|
+
* The mentioned user list.
|
|
216
|
+
*/
|
|
217
|
+
get mentions(): IUser[];
|
|
218
|
+
/**
|
|
219
|
+
* Add a user mention.
|
|
220
|
+
*/
|
|
221
|
+
addMention(user: IUser): void;
|
|
222
|
+
/**
|
|
223
|
+
* Remove a user mention.
|
|
224
|
+
*/
|
|
225
|
+
removeMention(user: IUser): void;
|
|
226
|
+
/**
|
|
227
|
+
* Clear mentions list.
|
|
228
|
+
*/
|
|
229
|
+
clearMentions: () => void;
|
|
189
230
|
/**
|
|
190
231
|
* Dispose the input model.
|
|
191
232
|
*/
|
|
@@ -195,10 +236,12 @@ export declare class InputModel implements IInputModel {
|
|
|
195
236
|
*/
|
|
196
237
|
get isDisposed(): boolean;
|
|
197
238
|
private _onSend;
|
|
239
|
+
private _chatContext;
|
|
198
240
|
private _value;
|
|
199
241
|
private _cursorIndex;
|
|
200
242
|
private _currentWord;
|
|
201
243
|
private _attachments;
|
|
244
|
+
private _mentions;
|
|
202
245
|
private _activeCellManager;
|
|
203
246
|
private _selectionWatcher;
|
|
204
247
|
private _documentManager;
|
|
@@ -213,6 +256,10 @@ export declare class InputModel implements IInputModel {
|
|
|
213
256
|
}
|
|
214
257
|
export declare namespace InputModel {
|
|
215
258
|
interface IOptions {
|
|
259
|
+
/**
|
|
260
|
+
* The chat context (a readonly subset of the chat model).
|
|
261
|
+
*/
|
|
262
|
+
chatContext: IChatContext;
|
|
216
263
|
/**
|
|
217
264
|
* The function that should send the message.
|
|
218
265
|
* @param content - the content of the message.
|
|
@@ -231,6 +278,10 @@ export declare namespace InputModel {
|
|
|
231
278
|
* The initial attachments.
|
|
232
279
|
*/
|
|
233
280
|
attachments?: IAttachment[];
|
|
281
|
+
/**
|
|
282
|
+
* The initial mentions.
|
|
283
|
+
*/
|
|
284
|
+
mentions?: IUser[];
|
|
234
285
|
/**
|
|
235
286
|
* The current cursor index.
|
|
236
287
|
* This refers to the index of the character in front of the cursor.
|
package/lib/input-model.js
CHANGED
|
@@ -46,6 +46,12 @@ export class InputModel {
|
|
|
46
46
|
this._attachments = [];
|
|
47
47
|
this._attachmentsChanged.emit([]);
|
|
48
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Clear mentions list.
|
|
51
|
+
*/
|
|
52
|
+
this.clearMentions = () => {
|
|
53
|
+
this._mentions = [];
|
|
54
|
+
};
|
|
49
55
|
this._cursorIndex = null;
|
|
50
56
|
this._currentWord = null;
|
|
51
57
|
this._valueChanged = new Signal(this);
|
|
@@ -56,8 +62,10 @@ export class InputModel {
|
|
|
56
62
|
this._attachmentsChanged = new Signal(this);
|
|
57
63
|
this._isDisposed = false;
|
|
58
64
|
this._onSend = options.onSend;
|
|
65
|
+
this._chatContext = options.chatContext;
|
|
59
66
|
this._value = options.value || '';
|
|
60
67
|
this._attachments = options.attachments || [];
|
|
68
|
+
this._mentions = options.mentions || [];
|
|
61
69
|
this.cursorIndex = options.cursorIndex || this.value.length;
|
|
62
70
|
this._activeCellManager = (_a = options.activeCellManager) !== null && _a !== void 0 ? _a : null;
|
|
63
71
|
this._selectionWatcher = (_b = options.selectionWatcher) !== null && _b !== void 0 ? _b : null;
|
|
@@ -67,6 +75,12 @@ export class InputModel {
|
|
|
67
75
|
};
|
|
68
76
|
this.cancel = options.onCancel;
|
|
69
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The chat context (a readonly subset of the chat model);
|
|
80
|
+
*/
|
|
81
|
+
get chatContext() {
|
|
82
|
+
return this._chatContext;
|
|
83
|
+
}
|
|
70
84
|
/**
|
|
71
85
|
* The entire input value.
|
|
72
86
|
*/
|
|
@@ -190,6 +204,30 @@ export class InputModel {
|
|
|
190
204
|
const [start, end] = Private.getCurrentWordBoundaries(this.value, this.cursorIndex);
|
|
191
205
|
this.value = this.value.slice(0, start) + newWord + this.value.slice(end);
|
|
192
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* The mentioned user list.
|
|
209
|
+
*/
|
|
210
|
+
get mentions() {
|
|
211
|
+
return this._mentions;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Add a user mention.
|
|
215
|
+
*/
|
|
216
|
+
addMention(user) {
|
|
217
|
+
const usernames = this._mentions.map(user => user.username);
|
|
218
|
+
if (!usernames.includes(user.username)) {
|
|
219
|
+
this._mentions.push(user);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Remove a user mention.
|
|
224
|
+
*/
|
|
225
|
+
removeMention(user) {
|
|
226
|
+
const index = this._mentions.indexOf(user);
|
|
227
|
+
if (index > -1) {
|
|
228
|
+
this._mentions.splice(index, 1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
193
231
|
/**
|
|
194
232
|
* Dispose the input model.
|
|
195
233
|
*/
|
package/lib/model.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
|
2
2
|
import { CommandRegistry } from '@lumino/commands';
|
|
3
3
|
import { IDisposable } from '@lumino/disposable';
|
|
4
4
|
import { ISignal } from '@lumino/signaling';
|
|
5
|
-
import { IChatHistory, INewMessage, IChatMessage, IConfig, IUser } from './types';
|
|
6
5
|
import { IActiveCellManager } from './active-cell-manager';
|
|
7
|
-
import { ISelectionWatcher } from './selection-watcher';
|
|
8
6
|
import { IInputModel } from './input-model';
|
|
7
|
+
import { ISelectionWatcher } from './selection-watcher';
|
|
8
|
+
import { IChatHistory, INewMessage, IChatMessage, IConfig, IUser } from './types';
|
|
9
9
|
/**
|
|
10
10
|
* The chat model interface.
|
|
11
11
|
*/
|
|
@@ -131,17 +131,22 @@ export interface IChatModel extends IDisposable {
|
|
|
131
131
|
* Update the current writers list.
|
|
132
132
|
*/
|
|
133
133
|
updateWriters(writers: IUser[]): void;
|
|
134
|
+
/**
|
|
135
|
+
* Create the chat context that will be passed to the input model.
|
|
136
|
+
*/
|
|
137
|
+
createChatContext(): IChatContext;
|
|
134
138
|
}
|
|
135
139
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
140
|
+
* An abstract implementation of IChatModel.
|
|
141
|
+
*
|
|
142
|
+
* The class inheriting from it must implement at least:
|
|
143
|
+
* - sendMessage(message: INewMessage)
|
|
139
144
|
*/
|
|
140
|
-
export declare class
|
|
145
|
+
export declare abstract class AbstractChatModel implements IChatModel {
|
|
141
146
|
/**
|
|
142
147
|
* Create a new chat model.
|
|
143
148
|
*/
|
|
144
|
-
constructor(options?:
|
|
149
|
+
constructor(options?: IChatModel.IOptions);
|
|
145
150
|
/**
|
|
146
151
|
* The chat model id.
|
|
147
152
|
*/
|
|
@@ -219,7 +224,7 @@ export declare class ChatModel implements IChatModel {
|
|
|
219
224
|
* @param message - the message to send.
|
|
220
225
|
* @returns whether the message has been sent or not.
|
|
221
226
|
*/
|
|
222
|
-
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
227
|
+
abstract sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
223
228
|
/**
|
|
224
229
|
* Clear the message list.
|
|
225
230
|
*/
|
|
@@ -262,6 +267,10 @@ export declare class ChatModel implements IChatModel {
|
|
|
262
267
|
* This implementation only propagate the list via a signal.
|
|
263
268
|
*/
|
|
264
269
|
updateWriters(writers: IUser[]): void;
|
|
270
|
+
/**
|
|
271
|
+
* Create the chat context that will be passed to the input model.
|
|
272
|
+
*/
|
|
273
|
+
abstract createChatContext(): IChatContext;
|
|
265
274
|
/**
|
|
266
275
|
* Add unread messages to the list.
|
|
267
276
|
* @param indexes - list of new indexes.
|
|
@@ -299,7 +308,7 @@ export declare class ChatModel implements IChatModel {
|
|
|
299
308
|
/**
|
|
300
309
|
* The chat model namespace.
|
|
301
310
|
*/
|
|
302
|
-
export declare namespace
|
|
311
|
+
export declare namespace IChatModel {
|
|
303
312
|
/**
|
|
304
313
|
* The instantiation options for a ChatModel.
|
|
305
314
|
*/
|
|
@@ -330,3 +339,39 @@ export declare namespace ChatModel {
|
|
|
330
339
|
documentManager?: IDocumentManager | null;
|
|
331
340
|
}
|
|
332
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Interface of the chat context, a 'subset' of the model with readonly attribute,
|
|
344
|
+
* which can be passed to the input model.
|
|
345
|
+
* This allows third party extensions to get some attribute of the model without
|
|
346
|
+
* exposing the method that can modify it.
|
|
347
|
+
*/
|
|
348
|
+
export interface IChatContext {
|
|
349
|
+
/**
|
|
350
|
+
* The name of the chat.
|
|
351
|
+
*/
|
|
352
|
+
readonly name: string;
|
|
353
|
+
/**
|
|
354
|
+
* A copy of the messages.
|
|
355
|
+
*/
|
|
356
|
+
readonly messages: IChatMessage[];
|
|
357
|
+
/**
|
|
358
|
+
* A list of all users who have connected to this chat.
|
|
359
|
+
*/
|
|
360
|
+
readonly users: IUser[];
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* An abstract base class implementing `IChatContext`. This can be extended into
|
|
364
|
+
* a complete implementation, as done in `jupyterlab-chat`.
|
|
365
|
+
*/
|
|
366
|
+
export declare abstract class AbstractChatContext implements IChatContext {
|
|
367
|
+
constructor(options: {
|
|
368
|
+
model: IChatModel;
|
|
369
|
+
});
|
|
370
|
+
get name(): string;
|
|
371
|
+
get messages(): IChatMessage[];
|
|
372
|
+
/**
|
|
373
|
+
* ABSTRACT: Should return a list of users who have connected to this chat.
|
|
374
|
+
*/
|
|
375
|
+
abstract get users(): IUser[];
|
|
376
|
+
protected _model: IChatModel;
|
|
377
|
+
}
|
package/lib/model.js
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Signal } from '@lumino/signaling';
|
|
6
6
|
import { InputModel } from './input-model';
|
|
7
|
+
import { replaceMentionToSpan } from './utils';
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* An abstract implementation of IChatModel.
|
|
10
|
+
*
|
|
11
|
+
* The class inheriting from it must implement at least:
|
|
12
|
+
* - sendMessage(message: INewMessage)
|
|
11
13
|
*/
|
|
12
|
-
export class
|
|
14
|
+
export class AbstractChatModel {
|
|
13
15
|
/**
|
|
14
16
|
* Create a new chat model.
|
|
15
17
|
*/
|
|
@@ -37,6 +39,7 @@ export class ChatModel {
|
|
|
37
39
|
...config
|
|
38
40
|
};
|
|
39
41
|
this._inputModel = new InputModel({
|
|
42
|
+
chatContext: this.createChatContext(),
|
|
40
43
|
activeCellManager: options.activeCellManager,
|
|
41
44
|
selectionWatcher: options.selectionWatcher,
|
|
42
45
|
documentManager: options.documentManager,
|
|
@@ -220,14 +223,6 @@ export class ChatModel {
|
|
|
220
223
|
get writersChanged() {
|
|
221
224
|
return this._writersChanged;
|
|
222
225
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Send a message, to be defined depending on the chosen technology.
|
|
225
|
-
* Default to no-op.
|
|
226
|
-
*
|
|
227
|
-
* @param message - the message to send.
|
|
228
|
-
* @returns whether the message has been sent or not.
|
|
229
|
-
*/
|
|
230
|
-
sendMessage(message) { }
|
|
231
226
|
/**
|
|
232
227
|
* Clear the message list.
|
|
233
228
|
*/
|
|
@@ -255,6 +250,10 @@ export class ChatModel {
|
|
|
255
250
|
* Can be useful if some actions are required on the message.
|
|
256
251
|
*/
|
|
257
252
|
formatChatMessage(message) {
|
|
253
|
+
var _a;
|
|
254
|
+
(_a = message.mentions) === null || _a === void 0 ? void 0 : _a.forEach(user => {
|
|
255
|
+
message.body = replaceMentionToSpan(message.body, user);
|
|
256
|
+
});
|
|
258
257
|
return message;
|
|
259
258
|
}
|
|
260
259
|
/**
|
|
@@ -379,3 +378,18 @@ export class ChatModel {
|
|
|
379
378
|
}
|
|
380
379
|
}
|
|
381
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* An abstract base class implementing `IChatContext`. This can be extended into
|
|
383
|
+
* a complete implementation, as done in `jupyterlab-chat`.
|
|
384
|
+
*/
|
|
385
|
+
export class AbstractChatContext {
|
|
386
|
+
constructor(options) {
|
|
387
|
+
this._model = options.model;
|
|
388
|
+
}
|
|
389
|
+
get name() {
|
|
390
|
+
return this._model.name;
|
|
391
|
+
}
|
|
392
|
+
get messages() {
|
|
393
|
+
return [...this._model.messages];
|
|
394
|
+
}
|
|
395
|
+
}
|
package/lib/types.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export interface IUser {
|
|
|
8
8
|
initials?: string;
|
|
9
9
|
color?: string;
|
|
10
10
|
avatar_url?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The string to use to mention a user in the chat.
|
|
13
|
+
*/
|
|
14
|
+
mention_name?: string;
|
|
11
15
|
}
|
|
12
16
|
/**
|
|
13
17
|
* The configuration interface.
|
|
@@ -44,6 +48,7 @@ export interface IChatMessage<T = IUser, U = IAttachment> {
|
|
|
44
48
|
time: number;
|
|
45
49
|
sender: T;
|
|
46
50
|
attachments?: U[];
|
|
51
|
+
mentions?: T[];
|
|
47
52
|
raw_time?: boolean;
|
|
48
53
|
deleted?: boolean;
|
|
49
54
|
edited?: boolean;
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CodeMirrorEditor } from '@jupyterlab/codemirror';
|
|
2
2
|
import { Notebook } from '@jupyterlab/notebook';
|
|
3
3
|
import { Widget } from '@lumino/widgets';
|
|
4
|
+
import { IUser } from './types';
|
|
4
5
|
/**
|
|
5
6
|
* Gets the editor instance used by a document widget. Returns `null` if unable.
|
|
6
7
|
*/
|
|
@@ -9,3 +10,17 @@ export declare function getEditor(widget: Widget | null): CodeMirrorEditor | nul
|
|
|
9
10
|
* Gets the index of the cell associated with `cellId`.
|
|
10
11
|
*/
|
|
11
12
|
export declare function getCellIndex(notebook: Notebook, cellId: string): number;
|
|
13
|
+
/**
|
|
14
|
+
* Replace a mention to user (@someone) to a span, for markdown renderer.
|
|
15
|
+
*
|
|
16
|
+
* @param content - the content to update.
|
|
17
|
+
* @param user - the user mentioned.
|
|
18
|
+
*/
|
|
19
|
+
export declare function replaceMentionToSpan(content: string, user: IUser): string;
|
|
20
|
+
/**
|
|
21
|
+
* Replace a span to a mentioned to user string (@someone).
|
|
22
|
+
*
|
|
23
|
+
* @param content - the content to update.
|
|
24
|
+
* @param user - the user mentioned.
|
|
25
|
+
*/
|
|
26
|
+
export declare function replaceSpanToMention(content: string, user: IUser): string;
|