@jupyter/chat 0.7.1 → 0.8.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.
Files changed (59) hide show
  1. package/lib/active-cell-manager.js +1 -4
  2. package/lib/chat-commands/index.d.ts +2 -0
  3. package/lib/chat-commands/index.js +6 -0
  4. package/lib/chat-commands/registry.d.ts +28 -0
  5. package/lib/chat-commands/registry.js +29 -0
  6. package/lib/chat-commands/types.d.ts +51 -0
  7. package/lib/chat-commands/types.js +5 -0
  8. package/lib/components/attachments.d.ts +23 -0
  9. package/lib/components/attachments.js +44 -0
  10. package/lib/components/chat-input.d.ts +8 -11
  11. package/lib/components/chat-input.js +70 -95
  12. package/lib/components/chat-messages.d.ts +4 -0
  13. package/lib/components/chat-messages.js +27 -1
  14. package/lib/components/chat.d.ts +11 -5
  15. package/lib/components/chat.js +7 -8
  16. package/lib/components/input/attach-button.d.ts +14 -0
  17. package/lib/components/input/attach-button.js +45 -0
  18. package/lib/components/input/index.d.ts +1 -0
  19. package/lib/components/input/index.js +1 -0
  20. package/lib/components/input/send-button.d.ts +2 -2
  21. package/lib/components/input/use-chat-commands.d.ts +19 -0
  22. package/lib/components/input/use-chat-commands.js +127 -0
  23. package/lib/context.d.ts +3 -0
  24. package/lib/context.js +6 -0
  25. package/lib/index.d.ts +2 -0
  26. package/lib/index.js +2 -0
  27. package/lib/input-model.d.ts +221 -0
  28. package/lib/input-model.js +217 -0
  29. package/lib/model.d.ts +10 -25
  30. package/lib/model.js +15 -17
  31. package/lib/registry.d.ts +11 -64
  32. package/lib/registry.js +4 -72
  33. package/lib/types.d.ts +19 -38
  34. package/lib/widgets/chat-widget.js +2 -1
  35. package/package.json +3 -114
  36. package/src/active-cell-manager.ts +0 -3
  37. package/src/chat-commands/index.ts +7 -0
  38. package/src/chat-commands/registry.ts +60 -0
  39. package/src/chat-commands/types.ts +67 -0
  40. package/src/components/attachments.tsx +91 -0
  41. package/src/components/chat-input.tsx +97 -124
  42. package/src/components/chat-messages.tsx +36 -3
  43. package/src/components/chat.tsx +28 -19
  44. package/src/components/input/attach-button.tsx +68 -0
  45. package/src/components/input/cancel-button.tsx +1 -0
  46. package/src/components/input/index.ts +1 -0
  47. package/src/components/input/send-button.tsx +2 -2
  48. package/src/components/input/use-chat-commands.tsx +186 -0
  49. package/src/context.ts +10 -0
  50. package/src/index.ts +2 -0
  51. package/src/input-model.ts +406 -0
  52. package/src/model.ts +24 -35
  53. package/src/registry.ts +14 -108
  54. package/src/types.ts +19 -39
  55. package/src/widgets/chat-widget.tsx +2 -1
  56. package/style/chat.css +27 -9
  57. package/style/icons/include-selection.svg +3 -1
  58. package/style/icons/read.svg +8 -6
  59. package/style/icons/replace-cell.svg +10 -6
package/src/model.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  } from './types';
17
17
  import { IActiveCellManager } from './active-cell-manager';
18
18
  import { ISelectionWatcher } from './selection-watcher';
19
+ import { IInputModel, InputModel } from './input-model';
19
20
 
20
21
  /**
21
22
  * The chat model interface.
@@ -51,6 +52,11 @@ export interface IChatModel extends IDisposable {
51
52
  */
52
53
  readonly messages: IChatMessage[];
53
54
 
55
+ /**
56
+ * The input model.
57
+ */
58
+ readonly input: IInputModel;
59
+
54
60
  /**
55
61
  * Get the active cell manager.
56
62
  */
