@jupyter/chat 0.20.0-alpha.1 → 0.20.0-alpha.3

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 (41) hide show
  1. package/lib/__tests__/preamble-registry.spec.d.ts +1 -0
  2. package/lib/__tests__/preamble-registry.spec.js +37 -0
  3. package/lib/components/chat.d.ts +5 -1
  4. package/lib/components/input/buttons/send-button.js +3 -6
  5. package/lib/components/messages/index.d.ts +1 -0
  6. package/lib/components/messages/index.js +1 -0
  7. package/lib/components/messages/messages.js +3 -1
  8. package/lib/components/messages/preamble.d.ts +12 -0
  9. package/lib/components/messages/preamble.js +31 -0
  10. package/lib/message.d.ts +2 -1
  11. package/lib/message.js +3 -0
  12. package/lib/registers/index.d.ts +1 -0
  13. package/lib/registers/index.js +1 -0
  14. package/lib/registers/preambles.d.ts +44 -0
  15. package/lib/registers/preambles.js +29 -0
  16. package/lib/types.d.ts +15 -3
  17. package/lib/widgets/chat-selector-popup.d.ts +102 -0
  18. package/lib/widgets/chat-selector-popup.js +293 -0
  19. package/lib/widgets/chat-widget.js +1 -0
  20. package/lib/widgets/index.d.ts +1 -0
  21. package/lib/widgets/index.js +1 -0
  22. package/lib/widgets/multichat-panel.d.ts +100 -73
  23. package/lib/widgets/multichat-panel.js +356 -159
  24. package/package.json +1 -1
  25. package/src/__tests__/preamble-registry.spec.ts +51 -0
  26. package/src/components/chat.tsx +6 -1
  27. package/src/components/input/buttons/send-button.tsx +4 -6
  28. package/src/components/messages/index.ts +1 -0
  29. package/src/components/messages/messages.tsx +11 -2
  30. package/src/components/messages/preamble.tsx +55 -0
  31. package/src/message.ts +10 -1
  32. package/src/registers/index.ts +1 -0
  33. package/src/registers/preambles.ts +65 -0
  34. package/src/types.ts +15 -3
  35. package/src/widgets/chat-selector-popup.tsx +440 -0
  36. package/src/widgets/chat-widget.tsx +1 -0
  37. package/src/widgets/index.ts +1 -0
  38. package/src/widgets/multichat-panel.tsx +434 -207
  39. package/style/base.css +1 -0
  40. package/style/chat-selector.css +161 -0
  41. package/style/chat.css +34 -2
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { MessagePreambleRegistry } from '../registers/preambles';
6
+ describe('MessagePreambleRegistry', () => {
7
+ let registry;
8
+ beforeEach(() => {
9
+ registry = new MessagePreambleRegistry();
10
+ });
11
+ it('should start with no components', () => {
12
+ expect(registry.getComponents()).toEqual([]);
13
+ });
14
+ it('should add a component', () => {
15
+ const component = () => null;
16
+ registry.addComponent(component);
17
+ expect(registry.getComponents()).toHaveLength(1);
18
+ expect(registry.getComponents()[0]).toBe(component);
19
+ });
20
+ it('should preserve insertion order', () => {
21
+ const first = () => null;
22
+ const second = () => null;
23
+ registry.addComponent(first);
24
+ registry.addComponent(second);
25
+ const components = registry.getComponents();
26
+ expect(components).toHaveLength(2);
27
+ expect(components[0]).toBe(first);
28
+ expect(components[1]).toBe(second);
29
+ });
30
+ it('should return a copy from getComponents', () => {
31
+ const component = () => null;
32
+ registry.addComponent(component);
33
+ const result = registry.getComponents();
34
+ result.push(() => null);
35
+ expect(registry.getComponents()).toHaveLength(1);
36
+ });
37
+ });
@@ -3,7 +3,7 @@ import { IThemeManager } from '@jupyterlab/apputils';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
4
  import { IInputToolbarRegistry } from './input';
5
5
  import { IChatModel } from '../model';
6
- import { IAttachmentOpenerRegistry, IChatCommandRegistry, IMessageFooterRegistry } from '../registers';
6
+ import { IAttachmentOpenerRegistry, IChatCommandRegistry, IMessageFooterRegistry, IMessagePreambleRegistry } from '../registers';
7
7
  import { ChatArea } from '../types';
8
8
  export declare function ChatBody(props: Chat.IChatProps): JSX.Element;
9
9
  export declare function Chat(props: Chat.IOptions): JSX.Element;
