jupyterlab-chat 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/lib/__tests__/jupyterlab_collaborative_chat.spec.d.ts +0 -0
- package/lib/__tests__/jupyterlab_collaborative_chat.spec.js +13 -0
- package/lib/factory.d.ts +118 -0
- package/lib/factory.js +146 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +9 -0
- package/lib/model.d.ts +64 -0
- package/lib/model.js +216 -0
- package/lib/token.d.ts +91 -0
- package/lib/token.js +59 -0
- package/lib/widget.d.ts +121 -0
- package/lib/widget.js +342 -0
- package/lib/ychat.d.ts +75 -0
- package/lib/ychat.js +148 -0
- package/package.json +205 -0
- package/src/__tests__/jupyterlab_collaborative_chat.spec.ts +14 -0
- package/src/factory.ts +207 -0
- package/src/index.ts +10 -0
- package/src/model.ts +273 -0
- package/src/token.ts +126 -0
- package/src/widget.tsx +479 -0
- package/src/ychat.ts +219 -0
- package/style/base.css +22 -0
- package/style/index.css +6 -0
- package/style/index.js +6 -0
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) Jupyter Development Team.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
|
|
8
|
+
*/
|
|
9
|
+
describe('jupyterlab-chat', () => {
|
|
10
|
+
it('should be tested', () => {
|
|
11
|
+
expect(1 + 1).toEqual(2);
|
|
12
|
+
});
|
|
13
|
+
});
|
package/lib/factory.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { IAutocompletionRegistry } from '@jupyter/chat';
|
|
2
|
+
import { IThemeManager } from '@jupyterlab/apputils';
|
|
3
|
+
import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
5
|
+
import { Contents } from '@jupyterlab/services';
|
|
6
|
+
import { ISignal } from '@lumino/signaling';
|
|
7
|
+
import { LabChatModel } from './model';
|
|
8
|
+
import { LabChatPanel } from './widget';
|
|
9
|
+
import { YChat } from './ychat';
|
|
10
|
+
import { ILabChatConfig, IWidgetConfig } from './token';
|
|
11
|
+
/**
|
|
12
|
+
* The object provided by the chatDocument extension.
|
|
13
|
+
* It is used to set the current config (from settings) to newly created chat widget,
|
|
14
|
+
* and to propagate every changes to the existing chat widgets.
|
|
15
|
+
*/
|
|
16
|
+
export declare class WidgetConfig implements IWidgetConfig {
|
|
17
|
+
/**
|
|
18
|
+
* The constructor of the WidgetConfig.
|
|
19
|
+
*/
|
|
20
|
+
constructor(config: Partial<ILabChatConfig>);
|
|
21
|
+
/**
|
|
22
|
+
* Getter and setter for the config.
|
|
23
|
+
*/
|
|
24
|
+
get config(): Partial<ILabChatConfig>;
|
|
25
|
+
set config(value: Partial<ILabChatConfig>);
|
|
26
|
+
/**
|
|
27
|
+
* Getter for the configChanged signal
|
|
28
|
+
*/
|
|
29
|
+
get configChanged(): ISignal<WidgetConfig, Partial<ILabChatConfig>>;
|
|
30
|
+
private _config;
|
|
31
|
+
private _configChanged;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A widget factory to create new instances of LabChatWidget.
|
|
35
|
+
*/
|
|
36
|
+
export declare class ChatWidgetFactory extends ABCWidgetFactory<LabChatPanel, LabChatModel> {
|
|
37
|
+
/**
|
|
38
|
+
* Constructor of ChatWidgetFactory.
|
|
39
|
+
*
|
|
40
|
+
* @param options Constructor options
|
|
41
|
+
*/
|
|
42
|
+
constructor(options: ChatWidgetFactory.IOptions<LabChatPanel>);
|
|
43
|
+
/**
|
|
44
|
+
* Create a new widget given a context.
|
|
45
|
+
*
|
|
46
|
+
* @param context Contains the information of the file
|
|
47
|
+
* @returns The widget
|
|
48
|
+
*/
|
|
49
|
+
protected createNewWidget(context: ChatWidgetFactory.IContext): LabChatPanel;
|
|
50
|
+
private _themeManager;
|
|
51
|
+
private _rmRegistry;
|
|
52
|
+
private _autocompletionRegistry?;
|
|
53
|
+
}
|
|
54
|
+
export declare namespace ChatWidgetFactory {
|
|
55
|
+
interface IContext extends DocumentRegistry.IContext<LabChatModel> {
|
|
56
|
+
themeManager: IThemeManager | null;
|
|
57
|
+
rmRegistry: IRenderMimeRegistry;
|
|
58
|
+
autocompletionRegistry?: IAutocompletionRegistry;
|
|
59
|
+
}
|
|
60
|
+
interface IOptions<T extends LabChatPanel> extends DocumentRegistry.IWidgetFactoryOptions<T> {
|
|
61
|
+
themeManager: IThemeManager | null;
|
|
62
|
+
rmRegistry: IRenderMimeRegistry;
|
|
63
|
+
autocompletionRegistry?: IAutocompletionRegistry;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export declare class LabChatModelFactory implements DocumentRegistry.IModelFactory<LabChatModel> {
|
|
67
|
+
constructor(options: LabChatModel.IOptions);
|
|
68
|
+
collaborative: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* The name of the model.
|
|
71
|
+
*
|
|
72
|
+
* @returns The name
|
|
73
|
+
*/
|
|
74
|
+
get name(): string;
|
|
75
|
+
/**
|
|
76
|
+
* The content type of the file.
|
|
77
|
+
*
|
|
78
|
+
* @returns The content type
|
|
79
|
+
*/
|
|
80
|
+
get contentType(): Contents.ContentType;
|
|
81
|
+
/**
|
|
82
|
+
* The format of the file.
|
|
83
|
+
*
|
|
84
|
+
* @returns the file format
|
|
85
|
+
*/
|
|
86
|
+
get fileFormat(): Contents.FileFormat;
|
|
87
|
+
/**
|
|
88
|
+
* Get whether the model factory has been disposed.
|
|
89
|
+
*
|
|
90
|
+
* @returns disposed status
|
|
91
|
+
*/
|
|
92
|
+
get isDisposed(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Dispose the model factory.
|
|
95
|
+
*/
|
|
96
|
+
dispose(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Get the preferred language given the path on the file.
|
|
99
|
+
*
|
|
100
|
+
* @param path path of the file represented by this document model
|
|
101
|
+
* @returns The preferred language
|
|
102
|
+
*/
|
|
103
|
+
preferredLanguage(path: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Create a new instance of LabChatModel.
|
|
106
|
+
*
|
|
107
|
+
* @param languagePreference Language
|
|
108
|
+
* @param modelDB Model database
|
|
109
|
+
* @returns The model
|
|
110
|
+
*/
|
|
111
|
+
createNew(options: DocumentRegistry.IModelOptions<YChat>): LabChatModel;
|
|
112
|
+
private _disposed;
|
|
113
|
+
private _user;
|
|
114
|
+
private _widgetConfig;
|
|
115
|
+
private _commands?;
|
|
116
|
+
private _activeCellManager;
|
|
117
|
+
private _selectionWatcher;
|
|
118
|
+
}
|
package/lib/factory.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { ChatWidget } from '@jupyter/chat';
|
|
6
|
+
import { ABCWidgetFactory } from '@jupyterlab/docregistry';
|
|
7
|
+
import { Signal } from '@lumino/signaling';
|
|
8
|
+
import { LabChatModel } from './model';
|
|
9
|
+
import { LabChatPanel } from './widget';
|
|
10
|
+
/**
|
|
11
|
+
* The object provided by the chatDocument extension.
|
|
12
|
+
* It is used to set the current config (from settings) to newly created chat widget,
|
|
13
|
+
* and to propagate every changes to the existing chat widgets.
|
|
14
|
+
*/
|
|
15
|
+
export class WidgetConfig {
|
|
16
|
+
/**
|
|
17
|
+
* The constructor of the WidgetConfig.
|
|
18
|
+
*/
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this._configChanged = new Signal(this);
|
|
21
|
+
this._config = config;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Getter and setter for the config.
|
|
25
|
+
*/
|
|
26
|
+
get config() {
|
|
27
|
+
return this._config;
|
|
28
|
+
}
|
|
29
|
+
set config(value) {
|
|
30
|
+
this._config = { ...this._config, ...value };
|
|
31
|
+
this._configChanged.emit(value);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Getter for the configChanged signal
|
|
35
|
+
*/
|
|
36
|
+
get configChanged() {
|
|
37
|
+
return this._configChanged;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A widget factory to create new instances of LabChatWidget.
|
|
42
|
+
*/
|
|
43
|
+
export class ChatWidgetFactory extends ABCWidgetFactory {
|
|
44
|
+
/**
|
|
45
|
+
* Constructor of ChatWidgetFactory.
|
|
46
|
+
*
|
|
47
|
+
* @param options Constructor options
|
|
48
|
+
*/
|
|
49
|
+
constructor(options) {
|
|
50
|
+
super(options);
|
|
51
|
+
this._themeManager = options.themeManager;
|
|
52
|
+
this._rmRegistry = options.rmRegistry;
|
|
53
|
+
this._autocompletionRegistry = options.autocompletionRegistry;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a new widget given a context.
|
|
57
|
+
*
|
|
58
|
+
* @param context Contains the information of the file
|
|
59
|
+
* @returns The widget
|
|
60
|
+
*/
|
|
61
|
+
createNewWidget(context) {
|
|
62
|
+
context.rmRegistry = this._rmRegistry;
|
|
63
|
+
context.themeManager = this._themeManager;
|
|
64
|
+
context.autocompletionRegistry = this._autocompletionRegistry;
|
|
65
|
+
return new LabChatPanel({
|
|
66
|
+
context,
|
|
67
|
+
content: new ChatWidget(context)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export class LabChatModelFactory {
|
|
72
|
+
constructor(options) {
|
|
73
|
+
var _a, _b;
|
|
74
|
+
this.collaborative = true;
|
|
75
|
+
this._disposed = false;
|
|
76
|
+
this._user = options.user;
|
|
77
|
+
this._widgetConfig = options.widgetConfig;
|
|
78
|
+
this._commands = options.commands;
|
|
79
|
+
this._activeCellManager = (_a = options.activeCellManager) !== null && _a !== void 0 ? _a : null;
|
|
80
|
+
this._selectionWatcher = (_b = options.selectionWatcher) !== null && _b !== void 0 ? _b : null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The name of the model.
|
|
84
|
+
*
|
|
85
|
+
* @returns The name
|
|
86
|
+
*/
|
|
87
|
+
get name() {
|
|
88
|
+
return 'chat';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The content type of the file.
|
|
92
|
+
*
|
|
93
|
+
* @returns The content type
|
|
94
|
+
*/
|
|
95
|
+
get contentType() {
|
|
96
|
+
return 'chat';
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The format of the file.
|
|
100
|
+
*
|
|
101
|
+
* @returns the file format
|
|
102
|
+
*/
|
|
103
|
+
get fileFormat() {
|
|
104
|
+
return 'text';
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get whether the model factory has been disposed.
|
|
108
|
+
*
|
|
109
|
+
* @returns disposed status
|
|
110
|
+
*/
|
|
111
|
+
get isDisposed() {
|
|
112
|
+
return this._disposed;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Dispose the model factory.
|
|
116
|
+
*/
|
|
117
|
+
dispose() {
|
|
118
|
+
this._disposed = true;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get the preferred language given the path on the file.
|
|
122
|
+
*
|
|
123
|
+
* @param path path of the file represented by this document model
|
|
124
|
+
* @returns The preferred language
|
|
125
|
+
*/
|
|
126
|
+
preferredLanguage(path) {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a new instance of LabChatModel.
|
|
131
|
+
*
|
|
132
|
+
* @param languagePreference Language
|
|
133
|
+
* @param modelDB Model database
|
|
134
|
+
* @returns The model
|
|
135
|
+
*/
|
|
136
|
+
createNew(options) {
|
|
137
|
+
return new LabChatModel({
|
|
138
|
+
...options,
|
|
139
|
+
user: this._user,
|
|
140
|
+
widgetConfig: this._widgetConfig,
|
|
141
|
+
commands: this._commands,
|
|
142
|
+
activeCellManager: this._activeCellManager,
|
|
143
|
+
selectionWatcher: this._selectionWatcher
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
package/lib/model.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ChatModel, IChatMessage, INewMessage, IUser } from '@jupyter/chat';
|
|
2
|
+
import { IChangedArgs } from '@jupyterlab/coreutils';
|
|
3
|
+
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
|
+
import { User } from '@jupyterlab/services';
|
|
5
|
+
import { PartialJSONObject } from '@lumino/coreutils';
|
|
6
|
+
import { ISignal } from '@lumino/signaling';
|
|
7
|
+
import { IWidgetConfig } from './token';
|
|
8
|
+
import { YChat } from './ychat';
|
|
9
|
+
/**
|
|
10
|
+
* Chat model namespace.
|
|
11
|
+
*/
|
|
12
|
+
export declare namespace LabChatModel {
|
|
13
|
+
interface IOptions extends ChatModel.IOptions {
|
|
14
|
+
widgetConfig: IWidgetConfig;
|
|
15
|
+
user: User.IIdentity | null;
|
|
16
|
+
sharedModel?: YChat;
|
|
17
|
+
languagePreference?: string;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The chat model.
|
|
22
|
+
*/
|
|
23
|
+
export declare class LabChatModel extends ChatModel implements DocumentRegistry.IModel {
|
|
24
|
+
constructor(options: LabChatModel.IOptions);
|
|
25
|
+
readonly collaborative = true;
|
|
26
|
+
get user(): IUser;
|
|
27
|
+
get sharedModel(): YChat;
|
|
28
|
+
get contentChanged(): ISignal<this, void>;
|
|
29
|
+
get stateChanged(): ISignal<this, IChangedArgs<any, any, string>>;
|
|
30
|
+
get dirty(): boolean;
|
|
31
|
+
set dirty(value: boolean);
|
|
32
|
+
get readOnly(): boolean;
|
|
33
|
+
set readOnly(value: boolean);
|
|
34
|
+
get disposed(): ISignal<LabChatModel, void>;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
toString(): string;
|
|
37
|
+
fromString(data: string): void;
|
|
38
|
+
toJSON(): PartialJSONObject;
|
|
39
|
+
fromJSON(data: PartialJSONObject): void;
|
|
40
|
+
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
41
|
+
updateMessage(id: string, updatedMessage: IChatMessage): Promise<boolean | void> | boolean | void;
|
|
42
|
+
deleteMessage(id: string): Promise<boolean | void> | boolean | void;
|
|
43
|
+
/**
|
|
44
|
+
* Function called by the input on key pressed.
|
|
45
|
+
*/
|
|
46
|
+
inputChanged(input?: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Triggered when an awareness state changes.
|
|
49
|
+
* Used to populate the writers list.
|
|
50
|
+
*/
|
|
51
|
+
onAwarenessChange: () => void;
|
|
52
|
+
private _resetWritingStatus;
|
|
53
|
+
private _onchange;
|
|
54
|
+
readonly defaultKernelName: string;
|
|
55
|
+
readonly defaultKernelLanguage: string;
|
|
56
|
+
private _sharedModel;
|
|
57
|
+
private _dirty;
|
|
58
|
+
private _readOnly;
|
|
59
|
+
private _disposed;
|
|
60
|
+
private _contentChanged;
|
|
61
|
+
private _stateChanged;
|
|
62
|
+
private _timeoutWriting;
|
|
63
|
+
private _user;
|
|
64
|
+
}
|
package/lib/model.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { ChatModel } from '@jupyter/chat';
|
|
6
|
+
import { UUID } from '@lumino/coreutils';
|
|
7
|
+
import { Signal } from '@lumino/signaling';
|
|
8
|
+
import { YChat } from './ychat';
|
|
9
|
+
const WRITING_DELAY = 1000;
|
|
10
|
+
/**
|
|
11
|
+
* The chat model.
|
|
12
|
+
*/
|
|
13
|
+
export class LabChatModel extends ChatModel {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
super(options);
|
|
16
|
+
this.collaborative = true;
|
|
17
|
+
/**
|
|
18
|
+
* Triggered when an awareness state changes.
|
|
19
|
+
* Used to populate the writers list.
|
|
20
|
+
*/
|
|
21
|
+
this.onAwarenessChange = () => {
|
|
22
|
+
const writers = [];
|
|
23
|
+
const states = this.sharedModel.awareness.getStates();
|
|
24
|
+
for (const stateID of states.keys()) {
|
|
25
|
+
const state = states.get(stateID);
|
|
26
|
+
if (!state || !state.user || state.user.username === this.user.username) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (state.isWriting) {
|
|
30
|
+
writers.push(state.user);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
this.updateWriters(writers);
|
|
34
|
+
};
|
|
35
|
+
this._onchange = (_, changes) => {
|
|
36
|
+
if (changes.messageChanges) {
|
|
37
|
+
const msgDelta = changes.messageChanges;
|
|
38
|
+
let index = 0;
|
|
39
|
+
msgDelta.forEach(delta => {
|
|
40
|
+
if (delta.retain) {
|
|
41
|
+
index += delta.retain;
|
|
42
|
+
}
|
|
43
|
+
else if (delta.insert) {
|
|
44
|
+
const messages = delta.insert.map(ymessage => {
|
|
45
|
+
const msg = {
|
|
46
|
+
...ymessage,
|
|
47
|
+
sender: this.sharedModel.getUser(ymessage.sender) || {
|
|
48
|
+
username: 'User undefined'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return msg;
|
|
52
|
+
});
|
|
53
|
+
this.messagesInserted(index, messages);
|
|
54
|
+
index += messages.length;
|
|
55
|
+
}
|
|
56
|
+
else if (delta.delete) {
|
|
57
|
+
this.messagesDeleted(index, delta.delete);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (changes.metadataChanges) {
|
|
62
|
+
changes.metadataChanges.forEach(change => {
|
|
63
|
+
// no need to search for update or add, if the new value contains ID, let's
|
|
64
|
+
// update the model ID.
|
|
65
|
+
if (change.key === 'id') {
|
|
66
|
+
this.id = change.newValue;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
this.defaultKernelName = '';
|
|
72
|
+
this.defaultKernelLanguage = '';
|
|
73
|
+
this._dirty = false;
|
|
74
|
+
this._readOnly = false;
|
|
75
|
+
this._disposed = new Signal(this);
|
|
76
|
+
this._contentChanged = new Signal(this);
|
|
77
|
+
this._stateChanged = new Signal(this);
|
|
78
|
+
this._timeoutWriting = null;
|
|
79
|
+
this._user = options.user || { username: 'user undefined' };
|
|
80
|
+
const { widgetConfig, sharedModel } = options;
|
|
81
|
+
if (sharedModel) {
|
|
82
|
+
this._sharedModel = sharedModel;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this._sharedModel = YChat.create();
|
|
86
|
+
}
|
|
87
|
+
this.id = this._sharedModel.id;
|
|
88
|
+
this.sharedModel.changed.connect(this._onchange, this);
|
|
89
|
+
this.config = widgetConfig.config;
|
|
90
|
+
widgetConfig.configChanged.connect((_, config) => {
|
|
91
|
+
this.config = config;
|
|
92
|
+
});
|
|
93
|
+
this.sharedModel.awareness.on('change', this.onAwarenessChange);
|
|
94
|
+
}
|
|
95
|
+
get user() {
|
|
96
|
+
return this._user;
|
|
97
|
+
}
|
|
98
|
+
get sharedModel() {
|
|
99
|
+
return this._sharedModel;
|
|
100
|
+
}
|
|
101
|
+
get contentChanged() {
|
|
102
|
+
return this._contentChanged;
|
|
103
|
+
}
|
|
104
|
+
get stateChanged() {
|
|
105
|
+
return this._stateChanged;
|
|
106
|
+
}
|
|
107
|
+
get dirty() {
|
|
108
|
+
return this._dirty;
|
|
109
|
+
}
|
|
110
|
+
set dirty(value) {
|
|
111
|
+
this._dirty = value;
|
|
112
|
+
}
|
|
113
|
+
get readOnly() {
|
|
114
|
+
return this._readOnly;
|
|
115
|
+
}
|
|
116
|
+
set readOnly(value) {
|
|
117
|
+
this._readOnly = value;
|
|
118
|
+
}
|
|
119
|
+
get disposed() {
|
|
120
|
+
return this._disposed;
|
|
121
|
+
}
|
|
122
|
+
dispose() {
|
|
123
|
+
if (this.isDisposed) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
super.dispose();
|
|
127
|
+
this._sharedModel.dispose();
|
|
128
|
+
this._disposed.emit();
|
|
129
|
+
Signal.clearData(this);
|
|
130
|
+
}
|
|
131
|
+
toString() {
|
|
132
|
+
return JSON.stringify({}, null, 2);
|
|
133
|
+
}
|
|
134
|
+
fromString(data) {
|
|
135
|
+
/** */
|
|
136
|
+
}
|
|
137
|
+
toJSON() {
|
|
138
|
+
return JSON.parse(this.toString());
|
|
139
|
+
}
|
|
140
|
+
fromJSON(data) {
|
|
141
|
+
// nothing to do
|
|
142
|
+
}
|
|
143
|
+
sendMessage(message) {
|
|
144
|
+
this._resetWritingStatus();
|
|
145
|
+
if (this._timeoutWriting !== null) {
|
|
146
|
+
window.clearTimeout(this._timeoutWriting);
|
|
147
|
+
}
|
|
148
|
+
const msg = {
|
|
149
|
+
type: 'msg',
|
|
150
|
+
id: UUID.uuid4(),
|
|
151
|
+
body: message.body,
|
|
152
|
+
time: Date.now() / 1000,
|
|
153
|
+
sender: this._user.username,
|
|
154
|
+
raw_time: true
|
|
155
|
+
};
|
|
156
|
+
// Add the user if it does not exist or has changed
|
|
157
|
+
if (!(this.sharedModel.getUser(this._user.username) === this._user)) {
|
|
158
|
+
this.sharedModel.setUser(this._user);
|
|
159
|
+
}
|
|
160
|
+
this.sharedModel.addMessage(msg);
|
|
161
|
+
}
|
|
162
|
+
updateMessage(id, updatedMessage) {
|
|
163
|
+
const index = this.sharedModel.getMessageIndex(id);
|
|
164
|
+
let message = this.sharedModel.getMessage(index);
|
|
165
|
+
if (message) {
|
|
166
|
+
message.body = updatedMessage.body;
|
|
167
|
+
message.edited = true;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
const sender = updatedMessage.sender.username;
|
|
171
|
+
message = {
|
|
172
|
+
type: 'msg',
|
|
173
|
+
id: id || UUID.uuid4(),
|
|
174
|
+
body: updatedMessage.body,
|
|
175
|
+
time: updatedMessage.time || Date.now() / 1000,
|
|
176
|
+
sender: sender,
|
|
177
|
+
edited: true
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
this.sharedModel.updateMessage(index, message);
|
|
181
|
+
}
|
|
182
|
+
deleteMessage(id) {
|
|
183
|
+
const index = this.sharedModel.getMessageIndex(id);
|
|
184
|
+
const message = this.sharedModel.getMessage(index);
|
|
185
|
+
if (!message) {
|
|
186
|
+
console.error('The message to delete does not exist');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
message.body = '';
|
|
190
|
+
message.deleted = true;
|
|
191
|
+
this.sharedModel.updateMessage(index, message);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Function called by the input on key pressed.
|
|
195
|
+
*/
|
|
196
|
+
inputChanged(input) {
|
|
197
|
+
if (!input || !this.config.sendTypingNotification) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const awareness = this.sharedModel.awareness;
|
|
201
|
+
if (this._timeoutWriting !== null) {
|
|
202
|
+
window.clearTimeout(this._timeoutWriting);
|
|
203
|
+
}
|
|
204
|
+
awareness.setLocalStateField('isWriting', true);
|
|
205
|
+
this._timeoutWriting = window.setTimeout(() => {
|
|
206
|
+
this._resetWritingStatus();
|
|
207
|
+
}, WRITING_DELAY);
|
|
208
|
+
}
|
|
209
|
+
_resetWritingStatus() {
|
|
210
|
+
const awareness = this.sharedModel.awareness;
|
|
211
|
+
const states = awareness.getLocalState();
|
|
212
|
+
states === null || states === void 0 ? true : delete states.isWriting;
|
|
213
|
+
awareness.setLocalState(states);
|
|
214
|
+
this._timeoutWriting = null;
|
|
215
|
+
}
|
|
216
|
+
}
|
package/lib/token.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { IConfig, IActiveCellManager, ISelectionWatcher } from '@jupyter/chat';
|
|
2
|
+
import { IWidgetTracker } from '@jupyterlab/apputils';
|
|
3
|
+
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
|
+
import { Token } from '@lumino/coreutils';
|
|
5
|
+
import { ISignal } from '@lumino/signaling';
|
|
6
|
+
import { ChatPanel, LabChatPanel } from './widget';
|
|
7
|
+
/**
|
|
8
|
+
* The file type for a chat document.
|
|
9
|
+
*/
|
|
10
|
+
export declare const chatFileType: DocumentRegistry.IFileType;
|
|
11
|
+
/**
|
|
12
|
+
* The token for the chat widget factory.
|
|
13
|
+
*/
|
|
14
|
+
export declare const IChatFactory: Token<IChatFactory>;
|
|
15
|
+
/**
|
|
16
|
+
* The chat configs.
|
|
17
|
+
*/
|
|
18
|
+
export interface ILabChatConfig extends IConfig {
|
|
19
|
+
/**
|
|
20
|
+
* The default directory where to create and look for chat.
|
|
21
|
+
*/
|
|
22
|
+
defaultDirectory?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The interface for the chat factory objects.
|
|
26
|
+
*/
|
|
27
|
+
export interface IChatFactory {
|
|
28
|
+
/**
|
|
29
|
+
* The chat widget config.
|
|
30
|
+
*/
|
|
31
|
+
widgetConfig: IWidgetConfig;
|
|
32
|
+
/**
|
|
33
|
+
* The chat panel tracker.
|
|
34
|
+
*/
|
|
35
|
+
tracker: IWidgetTracker<LabChatPanel>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The interface for the chats config.
|
|
39
|
+
*/
|
|
40
|
+
export interface IWidgetConfig {
|
|
41
|
+
/**
|
|
42
|
+
* The widget config
|
|
43
|
+
*/
|
|
44
|
+
config: Partial<ILabChatConfig>;
|
|
45
|
+
/**
|
|
46
|
+
* A signal emitting when the configuration for the chats has changed.
|
|
47
|
+
*/
|
|
48
|
+
configChanged: IConfigChanged;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A signal emitting when the configuration for the chats has changed.
|
|
52
|
+
*/
|
|
53
|
+
export interface IConfigChanged extends ISignal<IWidgetConfig, Partial<ILabChatConfig>> {
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Command ids.
|
|
57
|
+
*/
|
|
58
|
+
export declare const CommandIDs: {
|
|
59
|
+
/**
|
|
60
|
+
* Create a chat file.
|
|
61
|
+
*/
|
|
62
|
+
createChat: string;
|
|
63
|
+
/**
|
|
64
|
+
* Open a chat file.
|
|
65
|
+
*/
|
|
66
|
+
openChat: string;
|
|
67
|
+
/**
|
|
68
|
+
* Move a main widget to the side panel.
|
|
69
|
+
*/
|
|
70
|
+
moveToSide: string;
|
|
71
|
+
/**
|
|
72
|
+
* Mark as read.
|
|
73
|
+
*/
|
|
74
|
+
markAsRead: string;
|
|
75
|
+
/**
|
|
76
|
+
* Focus the input of the current chat.
|
|
77
|
+
*/
|
|
78
|
+
focusInput: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* The chat panel token.
|
|
82
|
+
*/
|
|
83
|
+
export declare const IChatPanel: Token<ChatPanel>;
|
|
84
|
+
/**
|
|
85
|
+
* The active cell manager plugin.
|
|
86
|
+
*/
|
|
87
|
+
export declare const IActiveCellManagerToken: Token<IActiveCellManager>;
|
|
88
|
+
/**
|
|
89
|
+
* The selection watcher plugin.
|
|
90
|
+
*/
|
|
91
|
+
export declare const ISelectionWatcherToken: Token<ISelectionWatcher>;
|