@@ -86,11 +92,6 @@ export interface IChatModel extends IDisposable {
86
92
  */
87
93
  readonly writersChanged?: ISignal<IChatModel, IUser[]>;
88
94
 
89
- /**
90
- * A signal emitting when the focus is requested on the input.
91
- */
92
- readonly focusInputSignal?: ISignal<IChatModel, void>;
93
-
94
95
  /**
95
96
  * Send a message, to be defined depending on the chosen technology.
96
97
  * Default to no-op.
@@ -160,16 +161,6 @@ export interface IChatModel extends IDisposable {
160
161
  * Update the current writers list.
161
162
  */
162
163
  updateWriters(writers: IUser[]): void;
163
-
164
- /**
165
- * Function to request the focus on the input of the chat.
166
- */
167
- focusInput(): void;
168
-
169
- /**
170
- * Function called by the input on key pressed.
171
- */
172
- inputChanged?(input?: string): void;
173
164
  }
174
165
 
175
166
  /**
@@ -195,6 +186,14 @@ export class ChatModel implements IChatModel {
195
186
  ...config
196
187
  };
197
188
 
189
+ this._inputModel = new InputModel({
190
+ activeCellManager: options.activeCellManager,
191
+ selectionWatcher: options.selectionWatcher,
192
+ config: {
193
+ sendWithShiftEnter: config.sendWithShiftEnter
194
+ }
195
+ });
196
+
198
197
  this._commands = options.commands;
199
198
 
200
199
  this._activeCellManager = options.activeCellManager ?? null;
@@ -228,6 +227,14 @@ export class ChatModel implements IChatModel {
228
227
  get messages(): IChatMessage[] {
229
228
  return this._messages;
230
229
  }
230
+
231
+ /**
232
+ * The input model.
233
+ */
234
+ get input(): IInputModel {
235
+ return this._inputModel;
236
+ }
237
+
231
238
  /**
232
239
  * Get the active cell manager.
233
240
  */
