@jupyter/chat 0.20.0-alpha.3 → 0.21.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__/model.spec.js +49 -0
- package/lib/components/attachments.js +11 -16
- package/lib/components/chat.d.ts +5 -0
- package/lib/components/code-blocks/code-toolbar.js +12 -8
- package/lib/components/code-blocks/copy-button.js +9 -7
- package/lib/components/input/buttons/attach-button.js +4 -2
- package/lib/components/input/buttons/cancel-button.js +3 -1
- package/lib/components/input/buttons/save-edit-button.js +3 -1
- package/lib/components/input/buttons/send-button.js +4 -2
- package/lib/components/input/buttons/stop-button.js +3 -1
- package/lib/components/input/chat-input.js +3 -2
- package/lib/components/messages/header.js +7 -3
- package/lib/components/messages/message-renderer.js +17 -17
- package/lib/components/messages/navigation.js +5 -4
- package/lib/components/messages/toolbar.js +4 -2
- package/lib/components/writing-indicator.js +11 -6
- package/lib/context.d.ts +7 -0
- package/lib/context.js +12 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/message.d.ts +3 -3
- package/lib/message.js +3 -0
- package/lib/model.d.ts +16 -0
- package/lib/model.js +28 -8
- package/lib/theme-provider.js +0 -4
- package/lib/tokens.d.ts +8 -1
- package/lib/types.d.ts +50 -1
- package/lib/widgets/chat-error.d.ts +2 -1
- package/lib/widgets/chat-error.js +6 -3
- package/lib/widgets/chat-selector-popup.d.ts +6 -0
- package/lib/widgets/chat-selector-popup.js +8 -5
- package/lib/widgets/chat-sidebar.js +5 -1
- package/lib/widgets/chat-widget.js +6 -1
- package/lib/widgets/multichat-panel.d.ts +6 -0
- package/lib/widgets/multichat-panel.js +21 -13
- package/package.json +2 -1
- package/src/__tests__/model.spec.ts +58 -0
- package/src/components/attachments.tsx +18 -25
- package/src/components/chat.tsx +5 -0
- package/src/components/code-blocks/code-toolbar.tsx +14 -7
- package/src/components/code-blocks/copy-button.tsx +12 -8
- package/src/components/input/buttons/attach-button.tsx +4 -2
- package/src/components/input/buttons/cancel-button.tsx +4 -1
- package/src/components/input/buttons/save-edit-button.tsx +3 -1
- package/src/components/input/buttons/send-button.tsx +4 -2
- package/src/components/input/buttons/stop-button.tsx +3 -1
- package/src/components/input/chat-input.tsx +3 -2
- package/src/components/messages/header.tsx +9 -3
- package/src/components/messages/message-renderer.tsx +17 -17
- package/src/components/messages/navigation.tsx +5 -4
- package/src/components/messages/toolbar.tsx +6 -4
- package/src/components/writing-indicator.tsx +17 -6
- package/src/context.ts +13 -0
- package/src/index.ts +1 -0
- package/src/message.ts +6 -5
- package/src/model.ts +52 -4
- package/src/theme-provider.ts +0 -4
- package/src/tokens.ts +9 -3
- package/src/types.ts +52 -4
- package/src/widgets/chat-error.tsx +9 -5
- package/src/widgets/chat-selector-popup.tsx +21 -3
- package/src/widgets/chat-sidebar.tsx +5 -1
- package/src/widgets/chat-widget.tsx +7 -1
- package/src/widgets/multichat-panel.tsx +32 -12
package/src/message.ts
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IRenderMime } from '@jupyterlab/rendermime';
|
|
7
6
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
8
7
|
import {
|
|
9
8
|
IAttachment,
|
|
10
9
|
IMessageContent,
|
|
11
10
|
IMessage,
|
|
12
11
|
IMessageMetadata,
|
|
13
|
-
IUser
|
|
12
|
+
IUser,
|
|
13
|
+
IMimeModelBody
|
|
14
14
|
} from './types';
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -39,9 +39,7 @@ export class Message implements IMessage {
|
|
|
39
39
|
get type(): string {
|
|
40
40
|
return this._content.type;
|
|
41
41
|
}
|
|
42
|
-
get body():
|
|
43
|
-
| string
|
|
44
|
-
| (Partial<IRenderMime.IMimeModel> & Pick<IRenderMime.IMimeModel, 'data'>) {
|
|
42
|
+
get body(): string {
|
|
45
43
|
return this._content.body;
|
|
46
44
|
}
|
|
47
45
|
get id(): string {
|
|
@@ -74,6 +72,9 @@ export class Message implements IMessage {
|
|
|
74
72
|
get metadata(): IMessageMetadata | undefined {
|
|
75
73
|
return this._content.metadata;
|
|
76
74
|
}
|
|
75
|
+
get mime_model(): IMimeModelBody | undefined {
|
|
76
|
+
return this._content.mime_model;
|
|
77
|
+
}
|
|
77
78
|
|
|
78
79
|
/**
|
|
79
80
|
* A signal emitting when the message has been updated.
|
package/src/model.ts
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
7
|
+
import {
|
|
8
|
+
ITranslator,
|
|
9
|
+
nullTranslator,
|
|
10
|
+
TranslationBundle
|
|
11
|
+
} from '@jupyterlab/translation';
|
|
7
12
|
import { ArrayExt } from '@lumino/algorithm';
|
|
8
13
|
import { CommandRegistry } from '@lumino/commands';
|
|
9
14
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
@@ -11,6 +16,7 @@ import { IDisposable } from '@lumino/disposable';
|
|
|
11
16
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
12
17
|
|
|
13
18
|
import { IActiveCellManager } from './active-cell-manager';
|
|
19
|
+
import { TRANSLATION_DOMAIN } from './context';
|
|
14
20
|
import { IInputModel, InputModel } from './input-model';
|
|
15
21
|
import { Message } from './message';
|
|
16
22
|
import { ISelectionWatcher } from './selection-watcher';
|
|
@@ -112,6 +118,11 @@ export interface IChatModel extends IDisposable {
|
|
|
112
118
|
*/
|
|
113
119
|
readonly writersChanged?: ISignal<IChatModel, IChatModel.IWriter[]>;
|
|
114
120
|
|
|
121
|
+
/**
|
|
122
|
+
* A signal emitting when a message has been updated.
|
|
123
|
+
*/
|
|
124
|
+
readonly messageChanged: ISignal<IChatModel, IMessage>;
|
|
125
|
+
|
|
115
126
|
/**
|
|
116
127
|
* A signal emitting when the message edition input changed change.
|
|
117
128
|
*/
|
|
@@ -237,6 +248,10 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
237
248
|
...config
|
|
238
249
|
};
|
|
239
250
|
|
|
251
|
+
this._trans = (options.translator ?? nullTranslator).load(
|
|
252
|
+
TRANSLATION_DOMAIN
|
|
253
|
+
);
|
|
254
|
+
|
|
240
255
|
this._inputModel = new InputModel({
|
|
241
256
|
activeCellManager: options.activeCellManager,
|
|
242
257
|
selectionWatcher: options.selectionWatcher,
|
|
@@ -493,6 +508,13 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
493
508
|
return this._writersChanged;
|
|
494
509
|
}
|
|
495
510
|
|
|
511
|
+
/**
|
|
512
|
+
* A signal emitting when a message has been updated.
|
|
513
|
+
*/
|
|
514
|
+
get messageChanged(): ISignal<IChatModel, IMessage> {
|
|
515
|
+
return this._messageChanged;
|
|
516
|
+
}
|
|
517
|
+
|
|
496
518
|
/**
|
|
497
519
|
* A signal emitting when the message edition input changed change.
|
|
498
520
|
*/
|
|
@@ -528,6 +550,7 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
528
550
|
}
|
|
529
551
|
this._isDisposed = true;
|
|
530
552
|
this._disposed.emit();
|
|
553
|
+
Signal.clearData(this);
|
|
531
554
|
}
|
|
532
555
|
|
|
533
556
|
/**
|
|
@@ -580,7 +603,9 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
580
603
|
// Format the messages.
|
|
581
604
|
messages.forEach((message, idx) => {
|
|
582
605
|
const formattedMessage = this.formatChatMessage(message);
|
|
583
|
-
|
|
606
|
+
const msg = new Message(formattedMessage);
|
|
607
|
+
msg.changed.connect(this._onMessageChanged, this);
|
|
608
|
+
formattedMessages.push(msg);
|
|
584
609
|
if (message.time > this.lastRead) {
|
|
585
610
|
unreadIndexes.push(index + idx);
|
|
586
611
|
}
|
|
@@ -613,7 +638,9 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
613
638
|
* @param count - the number of messages to delete.
|
|
614
639
|
*/
|
|
615
640
|
messagesDeleted(index: number, count: number): void {
|
|
616
|
-
this._messages.splice(index, count)
|
|
641
|
+
this._messages.splice(index, count).forEach(msg => {
|
|
642
|
+
msg.changed.disconnect(this._onMessageChanged, this);
|
|
643
|
+
});
|
|
617
644
|
this._messagesUpdated.emit();
|
|
618
645
|
}
|
|
619
646
|
|
|
@@ -695,14 +722,24 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
695
722
|
this._commands
|
|
696
723
|
.execute('apputils:update-notification', {
|
|
697
724
|
id: this._notificationId,
|
|
698
|
-
message:
|
|
725
|
+
message: this._name
|
|
726
|
+
? this._trans.__(
|
|
727
|
+
'%1 incoming message(s) in %2',
|
|
728
|
+
unreadCount,
|
|
729
|
+
this._name
|
|
730
|
+
)
|
|
731
|
+
: this._trans.__('%1 incoming message(s)', unreadCount)
|
|
699
732
|
})
|
|
700
733
|
.then(success => {
|
|
701
734
|
// Create a new notification only if messages are added.
|
|
702
735
|
if (!success && canCreate) {
|
|
703
736
|
this._commands!.execute('apputils:notify', {
|
|
704
737
|
type: 'info',
|
|
705
|
-
message:
|
|
738
|
+
message: this._trans.__(
|
|
739
|
+
'%1 incoming message(s) in %2',
|
|
740
|
+
unreadCount,
|
|
741
|
+
this._name
|
|
742
|
+
)
|
|
706
743
|
}).then(id => {
|
|
707
744
|
this._notificationId = id;
|
|
708
745
|
});
|
|
@@ -718,6 +755,10 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
718
755
|
}
|
|
719
756
|
}
|
|
720
757
|
|
|
758
|
+
private _onMessageChanged(msg: IMessage): void {
|
|
759
|
+
this._messageChanged.emit(msg);
|
|
760
|
+
}
|
|
761
|
+
|
|
721
762
|
private _messages: IMessage[] = [];
|
|
722
763
|
private _unreadMessages: number[] = [];
|
|
723
764
|
private _lastRead: number = 0;
|
|
@@ -725,6 +766,7 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
725
766
|
private _id: string | undefined;
|
|
726
767
|
private _name: string = '';
|
|
727
768
|
private _config: IConfig;
|
|
769
|
+
protected _trans: TranslationBundle;
|
|
728
770
|
private _readyDelegate = new PromiseDelegate<void>();
|
|
729
771
|
private _inputModel: IInputModel;
|
|
730
772
|
private _disposed = new Signal<this, void>(this);
|
|
@@ -737,6 +779,7 @@ export abstract class AbstractChatModel implements IChatModel {
|
|
|
737
779
|
private _writers: IChatModel.IWriter[] = [];
|
|
738
780
|
private _messageEditions = new Map<string, IInputModel>();
|
|
739
781
|
private _messagesUpdated = new Signal<IChatModel, void>(this);
|
|
782
|
+
private _messageChanged = new Signal<IChatModel, IMessage>(this);
|
|
740
783
|
private _configChanged = new Signal<IChatModel, IConfig>(this);
|
|
741
784
|
private _unreadChanged = new Signal<IChatModel, number[]>(this);
|
|
742
785
|
private _viewportChanged = new Signal<IChatModel, number[]>(this);
|
|
@@ -770,6 +813,11 @@ export namespace IChatModel {
|
|
|
770
813
|
*/
|
|
771
814
|
commands?: CommandRegistry;
|
|
772
815
|
|
|
816
|
+
/**
|
|
817
|
+
* The translator for internationalization.
|
|
818
|
+
*/
|
|
819
|
+
translator?: ITranslator;
|
|
820
|
+
|
|
773
821
|
/**
|
|
774
822
|
* Active cell manager.
|
|
775
823
|
*/
|
package/src/theme-provider.ts
CHANGED
|
@@ -184,10 +184,6 @@ export async function getJupyterLabTheme(): Promise<Theme> {
|
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
186
|
palette: {
|
|
187
|
-
background: {
|
|
188
|
-
paper: getCSSVariable('--jp-layout-color1'),
|
|
189
|
-
default: getCSSVariable('--jp-layout-color1')
|
|
190
|
-
},
|
|
191
187
|
mode: light ? 'light' : 'dark',
|
|
192
188
|
primary: {
|
|
193
189
|
main: getCSSVariable(`--jp-brand-color${light ? '1' : '2'}`),
|
package/src/tokens.ts
CHANGED
|
@@ -7,13 +7,19 @@ import { IWidgetTracker, MainAreaWidget } from '@jupyterlab/apputils';
|
|
|
7
7
|
import { Token } from '@lumino/coreutils';
|
|
8
8
|
|
|
9
9
|
import { ChatWidget } from './widgets';
|
|
10
|
+
import { IChatModel } from './model';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The main area chat widget type.
|
|
14
|
+
*/
|
|
15
|
+
export type MainAreaChat = MainAreaWidget<ChatWidget> & {
|
|
16
|
+
model: IChatModel;
|
|
17
|
+
};
|
|
10
18
|
|
|
11
19
|
/**
|
|
12
20
|
* the chat tracker type.
|
|
13
21
|
*/
|
|
14
|
-
export type IChatTracker = IWidgetTracker<
|
|
15
|
-
ChatWidget | MainAreaWidget<ChatWidget>
|
|
16
|
-
>;
|
|
22
|
+
export type IChatTracker = IWidgetTracker<ChatWidget | MainAreaChat>;
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* A chat tracker token.
|
package/src/types.ts
CHANGED
|
@@ -61,6 +61,12 @@ export interface IConfig {
|
|
|
61
61
|
showDeleted?: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Mime model body type, a partial mime bundle model containing at least the data.
|
|
66
|
+
*/
|
|
67
|
+
export type IMimeModelBody = Partial<IRenderMime.IMimeModel> &
|
|
68
|
+
Pick<IRenderMime.IMimeModel, 'data'>;
|
|
69
|
+
|
|
64
70
|
/**
|
|
65
71
|
* An empty interface to describe optional metadata attached to a chat message.
|
|
66
72
|
* Extensions can augment this interface to add custom fields:
|
|
@@ -79,21 +85,59 @@ export interface IMessageMetadata {} /* eslint-disable-line @typescript-eslint/n
|
|
|
79
85
|
* The chat message description.
|
|
80
86
|
*/
|
|
81
87
|
export type IMessageContent<T = IUser, U = IAttachment> = {
|
|
88
|
+
/**
|
|
89
|
+
* The type of the message, usually 'msg' for a regular message.
|
|
90
|
+
*/
|
|
82
91
|
type: string;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
/**
|
|
93
|
+
* The body of the message, markdown formatted.
|
|
94
|
+
*/
|
|
95
|
+
body: string;
|
|
96
|
+
/**
|
|
97
|
+
* The message id (should be unique).
|
|
98
|
+
*/
|
|
87
99
|
id: string;
|
|
100
|
+
/**
|
|
101
|
+
* The message timestamp (seconds since epoch).
|
|
102
|
+
*/
|
|
88
103
|
time: number;
|
|
104
|
+
/**
|
|
105
|
+
* The sender of the message, default to IUser type.
|
|
106
|
+
*/
|
|
89
107
|
sender: T;
|
|
108
|
+
/**
|
|
109
|
+
* The attachment list, default to IAttachment type.
|
|
110
|
+
*/
|
|
90
111
|
attachments?: U[];
|
|
112
|
+
/**
|
|
113
|
+
* Optional, list of users mentioned in the message.
|
|
114
|
+
*/
|
|
91
115
|
mentions?: T[];
|
|
116
|
+
/**
|
|
117
|
+
* Optional, whether the message time has been verified.
|
|
118
|
+
*/
|
|
92
119
|
raw_time?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Optional, whether the message has been deleted.
|
|
122
|
+
*/
|
|
93
123
|
deleted?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Optional, whether the message has been edited.
|
|
126
|
+
*/
|
|
94
127
|
edited?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Optional, whether the message should be stacked to the previous one.
|
|
130
|
+
*/
|
|
95
131
|
stacked?: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Optional, the metadata of the message.
|
|
134
|
+
*/
|
|
96
135
|
metadata?: IMessageMetadata;
|
|
136
|
+
/**
|
|
137
|
+
* Optional, a mime bundle.
|
|
138
|
+
* If provided, the body won't be displayed in favor of the mime bundle.
|
|
139
|
+
*/
|
|
140
|
+
mime_model?: IMimeModelBody;
|
|
97
141
|
};
|
|
98
142
|
|
|
99
143
|
export interface IMessage extends IMessageContent {
|
|
@@ -187,6 +231,10 @@ export interface INotebookAttachment {
|
|
|
187
231
|
* The local path of the notebook, relative to `ContentsManager.root_dir`.
|
|
188
232
|
*/
|
|
189
233
|
value: string;
|
|
234
|
+
/**
|
|
235
|
+
* (optional) The MIME type of the attachment.
|
|
236
|
+
*/
|
|
237
|
+
mimetype?: string;
|
|
190
238
|
/**
|
|
191
239
|
* (optional) A list of cells in the notebook.
|
|
192
240
|
*/
|
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IThemeManager, ReactWidget } from '@jupyterlab/apputils';
|
|
7
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
7
8
|
import { Alert, Box } from '@mui/material';
|
|
8
9
|
import React from 'react';
|
|
9
10
|
|
|
10
11
|
import { JlThemeProvider } from '../components/jl-theme-provider';
|
|
12
|
+
import { TRANSLATION_DOMAIN } from '../context';
|
|
11
13
|
import { chatIcon } from '../icons';
|
|
12
14
|
|
|
13
15
|
export function buildErrorWidget(
|
|
14
|
-
themeManager: IThemeManager | null
|
|
16
|
+
themeManager: IThemeManager | null,
|
|
17
|
+
translator?: ITranslator
|
|
15
18
|
): ReactWidget {
|
|
19
|
+
const trans = (translator ?? nullTranslator).load(TRANSLATION_DOMAIN);
|
|
16
20
|
const ErrorWidget = ReactWidget.create(
|
|
17
21
|
<JlThemeProvider themeManager={themeManager}>
|
|
18
22
|
<Box
|
|
@@ -27,9 +31,9 @@ export function buildErrorWidget(
|
|
|
27
31
|
>
|
|
28
32
|
<Box sx={{ padding: 4 }}>
|
|
29
33
|
<Alert severity="error">
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
{trans.__(
|
|
35
|
+
'There seems to be a problem with the Chat backend, please look at the JupyterLab server logs or contact your administrator to correct this problem.'
|
|
36
|
+
)}
|
|
33
37
|
</Alert>
|
|
34
38
|
</Box>
|
|
35
39
|
</Box>
|
|
@@ -37,7 +41,7 @@ export function buildErrorWidget(
|
|
|
37
41
|
);
|
|
38
42
|
ErrorWidget.id = 'jupyter-chat::chat';
|
|
39
43
|
ErrorWidget.title.icon = chatIcon;
|
|
40
|
-
ErrorWidget.title.caption = 'Jupyter Chat';
|
|
44
|
+
ErrorWidget.title.caption = trans.__('Jupyter Chat');
|
|
41
45
|
|
|
42
46
|
return ErrorWidget;
|
|
43
47
|
}
|
|
@@ -4,10 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Button } from '@jupyter/react-components';
|
|
7
|
+
import {
|
|
8
|
+
ITranslator,
|
|
9
|
+
nullTranslator,
|
|
10
|
+
TranslationBundle
|
|
11
|
+
} from '@jupyterlab/translation';
|
|
7
12
|
import { closeIcon, ReactWidget } from '@jupyterlab/ui-components';
|
|
8
13
|
import { Message } from '@lumino/messaging';
|
|
9
14
|
import React, { useEffect, useRef } from 'react';
|
|
10
15
|
|
|
16
|
+
import { TRANSLATION_DOMAIN } from '../context';
|
|
17
|
+
|
|
11
18
|
const POPUP_CLASS = 'jp-chat-selector-popup';
|
|
12
19
|
const POPUP_LIST_CLASS = 'jp-chat-selector-popup-list';
|
|
13
20
|
const POPUP_ITEM_CLASS = 'jp-chat-selector-popup-item';
|
|
@@ -26,6 +33,9 @@ export class ChatSelectorPopup extends ReactWidget {
|
|
|
26
33
|
this._onSelect = options.onSelect;
|
|
27
34
|
this._onClose = options.onClose;
|
|
28
35
|
this._anchor = options.anchor ?? null;
|
|
36
|
+
this._trans = (options.translator ?? nullTranslator).load(
|
|
37
|
+
TRANSLATION_DOMAIN
|
|
38
|
+
);
|
|
29
39
|
|
|
30
40
|
// Start hidden
|
|
31
41
|
this.hide();
|
|
@@ -181,6 +191,7 @@ export class ChatSelectorPopup extends ReactWidget {
|
|
|
181
191
|
onSelect={this._handleItemClick}
|
|
182
192
|
onUpdateSelectedName={this._handleUpdateSelectedName}
|
|
183
193
|
onClose={this._handleClose}
|
|
194
|
+
trans={this._trans}
|
|
184
195
|
/>
|
|
185
196
|
);
|
|
186
197
|
}
|
|
@@ -319,6 +330,7 @@ export class ChatSelectorPopup extends ReactWidget {
|
|
|
319
330
|
private _onClose?: (name: string) => void;
|
|
320
331
|
private _anchor: HTMLElement | null = null;
|
|
321
332
|
private _anchorRect: DOMRect | null = null;
|
|
333
|
+
private _trans: TranslationBundle;
|
|
322
334
|
}
|
|
323
335
|
|
|
324
336
|
/**
|
|
@@ -342,6 +354,10 @@ export namespace ChatSelectorPopup {
|
|
|
342
354
|
* The element to anchor the popup to.
|
|
343
355
|
*/
|
|
344
356
|
anchor?: HTMLElement;
|
|
357
|
+
/**
|
|
358
|
+
* The translator for internationalization.
|
|
359
|
+
*/
|
|
360
|
+
translator?: ITranslator;
|
|
345
361
|
}
|
|
346
362
|
}
|
|
347
363
|
|
|
@@ -355,6 +371,7 @@ interface IChatSelectorListProps {
|
|
|
355
371
|
onSelect: (name: string) => void;
|
|
356
372
|
onUpdateSelectedName: (name: string) => void;
|
|
357
373
|
onClose: (names: string) => void;
|
|
374
|
+
trans: TranslationBundle;
|
|
358
375
|
}
|
|
359
376
|
|
|
360
377
|
/**
|
|
@@ -366,7 +383,8 @@ function ChatSelectorList({
|
|
|
366
383
|
loadedModels,
|
|
367
384
|
onSelect,
|
|
368
385
|
onUpdateSelectedName,
|
|
369
|
-
onClose
|
|
386
|
+
onClose,
|
|
387
|
+
trans
|
|
370
388
|
}: IChatSelectorListProps): JSX.Element {
|
|
371
389
|
const listRef = useRef<HTMLUListElement>(null);
|
|
372
390
|
|
|
@@ -388,7 +406,7 @@ function ChatSelectorList({
|
|
|
388
406
|
};
|
|
389
407
|
|
|
390
408
|
if (names.length === 0) {
|
|
391
|
-
return <div className={POPUP_EMPTY_CLASS}>No
|
|
409
|
+
return <div className={POPUP_EMPTY_CLASS}>{trans.__('No chat found')}</div>;
|
|
392
410
|
}
|
|
393
411
|
|
|
394
412
|
return (
|
|
@@ -425,7 +443,7 @@ function ChatSelectorList({
|
|
|
425
443
|
<Button
|
|
426
444
|
onClick={e => handleCloseClick(e, name)}
|
|
427
445
|
appearance="stealth"
|
|
428
|
-
title=
|
|
446
|
+
title={trans.__('Close and dispose this chat')}
|
|
429
447
|
className="jp-chat-selector-popup-item-close"
|
|
430
448
|
>
|
|
431
449
|
<closeIcon.react tag={null} />
|
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { nullTranslator } from '@jupyterlab/translation';
|
|
7
|
+
|
|
6
8
|
import { Chat } from '../components/chat';
|
|
9
|
+
import { TRANSLATION_DOMAIN } from '../context';
|
|
7
10
|
import { chatIcon } from '../icons';
|
|
8
11
|
import { ChatWidget } from './chat-widget';
|
|
9
12
|
|
|
10
13
|
export function buildChatSidebar(options: Chat.IOptions): ChatWidget {
|
|
11
14
|
const widget = new ChatWidget(options);
|
|
15
|
+
const trans = (options.translator ?? nullTranslator).load(TRANSLATION_DOMAIN);
|
|
12
16
|
widget.id = 'jupyter-chat::side-panel';
|
|
13
17
|
widget.title.icon = chatIcon;
|
|
14
|
-
widget.title.caption = 'Jupyter Chat';
|
|
18
|
+
widget.title.caption = trans.__('Jupyter Chat');
|
|
15
19
|
return widget;
|
|
16
20
|
}
|
|
@@ -8,12 +8,14 @@ import { Cell } from '@jupyterlab/cells';
|
|
|
8
8
|
import { DirListing } from '@jupyterlab/filebrowser';
|
|
9
9
|
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
10
10
|
import { ICell, isCode, isMarkdown, isRaw } from '@jupyterlab/nbformat';
|
|
11
|
+
import { nullTranslator } from '@jupyterlab/translation';
|
|
11
12
|
import React from 'react';
|
|
12
13
|
import { Message } from '@lumino/messaging';
|
|
13
14
|
import { Drag } from '@lumino/dragdrop';
|
|
14
15
|
import { Widget } from '@lumino/widgets';
|
|
15
16
|
|
|
16
17
|
import { Chat, IInputToolbarRegistry, MESSAGE_CLASS } from '../components';
|
|
18
|
+
import { TRANSLATION_DOMAIN } from '../context';
|
|
17
19
|
import { chatIcon } from '../icons';
|
|
18
20
|
import { IChatModel } from '../model';
|
|
19
21
|
import {
|
|
@@ -42,8 +44,11 @@ export class ChatWidget extends ReactWidget {
|
|
|
42
44
|
constructor(options: Chat.IOptions) {
|
|
43
45
|
super();
|
|
44
46
|
|
|
47
|
+
const trans = (options.translator ?? nullTranslator).load(
|
|
48
|
+
TRANSLATION_DOMAIN
|
|
49
|
+
);
|
|
45
50
|
this.title.icon = chatIcon;
|
|
46
|
-
this.title.caption = 'Jupyter Chat';
|
|
51
|
+
this.title.caption = trans.__('Jupyter Chat');
|
|
47
52
|
|
|
48
53
|
this._chatOptions = options;
|
|
49
54
|
this.id = `jupyter-chat::widget::${options.model.name}`;
|
|
@@ -334,6 +339,7 @@ export class ChatWidget extends ReactWidget {
|
|
|
334
339
|
const attachment: INotebookAttachment = {
|
|
335
340
|
type: 'notebook',
|
|
336
341
|
value: notebookPath,
|
|
342
|
+
mimetype: 'application/x-ipynb+json',
|
|
337
343
|
cells: validCells
|
|
338
344
|
};
|
|
339
345
|
const inputModel = this._getInputFromEvent(event);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { InputDialog } from '@jupyterlab/apputils';
|
|
12
|
+
import { nullTranslator, TranslationBundle } from '@jupyterlab/translation';
|
|
12
13
|
import {
|
|
13
14
|
addIcon,
|
|
14
15
|
closeIcon,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
IInputToolbarRegistry,
|
|
33
34
|
IInputToolbarRegistryFactory
|
|
34
35
|
} from '../components';
|
|
36
|
+
import { TRANSLATION_DOMAIN } from '../context';
|
|
35
37
|
import { chatIcon, readIcon } from '../icons';
|
|
36
38
|
import { IChatModel } from '../model';
|
|
37
39
|
|
|
@@ -52,7 +54,11 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
52
54
|
super(options);
|
|
53
55
|
this.id = 'jupyter-chat::multi-chat-panel';
|
|
54
56
|
this.title.icon = chatIcon;
|
|
55
|
-
|
|
57
|
+
|
|
58
|
+
const translator = options.translator ?? nullTranslator;
|
|
59
|
+
this._trans = translator.load(TRANSLATION_DOMAIN);
|
|
60
|
+
|
|
61
|
+
this.title.caption = this._trans.__('Jupyter Chat');
|
|
56
62
|
|
|
57
63
|
this.addClass(SIDEPANEL_CLASS);
|
|
58
64
|
|
|
@@ -72,7 +78,7 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
72
78
|
this.open(addChatArgs);
|
|
73
79
|
},
|
|
74
80
|
icon: addIcon,
|
|
75
|
-
tooltip: 'Create a new chat'
|
|
81
|
+
tooltip: this._trans.__('Create a new chat')
|
|
76
82
|
});
|
|
77
83
|
addChat.addClass(ADD_BUTTON_CLASS);
|
|
78
84
|
this.toolbar.addItem('createChat', addChat);
|
|
@@ -85,6 +91,7 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
85
91
|
selectChat={this._onSelectChat}
|
|
86
92
|
getPopup={() => this._chatSelectorPopup}
|
|
87
93
|
chatOpened={this._chatOpened}
|
|
94
|
+
trans={this._trans}
|
|
88
95
|
/>
|
|
89
96
|
);
|
|
90
97
|
this._openChatWidget.addClass(OPEN_SELECT_CLASS);
|
|
@@ -96,7 +103,8 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
96
103
|
onSelect: this._onSelectChat,
|
|
97
104
|
onClose: (name: string) => {
|
|
98
105
|
this.disposeLoadedModel(name);
|
|
99
|
-
}
|
|
106
|
+
},
|
|
107
|
+
translator
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
110
|
|
|
@@ -234,7 +242,8 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
234
242
|
renameChat: this._renameChat,
|
|
235
243
|
onClose: (name: string) => {
|
|
236
244
|
this.disposeLoadedModel(name);
|
|
237
|
-
}
|
|
245
|
+
},
|
|
246
|
+
trans: this._trans
|
|
238
247
|
});
|
|
239
248
|
|
|
240
249
|
// Add to content panel
|
|
@@ -389,6 +398,7 @@ export class MultiChatPanel extends PanelWithToolbar {
|
|
|
389
398
|
private _currentWidget?: SidePanelWidget;
|
|
390
399
|
private _chatNames: { [name: string]: string } = {};
|
|
391
400
|
private _visibilityChanged = new Signal<MultiChatPanel, boolean>(this);
|
|
401
|
+
private _trans: TranslationBundle;
|
|
392
402
|
}
|
|
393
403
|
|
|
394
404
|
/**
|
|
@@ -457,6 +467,7 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
457
467
|
super();
|
|
458
468
|
this._chatWidget = options.widget;
|
|
459
469
|
this._displayName = options.displayName ?? options.widget.model.name;
|
|
470
|
+
const trans = options.trans;
|
|
460
471
|
|
|
461
472
|
this.addClass(SIDEPANEL_WIDGET_CLASS);
|
|
462
473
|
this.toolbar.addClass(TOOLBAR_CLASS);
|
|
@@ -477,7 +488,7 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
477
488
|
// Add toolbar buttons
|
|
478
489
|
this._markAsRead = new ToolbarButton({
|
|
479
490
|
icon: readIcon,
|
|
480
|
-
iconLabel: 'Mark chat as read',
|
|
491
|
+
iconLabel: trans.__('Mark chat as read'),
|
|
481
492
|
className: 'jp-mod-styled',
|
|
482
493
|
onClick: () => {
|
|
483
494
|
if (this.model) {
|
|
@@ -490,7 +501,7 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
490
501
|
if (options.renameChat) {
|
|
491
502
|
const renameButton = new ToolbarButton({
|
|
492
503
|
iconClass: 'jp-EditIcon',
|
|
493
|
-
iconLabel: 'Rename chat',
|
|
504
|
+
iconLabel: trans.__('Rename chat'),
|
|
494
505
|
className: 'jp-mod-styled',
|
|
495
506
|
onClick: async () => {
|
|
496
507
|
if (!options.renameChat) {
|
|
@@ -500,9 +511,9 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
500
511
|
if (options.renameChat === true) {
|
|
501
512
|
// If rename chat is true, let's provide a input to select new name.
|
|
502
513
|
const result = await InputDialog.getText({
|
|
503
|
-
title: 'Rename Chat',
|
|
514
|
+
title: trans.__('Rename Chat'),
|
|
504
515
|
text: this.model.name,
|
|
505
|
-
placeholder: 'new-name'
|
|
516
|
+
placeholder: trans.__('new-name')
|
|
506
517
|
});
|
|
507
518
|
if (!result.button.accept && result.value) {
|
|
508
519
|
return;
|
|
@@ -526,7 +537,7 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
526
537
|
if (options.openInMain) {
|
|
527
538
|
const moveToMain = new ToolbarButton({
|
|
528
539
|
icon: launchIcon,
|
|
529
|
-
iconLabel: 'Move the chat to the main area',
|
|
540
|
+
iconLabel: trans.__('Move the chat to the main area'),
|
|
530
541
|
className: 'jp-mod-styled',
|
|
531
542
|
onClick: async () => {
|
|
532
543
|
const name = this.model.name;
|
|
@@ -540,7 +551,7 @@ class SidePanelWidget extends PanelWithToolbar {
|
|
|
540
551
|
|
|
541
552
|
const closeButton = new ToolbarButton({
|
|
542
553
|
icon: closeIcon,
|
|
543
|
-
iconLabel: 'Close the chat',
|
|
554
|
+
iconLabel: trans.__('Close the chat'),
|
|
544
555
|
className: 'jp-mod-styled',
|
|
545
556
|
onClick: () => {
|
|
546
557
|
options.onClose(this._displayName);
|
|
@@ -668,6 +679,10 @@ namespace SidePanelWidget {
|
|
|
668
679
|
* The callback to rename the chat.
|
|
669
680
|
*/
|
|
670
681
|
renameChat?: boolean | ((oldName: string) => Promise<string | null>);
|
|
682
|
+
/**
|
|
683
|
+
* The translation bundle.
|
|
684
|
+
*/
|
|
685
|
+
trans: TranslationBundle;
|
|
671
686
|
}
|
|
672
687
|
}
|
|
673
688
|
|
|
@@ -684,6 +699,10 @@ type ChatSearchInputProps = {
|
|
|
684
699
|
* Signal emitting when a chat is opened.
|
|
685
700
|
*/
|
|
686
701
|
chatOpened: ISignal<MultiChatPanel, ChatWidget>;
|
|
702
|
+
/**
|
|
703
|
+
* The translation bundle.
|
|
704
|
+
*/
|
|
705
|
+
trans: TranslationBundle;
|
|
687
706
|
};
|
|
688
707
|
|
|
689
708
|
/**
|
|
@@ -692,7 +711,8 @@ type ChatSearchInputProps = {
|
|
|
692
711
|
function ChatSearchInput({
|
|
693
712
|
selectChat,
|
|
694
713
|
getPopup,
|
|
695
|
-
chatOpened
|
|
714
|
+
chatOpened,
|
|
715
|
+
trans
|
|
696
716
|
}: ChatSearchInputProps): JSX.Element {
|
|
697
717
|
const [query, setQuery] = useState<string>('');
|
|
698
718
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
@@ -777,7 +797,7 @@ function ChatSearchInput({
|
|
|
777
797
|
<input
|
|
778
798
|
ref={inputRef}
|
|
779
799
|
type="text"
|
|
780
|
-
placeholder=
|
|
800
|
+
placeholder={trans.__('Select a chat')}
|
|
781
801
|
value={query}
|
|
782
802
|
onChange={handleInputChange}
|
|
783
803
|
onFocus={handleInputFocus}
|