@@ -39,6 +39,10 @@ export declare namespace Chat {
39
39
  * The footer registry.
40
40
  */
41
41
  messageFooterRegistry?: IMessageFooterRegistry;
42
+ /**
43
+ * The preamble registry for content above message body.
44
+ */
45
+ messagePreambleRegistry?: IMessagePreambleRegistry;
42
46
  /**
43
47
  * The welcome message.
44
48
  */
@@ -10,7 +10,7 @@ const SEND_BUTTON_CLASS = 'jp-chat-send-button';
10
10
  * The send button.
11
11
  */
12
12
  export function SendButton(props) {
13
- const { model, chatModel, chatCommandRegistry, edit } = props;
13
+ const { model, chatCommandRegistry, edit } = props;
14
14
  // Don't show this button when in edit mode
15
15
  if (edit) {
16
16
  return React.createElement(React.Fragment, null);
@@ -45,12 +45,9 @@ export function SendButton(props) {
45
45
  async function send() {
46
46
  // Run all command providers
47
47
  await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
48
- // send message through chat model
49
- await (chatModel === null || chatModel === void 0 ? void 0 : chatModel.sendMessage({
50
- body: model.value
51
- }));
52
- // clear input model value & re-focus
48
+ const body = model.value;
53
49
  model.value = '';
50
+ model.send(body);
54
51
  model.focus();
55
52
  }
56
53
  return (React.createElement(TooltippedIconButton, { onClick: send, tooltip: tooltip, disabled: disabled, buttonProps: {
@@ -4,5 +4,6 @@ export * from './message';
4
4
  export * from './message-renderer';
5
5
  export * from './messages';
6
6
  export * from './navigation';
7
+ export * from './preamble';
7
8
  export * from './toolbar';
8
9
  export * from './welcome';
@@ -8,5 +8,6 @@ export * from './message';
8
8
  export * from './message-renderer';
9
9
  export * from './messages';
10
10
  export * from './navigation';
11
+ export * from './preamble';
11
12
  export * from './toolbar';
12
13
  export * from './welcome';
@@ -9,6 +9,7 @@ import React, { useEffect, useState, useRef } from 'react';
9
9
  import { MessageFooterComponent } from './footer';
10
10
  import { ChatMessageHeader } from './header';
11
11
  import { ChatMessage } from './message';
12
+ import { MessagePreambleComponent } from './preamble';
12
13
  import { Navigation } from './navigation';
13
14
  import { WelcomeMessage } from './welcome';
14
15
  import { ScrollContainer } from '../scroll-container';
@@ -22,7 +23,7 @@ const MESSAGE_STACKED_CLASS = 'jp-chat-message-stacked';
22
23
  */
23
24
  export function ChatMessages() {
24
25
  var _a;
25
- const { area, messageFooterRegistry, model, welcomeMessage } = useChatContext();
26
+ const { area, messageFooterRegistry, messagePreambleRegistry, model, welcomeMessage } = useChatContext();
26
27
  const [messages, setMessages] = useState(model.messages);
27
28
  const refMsgBox = useRef(null);
28
29
  const [allRendered, setAllRendered] = useState(false);
@@ -161,6 +162,7 @@ export function ChatMessages() {
161
162
  })
162
163
  }, className: clsx(MESSAGE_CLASS, message.stacked ? MESSAGE_STACKED_CLASS : '') },
163
164
  React.createElement(ChatMessageHeader, { message: message, isCurrentUser: isCurrentUser }),
165
+ messagePreambleRegistry && (React.createElement(MessagePreambleComponent, { message: message })),
164
166
  React.createElement(ChatMessage, { message: message, index: i, renderedPromise: renderedPromise.current[i], ref: el => (listRef.current[i] = el) }),
165
167
  messageFooterRegistry && (React.createElement(MessageFooterComponent, { message: message }))));
166
168
  }))),
