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/ychat.js ADDED
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { YDocument } from '@jupyter/ydoc';
6
+ import { JSONExt } from '@lumino/coreutils';
7
+ /**
8
+ * The jupyterlab chat shared document.
9
+ */
10
+ export class YChat extends YDocument {
11
+ /**
12
+ * Create a new jupyterlab chat model.
13
+ */
14
+ constructor(options) {
15
+ super(options);
16
+ /**
17
+ * Document version
18
+ */
19
+ this.version = '1.0.0';
20
+ this._usersObserver = (event) => {
21
+ const userChange = new Array();
22
+ event.keysChanged.forEach(key => {
23
+ const change = event.changes.keys.get(key);
24
+ if (change) {
25
+ switch (change.action) {
26
+ case 'add':
27
+ userChange.push({
28
+ key,
29
+ newValue: this._users.get(key),
30
+ type: 'add'
31
+ });
32
+ break;
33
+ case 'delete':
34
+ userChange.push({
35
+ key,
36
+ oldValue: change.oldValue,
37
+ type: 'remove'
38
+ });
39
+ break;
40
+ case 'update':
41
+ userChange.push({
42
+ key: key,
43
+ oldValue: change.oldValue,
44
+ newValue: this._users.get(key),
45
+ type: 'change'
46
+ });
47
+ break;
48
+ }
49
+ }
50
+ });
51
+ this._changed.emit({ userChange: userChange });
52
+ };
53
+ this._messagesObserver = (event) => {
54
+ const messageChanges = event.delta;
55
+ this._changed.emit({
56
+ messageChanges: messageChanges
57
+ });
58
+ };
59
+ this._metadataObserver = (event) => {
60
+ const metadataChange = new Array();
61
+ event.changes.keys.forEach((change, key) => {
62
+ switch (change.action) {
63
+ case 'add':
64
+ metadataChange.push({
65
+ key,
66
+ newValue: this._metadata.get(key),
67
+ type: 'add'
68
+ });
69
+ break;
70
+ case 'delete':
71
+ metadataChange.push({
72
+ key,
73
+ oldValue: change.oldValue,
74
+ type: 'remove'
75
+ });
76
+ break;
77
+ case 'update':
78
+ metadataChange.push({
79
+ key: key,
80
+ oldValue: change.oldValue,
81
+ newValue: this._metadata.get(key),
82
+ type: 'change'
83
+ });
84
+ break;
85
+ }
86
+ });
87
+ this._changed.emit({
88
+ metadataChanges: metadataChange
89
+ });
90
+ };
91
+ this._users = this.ydoc.getMap('users');
92
+ this._users.observe(this._usersObserver);
93
+ this._messages = this.ydoc.getArray('messages');
94
+ this._messages.observe(this._messagesObserver);
95
+ this._metadata = this.ydoc.getMap('metadata');
96
+ this._metadata.observe(this._metadataObserver);
97
+ }
98
+ /**
99
+ * Static method to create instances on the sharedModel
100
+ *
101
+ * @returns The sharedModel instance
102
+ */
103
+ static create(options) {
104
+ return new YChat(options);
105
+ }
106
+ get id() {
107
+ return this._metadata.get('id') || '';
108
+ }
109
+ get users() {
110
+ return JSONExt.deepCopy(this._users.toJSON());
111
+ }
112
+ get messages() {
113
+ return JSONExt.deepCopy(this._messages.toJSON());
114
+ }
115
+ getUser(username) {
116
+ if (!username) {
117
+ return undefined;
118
+ }
119
+ return this._users.get(username);
120
+ }
121
+ setUser(value) {
122
+ this.transact(() => {
123
+ this._users.set(value.username, value);
124
+ });
125
+ }
126
+ getMessage(index) {
127
+ return this._messages.get(index);
128
+ }
129
+ addMessage(value) {
130
+ this.transact(() => {
131
+ this._messages.push([value]);
132
+ });
133
+ }
134
+ updateMessage(index, value) {
135
+ this.transact(() => {
136
+ this._messages.delete(index);
137
+ this._messages.insert(index, [value]);
138
+ });
139
+ }
140
+ getMessageIndex(id) {
141
+ return this._messages.toArray().findIndex(msg => msg.id === id);
142
+ }
143
+ deleteMessage(index) {
144
+ this.transact(() => {
145
+ this._messages.delete(index);
146
+ });
147
+ }
148
+ }
package/package.json ADDED
@@ -0,0 +1,205 @@
1
+ {
2
+ "name": "jupyterlab-chat",
3
+ "version": "0.6.0",
4
+ "description": "The library to build a chat based on shared document",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension"
9
+ ],
10
+ "homepage": "https://github.com/jupyterlab/jupyter-chat",
11
+ "bugs": {
12
+ "url": "https://github.com/jupyterlab/jupyter-chat/issues"
13
+ },
14
+ "license": "BSD-3-Clause",
15
+ "author": {
16
+ "name": "Jupyter Development Team",
17
+ "email": "jupyter@googlegroups.com"
18
+ },
19
+ "files": [
20
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
21
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
22
+ "src/**/*.{ts,tsx}"
23
+ ],
24
+ "main": "lib/index.js",
25
+ "types": "lib/index.d.ts",
26
+ "style": "style/index.css",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/jupyterlab/jupyter-chat.git"
30
+ },
31
+ "scripts": {
32
+ "build": "jlpm build:lib",
33
+ "build:prod": "jlpm clean && jlpm build:lib:prod",
34
+ "build:lib": "tsc --sourceMap",
35
+ "build:lib:prod": "tsc",
36
+ "clean": "jlpm clean:lib",
37
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
38
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
39
+ "clean:all": "jlpm clean:lib && jlpm clean:lintcache",
40
+ "eslint": "jlpm eslint:check --fix",
41
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
42
+ "install:extension": "jlpm build",
43
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
44
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
45
+ "prettier": "jlpm prettier:base --write --list-different",
46
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
47
+ "prettier:check": "jlpm prettier:base --check",
48
+ "stylelint": "jlpm stylelint:check --fix",
49
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
50
+ "test": "jest --coverage",
51
+ "watch:src": "tsc -w --sourceMap"
52
+ },
53
+ "dependencies": {
54
+ "@jupyter/chat": "^0.6.0",
55
+ "@jupyter/docprovider": "^2.1.4",
56
+ "@jupyter/ydoc": "^1.1.1",
57
+ "@jupyterlab/application": "^4.2.0",
58
+ "@jupyterlab/apputils": "^4.3.0",
59
+ "@jupyterlab/coreutils": "^6.2.0",
60
+ "@jupyterlab/docregistry": "^4.2.0",
61
+ "@jupyterlab/launcher": "^4.2.0",
62
+ "@jupyterlab/notebook": "^4.2.0",
63
+ "@jupyterlab/rendermime": "^4.2.0",
64
+ "@jupyterlab/services": "^7.2.0",
65
+ "@jupyterlab/settingregistry": "^4.2.0",
66
+ "@jupyterlab/translation": "^4.2.0",
67
+ "@jupyterlab/ui-components": "^4.2.0",
68
+ "@lumino/commands": "^2.0.0",
69
+ "@lumino/coreutils": "^2.0.0",
70
+ "@lumino/signaling": "^2.0.0",
71
+ "@lumino/widgets": "^2.0.0",
72
+ "react": "^18.2.0",
73
+ "y-protocols": "^1.0.5",
74
+ "yjs": "^13.5.40"
75
+ },
76
+ "devDependencies": {
77
+ "@jupyterlab/testing": "^4.2.0",
78
+ "@types/jest": "^29.2.0",
79
+ "@types/json-schema": "^7.0.11",
80
+ "@types/react": "^18.2.0",
81
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
82
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
83
+ "@typescript-eslint/parser": "^6.1.0",
84
+ "css-loader": "^6.7.1",
85
+ "eslint": "^8.36.0",
86
+ "eslint-config-prettier": "^8.8.0",
87
+ "eslint-plugin-prettier": "^5.0.0",
88
+ "jest": "^29.2.0",
89
+ "mkdirp": "^1.0.3",
90
+ "npm-run-all": "^4.1.5",
91
+ "prettier": "^3.0.0",
92
+ "rimraf": "^5.0.1",
93
+ "source-map-loader": "^1.0.2",
94
+ "style-loader": "^3.3.1",
95
+ "stylelint": "^15.10.1",
96
+ "stylelint-config-recommended": "^13.0.0",
97
+ "stylelint-config-standard": "^34.0.0",
98
+ "stylelint-csstree-validator": "^3.0.0",
99
+ "stylelint-prettier": "^4.0.0",
100
+ "typescript": "~5.0.2",
101
+ "yjs": "^13.5.0"
102
+ },
103
+ "sideEffects": [
104
+ "style/*.css",
105
+ "style/index.js"
106
+ ],
107
+ "styleModule": "style/index.js",
108
+ "publishConfig": {
109
+ "access": "public"
110
+ },
111
+ "eslintIgnore": [
112
+ "node_modules",
113
+ "dist",
114
+ "coverage",
115
+ "**/*.d.ts",
116
+ "tests",
117
+ "**/__tests__",
118
+ "ui-tests"
119
+ ],
120
+ "eslintConfig": {
121
+ "extends": [
122
+ "eslint:recommended",
123
+ "plugin:@typescript-eslint/eslint-recommended",
124
+ "plugin:@typescript-eslint/recommended",
125
+ "plugin:prettier/recommended"
126
+ ],
127
+ "parser": "@typescript-eslint/parser",
128
+ "parserOptions": {
129
+ "project": "tsconfig.json",
130
+ "sourceType": "module"
131
+ },
132
+ "plugins": [
133
+ "@typescript-eslint"
134
+ ],
135
+ "rules": {
136
+ "@typescript-eslint/naming-convention": [
137
+ "error",
138
+ {
139
+ "selector": "interface",
140
+ "format": [
141
+ "PascalCase"
142
+ ],
143
+ "custom": {
144
+ "regex": "^I[A-Z]",
145
+ "match": true
146
+ }
147
+ }
148
+ ],
149
+ "@typescript-eslint/no-unused-vars": [
150
+ "warn",
151
+ {
152
+ "args": "none"
153
+ }
154
+ ],
155
+ "@typescript-eslint/no-explicit-any": "off",
156
+ "@typescript-eslint/no-namespace": "off",
157
+ "@typescript-eslint/no-use-before-define": "off",
158
+ "@typescript-eslint/quotes": [
159
+ "error",
160
+ "single",
161
+ {
162
+ "avoidEscape": true,
163
+ "allowTemplateLiterals": false
164
+ }
165
+ ],
166
+ "curly": [
167
+ "error",
168
+ "all"
169
+ ],
170
+ "eqeqeq": "error",
171
+ "prefer-arrow-callback": "error"
172
+ }
173
+ },
174
+ "prettier": {
175
+ "singleQuote": true,
176
+ "trailingComma": "none",
177
+ "arrowParens": "avoid",
178
+ "endOfLine": "auto",
179
+ "overrides": [
180
+ {
181
+ "files": "package.json",
182
+ "options": {
183
+ "tabWidth": 2
184
+ }
185
+ }
186
+ ]
187
+ },
188
+ "stylelint": {
189
+ "extends": [
190
+ "stylelint-config-recommended",
191
+ "stylelint-config-standard",
192
+ "stylelint-prettier/recommended"
193
+ ],
194
+ "plugins": [
195
+ "stylelint-csstree-validator"
196
+ ],
197
+ "rules": {
198
+ "csstree/validator": true,
199
+ "property-no-vendor-prefix": null,
200
+ "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
201
+ "selector-no-vendor-prefix": null,
202
+ "value-no-vendor-prefix": null
203
+ }
204
+ }
205
+ }
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ /**
7
+ * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
8
+ */
9
+
10
+ describe('jupyterlab-chat', () => {
11
+ it('should be tested', () => {
12
+ expect(1 + 1).toEqual(2);
13
+ });
14
+ });
package/src/factory.ts ADDED
@@ -0,0 +1,207 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ import {
7
+ ChatWidget,
8
+ IActiveCellManager,
9
+ IAutocompletionRegistry,
10
+ ISelectionWatcher
11
+ } from '@jupyter/chat';
12
+ import { IThemeManager } from '@jupyterlab/apputils';
13
+ import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
14
+ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
15
+ import { Contents, User } from '@jupyterlab/services';
16
+ import { CommandRegistry } from '@lumino/commands';
17
+ import { ISignal, Signal } from '@lumino/signaling';
18
+
19
+ import { LabChatModel } from './model';
20
+ import { LabChatPanel } from './widget';
21
+ import { YChat } from './ychat';
22
+ import { ILabChatConfig, IWidgetConfig } from './token';
23
+
24
+ /**
25
+ * The object provided by the chatDocument extension.
26
+ * It is used to set the current config (from settings) to newly created chat widget,
27
+ * and to propagate every changes to the existing chat widgets.
28
+ */
29
+ export class WidgetConfig implements IWidgetConfig {
30
+ /**
31
+ * The constructor of the WidgetConfig.
32
+ */
33
+ constructor(config: Partial<ILabChatConfig>) {
34
+ this._config = config;
35
+ }
36
+
37
+ /**
38
+ * Getter and setter for the config.
39
+ */
40
+ get config(): Partial<ILabChatConfig> {
41
+ return this._config;
42
+ }
43
+ set config(value: Partial<ILabChatConfig>) {
44
+ this._config = { ...this._config, ...value };
45
+ this._configChanged.emit(value);
46
+ }
47
+
48
+ /**
49
+ * Getter for the configChanged signal
50
+ */
51
+ get configChanged(): ISignal<WidgetConfig, Partial<ILabChatConfig>> {
52
+ return this._configChanged;
53
+ }
54
+
55
+ private _config: Partial<ILabChatConfig>;
56
+ private _configChanged = new Signal<WidgetConfig, Partial<ILabChatConfig>>(
57
+ this
58
+ );
59
+ }
60
+
61
+ /**
62
+ * A widget factory to create new instances of LabChatWidget.
63
+ */
64
+ export class ChatWidgetFactory extends ABCWidgetFactory<
65
+ LabChatPanel,
66
+ LabChatModel
67
+ > {
68
+ /**
69
+ * Constructor of ChatWidgetFactory.
70
+ *
71
+ * @param options Constructor options
72
+ */
73
+ constructor(options: ChatWidgetFactory.IOptions<LabChatPanel>) {
74
+ super(options);
75
+ this._themeManager = options.themeManager;
76
+ this._rmRegistry = options.rmRegistry;
77
+ this._autocompletionRegistry = options.autocompletionRegistry;
78
+ }
79
+
80
+ /**
81
+ * Create a new widget given a context.
82
+ *
83
+ * @param context Contains the information of the file
84
+ * @returns The widget
85
+ */
86
+ protected createNewWidget(context: ChatWidgetFactory.IContext): LabChatPanel {
87
+ context.rmRegistry = this._rmRegistry;
88
+ context.themeManager = this._themeManager;
89
+ context.autocompletionRegistry = this._autocompletionRegistry;
90
+ return new LabChatPanel({
91
+ context,
92
+ content: new ChatWidget(context)
93
+ });
94
+ }
95
+
96
+ private _themeManager: IThemeManager | null;
97
+ private _rmRegistry: IRenderMimeRegistry;
98
+ private _autocompletionRegistry?: IAutocompletionRegistry;
99
+ }
100
+
101
+ export namespace ChatWidgetFactory {
102
+ export interface IContext extends DocumentRegistry.IContext<LabChatModel> {
103
+ themeManager: IThemeManager | null;
104
+ rmRegistry: IRenderMimeRegistry;
105
+ autocompletionRegistry?: IAutocompletionRegistry;
106
+ }
107
+
108
+ export interface IOptions<T extends LabChatPanel>
109
+ extends DocumentRegistry.IWidgetFactoryOptions<T> {
110
+ themeManager: IThemeManager | null;
111
+ rmRegistry: IRenderMimeRegistry;
112
+ autocompletionRegistry?: IAutocompletionRegistry;
113
+ }
114
+ }
115
+
116
+ export class LabChatModelFactory
117
+ implements DocumentRegistry.IModelFactory<LabChatModel>
118
+ {
119
+ constructor(options: LabChatModel.IOptions) {
120
+ this._user = options.user;
121
+ this._widgetConfig = options.widgetConfig;
122
+ this._commands = options.commands;
123
+ this._activeCellManager = options.activeCellManager ?? null;
124
+ this._selectionWatcher = options.selectionWatcher ?? null;
125
+ }
126
+
127
+ collaborative = true;
128
+ /**
129
+ * The name of the model.
130
+ *
131
+ * @returns The name
132
+ */
133
+ get name(): string {
134
+ return 'chat';
135
+ }
136
+
137
+ /**
138
+ * The content type of the file.
139
+ *
140
+ * @returns The content type
141
+ */
142
+ get contentType(): Contents.ContentType {
143
+ return 'chat';
144
+ }
145
+
146
+ /**
147
+ * The format of the file.
148
+ *
149
+ * @returns the file format
150
+ */
151
+ get fileFormat(): Contents.FileFormat {
152
+ return 'text';
153
+ }
154
+
155
+ /**
156
+ * Get whether the model factory has been disposed.
157
+ *
158
+ * @returns disposed status
159
+ */
160
+
161
+ get isDisposed(): boolean {
162
+ return this._disposed;
163
+ }
164
+
165
+ /**
166
+ * Dispose the model factory.
167
+ */
168
+ dispose(): void {
169
+ this._disposed = true;
170
+ }
171
+
172
+ /**
173
+ * Get the preferred language given the path on the file.
174
+ *
175
+ * @param path path of the file represented by this document model
176
+ * @returns The preferred language
177
+ */
178
+ preferredLanguage(path: string): string {
179
+ return '';
180
+ }
181
+
182
+ /**
183
+ * Create a new instance of LabChatModel.
184
+ *
185
+ * @param languagePreference Language
186
+ * @param modelDB Model database
187
+ * @returns The model
188
+ */
189
+
190
+ createNew(options: DocumentRegistry.IModelOptions<YChat>): LabChatModel {
191
+ return new LabChatModel({
192
+ ...options,
193
+ user: this._user,
194
+ widgetConfig: this._widgetConfig,
195
+ commands: this._commands,
196
+ activeCellManager: this._activeCellManager,
197
+ selectionWatcher: this._selectionWatcher
198
+ });
199
+ }
200
+
201
+ private _disposed = false;
202
+ private _user: User.IIdentity | null;
203
+ private _widgetConfig: IWidgetConfig;
204
+ private _commands?: CommandRegistry;
205
+ private _activeCellManager: IActiveCellManager | null;
206
+ private _selectionWatcher: ISelectionWatcher | null;
207
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ export * from './factory';
7
+ export * from './model';
8
+ export * from './token';
9
+ export * from './widget';
10
+ export * from './ychat';