@@ -284,6 +291,7 @@ export class ChatModel implements IChatModel {
284
291
 
285
292
  this._configChanged.emit(this._config);
286
293
 
294
+ this.input.config = value;
287
295
  // Update the stacked status of the messages and the view.
288
296
  if (stackMessagesChanged) {
289
297
  if (this._config.stackMessages) {
@@ -386,13 +394,6 @@ export class ChatModel implements IChatModel {
386
394
  return this._writersChanged;
387
395
  }
388
396
 
389
- /**
390
- * A signal emitting when the focus is requested on the input.
391
- */
392
- get focusInputSignal(): ISignal<IChatModel, void> {
393
- return this._focusInputSignal;
394
- }
395
-
396
397
  /**
397
398
  * Send a message, to be defined depending on the chosen technology.
398
399
  * Default to no-op.
@@ -508,18 +509,6 @@ export class ChatModel implements IChatModel {
508
509
  this._writersChanged.emit(writers);
509
510
  }
510
511
 
511
- /**
512
- * Function to request the focus on the input of the chat.
513
- */
514
- focusInput(): void {
515
- this._focusInputSignal.emit();
516
- }
517
-
518
- /**
519
- * Function called by the input on key pressed.
520
- */
521
- inputChanged?(input?: string): void {}
522
-
523
512
  /**
524
513
  * Add unread messages to the list.
525
514
  * @param indexes - list of new indexes.
@@ -575,6 +564,7 @@ export class ChatModel implements IChatModel {
575
564
  private _id: string | undefined;
576
565
  private _name: string = '';
577
566
  private _config: IConfig;
567
+ private _inputModel: IInputModel;
578
568
  private _isDisposed = false;
579
569
  private _commands?: CommandRegistry;
580
570
  private _activeCellManager: IActiveCellManager | null;
@@ -585,7 +575,6 @@ export class ChatModel implements IChatModel {
585
575
  private _unreadChanged = new Signal<IChatModel, number[]>(this);
586
576
  private _viewportChanged = new Signal<IChatModel, number[]>(this);
587
577
  private _writersChanged = new Signal<IChatModel, IUser[]>(this);
588
- private _focusInputSignal = new Signal<ChatModel, void>(this);
589
578
  }
590
579
 
591
580
  /**
package/src/registry.ts CHANGED
@@ -3,127 +3,33 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { Token } from '@lumino/coreutils';
6
- import { IAutocompletionCommandsProps } from './types';
6
+ import { IAttachment } from './types';
7
7
 
8
8
  /**
9
- * The token for the autocomplete registry, which can be provided by an extension
9
+ * The token for the attachments opener registry, which can be provided by an extension
10
10
  * using @jupyter/chat package.
11
11
  */
12
- export const IAutocompletionRegistry = new Token<IAutocompletionRegistry>(
13
- '@jupyter/chat:IAutocompleteRegistry'
12
+ export const IAttachmentOpenerRegistry = new Token<IAttachmentOpenerRegistry>(
13
+ '@jupyter/chat:IAttachmentOpenerRegistry'
14
14
  );
15
15
 
16
16
  /**
17
- * The interface of a registry to provide autocompleters.
17
+ * The interface of a registry to provide attachments opener.
18
18
  */
19
- export interface IAutocompletionRegistry {
19
+ export interface IAttachmentOpenerRegistry {
20
20
  /**
21
- * The default autocompletion name.
21
+ * Get the function opening an attachment for a given type.
22
22
  */
23
- default: string | null;
23
+ get(type: string): ((attachment: IAttachment) => void) | undefined;
24
24
  /**
25
- * Get the default autocompletion.
25
+ * Register a function to open an attachment type.
26
26
  */
27
- getDefaultCompletion(): IAutocompletionCommandsProps | undefined;
28
- /**
29
- * Return a registered autocomplete props.
30
- *
31
- * @param name - the name of the registered autocomplete props.
32
- */
33
- get(name: string): IAutocompletionCommandsProps | undefined;
34
-
35
- /**
36
- * Register autocomplete props.
37
- *
38
- * @param name - the name for the registration.
39
- * @param autocompletion - the autocomplete props.
40
- */
41
- add(name: string, autocompletion: IAutocompletionCommandsProps): boolean;
42
-
43
- /**
44
- * Remove a registered autocomplete props.
45
- *
46
- * @param name - the name of the autocomplete props.
47
- */
48
- remove(name: string): boolean;
27
+ set(type: string, opener: (attachment: IAttachment) => void): void;
49
28
  }
50
29
 
51
30
  /**
52
- * A registry to provide autocompleters.
31
+ * The default registry, a Map object.
53
32
  */
54
- export class AutocompletionRegistry implements IAutocompletionRegistry {
55
- /**
56
- * Getter and setter for the default autocompletion name.
57
- */
58
- get default(): string | null {
59
- return this._default;
60
- }
61
- set default(name: string | null) {
62
- if (name === null || this._autocompletions.has(name)) {
63
- this._default = name;
64
- } else {
65
- console.warn(`There is no registered completer with the name '${name}'`);
66
- }
67
- }
68
-
69
- /**
70
- * Get the default autocompletion.
71
- */
72
- getDefaultCompletion(): IAutocompletionCommandsProps | undefined {
73
- if (this._default === null) {
74
- return undefined;
75
- }
76
- return this._autocompletions.get(this._default);
77
- }
78
-
79
- /**
80
- * Return a registered autocomplete props.
81
- *
82
- * @param name - the name of the registered autocomplete props.
83
- */
84
- get(name: string): IAutocompletionCommandsProps | undefined {
85
- return this._autocompletions.get(name);
86
- }
87
-
88
- /**
89
- * Register autocomplete props.
90
- *
91
- * @param name - the name for the registration.
92
- * @param autocompletion - the autocomplete props.
93
- */
94
- add(
95
- name: string,
96
- autocompletion: IAutocompletionCommandsProps,
97
- isDefault: boolean = false
98
- ): boolean {
99
- if (!this._autocompletions.has(name)) {
100
- this._autocompletions.set(name, autocompletion);
101
- if (this._autocompletions.size === 1 || isDefault) {
102
- this.default = name;
103
- }
104
- return true;
105
- } else {
106
- console.warn(`A completer with the name '${name}' is already registered`);
107
- return false;
108
- }
109
- }
110
-
111
- /**
112
- * Remove a registered autocomplete props.
113
- *
114
- * @param name - the name of the autocomplete props.
115
- */
116
- remove(name: string): boolean {
117
- return this._autocompletions.delete(name);
118
- }
119
-
120
- /**
121
- * Remove all registered autocompletions.
122
- */
123
- removeAll(): void {
124
- this._autocompletions.clear();
125
- }
126
-
127
- private _default: string | null = null;
128
- private _autocompletions = new Map<string, IAutocompletionCommandsProps>();
129
- }
33
+ export class AttachmentOpenerRegistry
34
+ extends Map<string, (attachment: IAttachment) => void>
35
+ implements IAttachmentOpenerRegistry {}
package/src/types.ts CHANGED
@@ -44,12 +44,13 @@ export interface IConfig {
44
44
  /**
45
45
  * The chat message description.
46
46
  */
47
- export interface IChatMessage<T = IUser> {
47
+ export interface IChatMessage<T = IUser, U = IAttachment> {
48
48
  type: 'msg';
49
49
  body: string;
50
50
  id: string;
51
51
  time: number;
52
52
  sender: T;
53
+ attachments?: U[];
53
54
  raw_time?: boolean;
54
55
  deleted?: boolean;
55
56
  edited?: boolean;
@@ -72,16 +73,27 @@ export interface INewMessage {
72
73
  }
73
74
 
74
75
  /**
75
- * An empty interface to describe optional settings that could be fetched from server.
76
+ * The attachment interface.
76
77
  */
77
- export interface ISettings {}
78
+ export interface IAttachment {
79
+ /**
80
+ * The type of the attachment (basically 'file', 'variable', 'image')
81
+ */
82
+ type: string;
83
+ /**
84
+ * The value, i.e. the file path, the variable name or image content.
85
+ */
86
+ value: string;
87
+ /**
88
+ * The mimetype of the attachment, optional.
89
+ */
90
+ mimetype?: string;
91
+ }
78
92
 
79
93
  /**
80
- * The autocomplete command type.
94
+ * An empty interface to describe optional settings that could be fetched from server.
81
95
  */
82
- export type AutocompleteCommand = {
83
- label: string;
84
- };
96
+ export interface ISettings {} /* eslint-disable-line @typescript-eslint/no-empty-object-type */
85
97
 
86
98
  /**
87
99
  * Representation of a selected text.
@@ -103,35 +115,3 @@ export type CellSelection = {
103
115
  * Selection object (text or cell).
104
116
  */
105
117
  export type Selection = TextSelection | CellSelection;
106
-
107
- /**
108
- * The properties of the autocompletion.
109
- *
110
- * The autocompletion component will open if the 'opener' string is typed at the
111
- * beginning of the input field.
112
- */
113
- export interface IAutocompletionCommandsProps {
114
- /**
115
- * The string that open the completer.
116
- */
117
- opener: string;
118
- /**
119
- * The list of available commands.
120
- */
121
- commands?: AutocompleteCommand[] | (() => Promise<AutocompleteCommand[]>);
122
- /**
123
- * The props for the Autocomplete component.
124
- *
125
- * Must be compatible with https://mui.com/material-ui/api/autocomplete/#props.
126
- *
127
- * ## NOTES:
128
- * - providing `options` will overwrite the commands argument.
129
- * - providing `renderInput` will overwrite the input component.
130
- * - providing `renderOptions` allows to customize the rendering of the component.
131
- * - some arguments should not be provided and would be overwritten:
132
- * - inputValue
133
- * - onInputChange
134
- * - onHighlightChange
135
- */
136
- props?: any;
137
- }
@@ -14,11 +14,12 @@ export class ChatWidget extends ReactWidget {
14
14
  constructor(options: Chat.IOptions) {
15
15
  super();
16
16
 
17
- this.id = 'jupyter-chat::widget';
18
17
  this.title.icon = chatIcon;
19
18
  this.title.caption = 'Jupyter Chat'; // TODO: i18n
20
19
 
21
20
  this._chatOptions = options;
21
+ this.id = `jupyter-chat::widget::${options.model.name}`;
22
+ this.node.onclick = () => this.model.input.focus();
22
23
  }
23
24
 
24
25
  /**
package/style/chat.css CHANGED
@@ -2,12 +2,12 @@
2
2
  * Copyright (c) Jupyter Development Team.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- .jp-chat-message:not(:first-child):not(.jp-chat-message-stacked) {
6
- border-top: 1px solid var(--jp-border-color2);
5
+ .jp-chat-message:not(.jp-chat-message-stacked) {
6
+ padding: 1em 1em 0;
7
7
  }
8
8
 
9
- .jp-chat-message:not(.jp-chat-message-stacked) {
10
- padding: 1em 1em 0 1em;
9
+ .jp-chat-message:not(:first-child, .jp-chat-message-stacked) {
10
+ border-top: 1px solid var(--jp-border-color2);
11
11
  }
12
12
 
13
13
  .jp-chat-message.jp-chat-message-stacked {
@@ -58,17 +58,17 @@
58
58
  color: var(--jp-ui-font-color3);
59
59
  }
60
60
 
61
- .jp-chat-rendered-markdown:hover .jp-chat-toolbar {
62
- display: inherit;
63
- }
64
-
65
61
  .jp-chat-toolbar:hover {
66
62
  cursor: pointer;
67
63
  color: var(--jp-ui-font-color2);
68
64
  }
69
65
 
66
+ .jp-chat-rendered-markdown:hover .jp-chat-toolbar {
67
+ display: inherit;
68
+ }
69
+
70
70
  .jp-chat-toolbar > .jp-ToolbarButtonComponent {
71
- margin-top: 0px;
71
+ margin-top: 0;
72
72
  }
73
73
 
74
74
  .jp-chat-writers {
@@ -112,3 +112,21 @@
112
112
  .jp-chat-navigation-bottom {
113
113
  bottom: 100px;
114
114
  }
115
+
116
+ .jp-chat-attachments {
117
+ display: flex;
118
+ min-height: 1.5em;
119
+ }
120
+
121
+ .jp-chat-attachment {
122
+ border: solid 1px;
123
+ border-radius: 10px;
124
+ margin: 0 0.2em;
125
+ padding: 0 0.3em;
126
+ align-content: center;
127
+ background-color: var(--jp-border-color3);
128
+ }
129
+
130
+ .jp-chat-attachment .jp-chat-attachment-clickable:hover {
131
+ cursor: pointer;
132
+ }
@@ -1,5 +1,7 @@
1
1
  <svg width="18" height="9" viewBox="0 0 18 9" fill="none" xmlns="http://www.w3.org/2000/svg">
2
2
  <path fill-rule="evenodd" clip-rule="evenodd"
3
3
  d="M-0.0018417 1.33824L0.837379 0.499023L1.35717 1.01871C1.35741 1.01862 1.35764 1.01853 1.35787 1.01845L3.411 3.07158H3.41049L4.83909 4.49987L3.41011 5.92872H3.41075L1.35769 7.98178C1.35749 7.98171 1.35729 7.98164 1.35709 7.98156L0.837023 8.50158L-0.00219727 7.66236L3.1547 4.49987L-0.0018417 1.33824ZM2.6821 8.07158H16.143C16.932 8.07158 17.5716 7.43201 17.5716 6.64301V2.35729C17.5716 1.56832 16.932 0.928721 16.143 0.928721H2.68236L4.82522 3.07158H15.4287V5.92872H4.82496L2.6821 8.07158ZM1.74238 4.50002L0.428719 5.81655V3.18349L1.74238 4.50002Z"
4
- fill="#757575" />
4
+ fill="#616161"
5
+ class="jp-icon3"
6
+ />
5
7
  </svg>
@@ -1,11 +1,13 @@
1
1
  <svg height="24px" viewBox="0 0 24 24" width="24px" xmlns="http://www.w3.org/2000/svg">
2
- <g>
2
+ <g class="jp-icon3">
3
3
  <path
4
- style="display:inline;fill:none;fill-opacity:1;stroke:#545454;stroke-width:1.54693;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.5;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
5
- d="M 2.2734634,9 V 20.226537 H 21.726536 V 9" />
4
+ style="display:inline;fill:none;fill-opacity:1;stroke:#616161;stroke-width:1.54693;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.5;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
5
+ d="M 2.2734634,9 V 20.226537 H 21.726536 V 9"
6
+ />
6
7
  <path
7
- style="display:inline;fill:none;stroke:#545454;stroke-width:1.54528;stroke-linecap:square;stroke-miterlimit:10"
8
- transform="matrix(0.81805878,0.57513462,-0.81805878,0.57513462,0,0)"
9
- d="M 9.5141659,-5.1535239 H 20.802967 V 6.1352773 H 9.5141659 Z" />
8
+ style="display:inline;fill:none;stroke:#616161;stroke-width:1.54528;stroke-linecap:square;stroke-miterlimit:10"
9
+ transform="matrix(0.81805878,0.57513462,-0.81805878,0.57513462,0,0)"
10
+ d="M 9.5141659,-5.1535239 H 20.802967 V 6.1352773 H 9.5141659 Z"
11
+ />
10
12
  </g>
11
13
  </svg>
@@ -1,8 +1,12 @@
1
1
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path fill-rule="evenodd" clip-rule="evenodd"
3
- d="M12 4.71429V7H1.71429V4.71429H12ZM12.5714 3C13.2026 3 13.7143 3.51168 13.7143 4.14286V7.57143C13.7143 8.20263 13.2026 8.71429 12.5714 8.71429H1.14286C0.51168 8.71429 0 8.20263 0 7.57143V4.14286C0 3.51168 0.511669 3 1.14286 3H12.5714Z"
4
- fill="#565656" />
5
- <path fill-rule="evenodd" clip-rule="evenodd"
6
- d="M14 8.71429V11H3.71429V8.71429H14ZM14.5714 7C15.2026 7 15.7143 7.51168 15.7143 8.14286V11.5714C15.7143 12.2026 15.2026 12.7143 14.5714 12.7143H3.14286C2.51168 12.7143 2 12.2026 2 11.5714V8.14286C2 7.51168 2.51167 7 3.14286 7H14.5714Z"
7
- fill="#565656" />
2
+ <path fill-rule="evenodd" clip-rule="evenodd"
3
+ d="M12 4.71429V7H1.71429V4.71429H12ZM12.5714 3C13.2026 3 13.7143 3.51168 13.7143 4.14286V7.57143C13.7143 8.20263 13.2026 8.71429 12.5714 8.71429H1.14286C0.51168 8.71429 0 8.20263 0 7.57143V4.14286C0 3.51168 0.511669 3 1.14286 3H12.5714Z"
4
+ fill="#616161"
5
+ class="jp-icon3"
6
+ />
7
+ <path fill-rule="evenodd" clip-rule="evenodd"
8
+ d="M14 8.71429V11H3.71429V8.71429H14ZM14.5714 7C15.2026 7 15.7143 7.51168 15.7143 8.14286V11.5714C15.7143 12.2026 15.2026 12.7143 14.5714 12.7143H3.14286C2.51168 12.7143 2 12.2026 2 11.5714V8.14286C2 7.51168 2.51167 7 3.14286 7H14.5714Z"
9
+ fill="#616161"
10
+ class="jp-icon3"
11
+ />
8
12
  </svg>