@@ -0,0 +1,12 @@
1
+ /// <reference types="react" />
2
+ import { IMessage } from '../../types';
3
+ /**
4
+ * The preamble component properties.
5
+ */
6
+ export interface IMessagePreambleProps {
7
+ message: IMessage;
8
+ }
9
+ /**
10
+ * Renders all registered preamble components vertically above the message body.
11
+ */
12
+ export declare function MessagePreambleComponent(props: IMessagePreambleProps): JSX.Element | null;
@@ -0,0 +1,31 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import React, { useEffect, useState } from 'react';
6
+ import { useChatContext } from '../../context';
7
+ /**
8
+ * Renders all registered preamble components vertically above the message body.
9
+ */
10
+ export function MessagePreambleComponent(props) {
11
+ const [message, setMessage] = useState(props.message.content);
12
+ useEffect(() => {
13
+ function messageChanged() {
14
+ setMessage(props.message.content);
15
+ }
16
+ props.message.changed.connect(messageChanged);
17
+ setMessage(props.message.content);
18
+ return () => {
19
+ props.message.changed.disconnect(messageChanged);
20
+ };
21
+ }, [props.message]);
22
+ const { model, messagePreambleRegistry } = useChatContext();
23
+ if (!messagePreambleRegistry) {
24
+ return null;
25
+ }
26
+ const components = messagePreambleRegistry.getComponents();
27
+ if (!components.length) {
28
+ return null;
29
+ }
30
+ return (React.createElement(React.Fragment, null, components.map((Component, i) => (React.createElement(Component, { key: i, model: model, message: message })))));
31
+ }
package/lib/message.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IRenderMime } from '@jupyterlab/rendermime';
2
2
  import { ISignal } from '@lumino/signaling';
3
- import { IAttachment, IMessageContent, IMessage, IUser } from './types';
3
+ import { IAttachment, IMessageContent, IMessage, IMessageMetadata, IUser } from './types';
4
4
  /**
5
5
  * The message object.
6
6
  */
@@ -29,6 +29,7 @@ export declare class Message implements IMessage {
29
29
  get deleted(): boolean | undefined;
30
30
  get edited(): boolean | undefined;
31
31
  get stacked(): boolean | undefined;
32
+ get metadata(): IMessageMetadata | undefined;
32
33
  /**
33
34
  * A signal emitting when the message has been updated.
34
35
  */
package/lib/message.js CHANGED
@@ -58,6 +58,9 @@ export class Message {
58
58
  get stacked() {
59
59
  return this._content.stacked;
60
60
  }
61
+ get metadata() {
62
+ return this._content.metadata;
63
+ }
61
64
  /**
62
65
  * A signal emitting when the message has been updated.
63
66
  */
@@ -1,3 +1,4 @@
1
1
  export * from './attachment-openers';
2
2
  export * from './chat-commands';
3
3
  export * from './footers';
4
+ export * from './preambles';
@@ -5,3 +5,4 @@
5
5
  export * from './attachment-openers';
6
6
  export * from './chat-commands';
7
7
  export * from './footers';
8
+ export * from './preambles';
@@ -0,0 +1,44 @@
1
+ /// <reference types="react" />
2
+ import { Token } from '@lumino/coreutils';
3
+ import { IChatModel } from '../model';
4
+ import { IMessageContent } from '../types';
5
+ /**
6
+ * The token providing the chat preamble registry.
7
+ */
8
+ export declare const IMessagePreambleRegistry: Token<IMessagePreambleRegistry>;
9
+ /**
10
+ * The props passed to each preamble component.
11
+ */
12
+ export type MessagePreambleProps = {
13
+ model: IChatModel;
14
+ message: IMessageContent;
15
+ };
16
+ /**
17
+ * The interface of a registry to provide message preamble components.
18
+ * Preamble components render above the message body, after the header.
19
+ */
20
+ export interface IMessagePreambleRegistry {
21
+ /**
22
+ * Add a preamble component to the registry.
23
+ * Components are rendered in the order they are added.
24
+ */
25
+ addComponent(component: (props: MessagePreambleProps) => JSX.Element | null): void;
26
+ /**
27
+ * Get all registered preamble components.
28
+ */
29
+ getComponents(): ((props: MessagePreambleProps) => JSX.Element | null)[];
30
+ }
31
+ /**
32
+ * The default implementation of the message preamble registry.
33
+ */
34
+ export declare class MessagePreambleRegistry implements IMessagePreambleRegistry {
35
+ /**
36
+ * Add a preamble component to the registry.
37
+ */
38
+ addComponent(component: (props: MessagePreambleProps) => JSX.Element | null): void;
39
+ /**
40
+ * Get all registered preamble components.
41
+ */
42
+ getComponents(): ((props: MessagePreambleProps) => JSX.Element | null)[];
43
+ private _components;
44
+ }
@@ -0,0 +1,29 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { Token } from '@lumino/coreutils';
6
+ /**
7
+ * The token providing the chat preamble registry.
8
+ */
9
+ export const IMessagePreambleRegistry = new Token('@jupyter/chat:ChatPreambleRegistry');
10
+ /**
11
+ * The default implementation of the message preamble registry.
12
+ */
13
+ export class MessagePreambleRegistry {
14
+ constructor() {
15
+ this._components = [];
16
+ }
17
+ /**
18
+ * Add a preamble component to the registry.
19
+ */
20
+ addComponent(component) {
21
+ this._components.push(component);
22
+ }
23
+ /**
24
+ * Get all registered preamble components.
25
+ */
26
+ getComponents() {
27
+ return [...this._components];
28
+ }
29
+ }
package/lib/types.d.ts CHANGED
@@ -53,6 +53,20 @@ export interface IConfig {
53
53
  */
54
54
  showDeleted?: boolean;
55
55
  }
56
+ /**
57
+ * An empty interface to describe optional metadata attached to a chat message.
58
+ * Extensions can augment this interface to add custom fields:
59
+ *
60
+ * ```ts
61
+ * declare module '@jupyter/chat' {
62
+ * interface IMessageMetadata {
63
+ * myField?: MyType;
64
+ * }
65
+ * }
66
+ * ```
67
+ */
68
+ export interface IMessageMetadata {
69
+ }
56
70
  /**
57
71
  * The chat message description.
58
72
  */
@@ -68,10 +82,8 @@ export type IMessageContent<T = IUser, U = IAttachment> = {
68
82
  deleted?: boolean;
69
83
  edited?: boolean;
70
84
  stacked?: boolean;
85
+ metadata?: IMessageMetadata;
71
86
  };
72
- /**
73
- *
74
- */
75
87
  export interface IMessage extends IMessageContent {
76
88
  /**
77
89
  * Update one or several fields of the message.
@@ -0,0 +1,102 @@
1
+ /// <reference types="react" />
2
+ import { ReactWidget } from '@jupyterlab/ui-components';
3
+ import { Message } from '@lumino/messaging';
4
+ /**
5
+ * A popup widget for selecting a chat from a filtered list.
6
+ */
7
+ export declare class ChatSelectorPopup extends ReactWidget {
8
+ constructor(options: ChatSelectorPopup.IOptions);
9
+ /**
10
+ * Getter/setter of the anchor element.
11
+ */
12
+ get anchor(): HTMLElement | null;
13
+ set anchor(element: HTMLElement | null);
14
+ /**
15
+ * Update the list of available chats.
16
+ */
17
+ updateChats(chatNames: string[]): void;
18
+ /**
19
+ * Update the list of loaded models.
20
+ */
21
+ setLoadedModels(loadedModels: string[]): void;
22
+ /**
23
+ * Set the currently displayed chat.
24
+ */
25
+ setCurrentChat(chatName: string | null): void;
26
+ /**
27
+ * Set the search query and filter the list.
28
+ */
29
+ setQuery(query: string): void;
30
+ /**
31
+ * Get the currently selected chat value.
32
+ */
33
+ getSelectedValue(): string | null;
34
+ /**
35
+ * Move selection down.
36
+ */
37
+ selectNext(): void;
38
+ /**
39
+ * Move selection up.
40
+ */
41
+ selectPrevious(): void;
42
+ /**
43
+ * Show the popup and position it.
44
+ */
45
+ show(): void;
46
+ /**
47
+ * Hide the popup.
48
+ */
49
+ hide(): void;
50
+ render(): JSX.Element;
51
+ protected onAfterShow(msg: Message): void;
52
+ protected onAfterHide(msg: Message): void;
53
+ /**
54
+ * Check if the popup should move (anchor has moved).
55
+ */
56
+ private _checkPosition;
57
+ /**
58
+ * Position the popup below the search element.
59
+ */
60
+ private _positionPopup;
61
+ /**
62
+ * Update the filtered and sorted chats based on current state.
63
+ */
64
+ private _updateFilteredChats;
65
+ private _handleItemClick;
66
+ private _handleUpdateSelectedName;
67
+ private _handleClose;
68
+ private _handleOutsideClick;
69
+ private _chatNames;
70
+ private _loadedModels;
71
+ private _currentChat;
72
+ private _query;
73
+ private _selectedName;
74
+ private _filteredChats;
75
+ private _onSelect?;
76
+ private _onClose?;
77
+ private _anchor;
78
+ private _anchorRect;
79
+ }
80
+ /**
81
+ * Namespace for ChatSelectorPopup.
82
+ */
83
+ export declare namespace ChatSelectorPopup {
84
+ interface IOptions {
85
+ /**
86
+ * Object mapping display names to values used to identify/open chats.
87
+ */
88
+ chatNames: string[];
89
+ /**
90
+ * Callback when a chat is selected.
91
+ */
92
+ onSelect?: (name: string) => void;
93
+ /**
94
+ * Callback when a chat is closed/disposed.
95
+ */
96
+ onClose?: (name: string) => void;
97
+ /**
98
+ * The element to anchor the popup to.
99
+ */
100
+ anchor?: HTMLElement;
101
+ }
102
+ }