@jupyter/chat 0.20.0-alpha.1 → 0.20.0-alpha.2
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__/preamble-registry.spec.d.ts +1 -0
- package/lib/__tests__/preamble-registry.spec.js +37 -0
- package/lib/components/chat.d.ts +5 -1
- package/lib/components/input/buttons/send-button.js +3 -6
- package/lib/components/messages/index.d.ts +1 -0
- package/lib/components/messages/index.js +1 -0
- package/lib/components/messages/messages.js +3 -1
- package/lib/components/messages/preamble.d.ts +12 -0
- package/lib/components/messages/preamble.js +31 -0
- package/lib/message.d.ts +2 -1
- package/lib/message.js +3 -0
- package/lib/registers/index.d.ts +1 -0
- package/lib/registers/index.js +1 -0
- package/lib/registers/preambles.d.ts +44 -0
- package/lib/registers/preambles.js +29 -0
- package/lib/types.d.ts +15 -3
- package/package.json +1 -1
- package/src/__tests__/preamble-registry.spec.ts +51 -0
- package/src/components/chat.tsx +6 -1
- package/src/components/input/buttons/send-button.tsx +4 -6
- package/src/components/messages/index.ts +1 -0
- package/src/components/messages/messages.tsx +11 -2
- package/src/components/messages/preamble.tsx +55 -0
- package/src/message.ts +10 -1
- package/src/registers/index.ts +1 -0
- package/src/registers/preambles.ts +65 -0
- package/src/types.ts +15 -3
|
@@ -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
|
+
});
|
package/lib/components/chat.d.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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: {
|
|
@@ -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
package/lib/registers/index.d.ts
CHANGED
package/lib/registers/index.js
CHANGED
|
@@ -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.
|
package/package.json
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MessagePreambleRegistry,
|
|
8
|
+
MessagePreambleProps
|
|
9
|
+
} from '../registers/preambles';
|
|
10
|
+
|
|
11
|
+
describe('MessagePreambleRegistry', () => {
|
|
12
|
+
let registry: MessagePreambleRegistry;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
registry = new MessagePreambleRegistry();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should start with no components', () => {
|
|
19
|
+
expect(registry.getComponents()).toEqual([]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should add a component', () => {
|
|
23
|
+
const component: (props: MessagePreambleProps) => JSX.Element | null = () =>
|
|
24
|
+
null;
|
|
25
|
+
registry.addComponent(component);
|
|
26
|
+
expect(registry.getComponents()).toHaveLength(1);
|
|
27
|
+
expect(registry.getComponents()[0]).toBe(component);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should preserve insertion order', () => {
|
|
31
|
+
const first: (props: MessagePreambleProps) => JSX.Element | null = () =>
|
|
32
|
+
null;
|
|
33
|
+
const second: (props: MessagePreambleProps) => JSX.Element | null = () =>
|
|
34
|
+
null;
|
|
35
|
+
registry.addComponent(first);
|
|
36
|
+
registry.addComponent(second);
|
|
37
|
+
const components = registry.getComponents();
|
|
38
|
+
expect(components).toHaveLength(2);
|
|
39
|
+
expect(components[0]).toBe(first);
|
|
40
|
+
expect(components[1]).toBe(second);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return a copy from getComponents', () => {
|
|
44
|
+
const component: (props: MessagePreambleProps) => JSX.Element | null = () =>
|
|
45
|
+
null;
|
|
46
|
+
registry.addComponent(component);
|
|
47
|
+
const result = registry.getComponents();
|
|
48
|
+
result.push(() => null);
|
|
49
|
+
expect(registry.getComponents()).toHaveLength(1);
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/components/chat.tsx
CHANGED
|
@@ -24,7 +24,8 @@ import { IChatModel } from '../model';
|
|
|
24
24
|
import {
|
|
25
25
|
IAttachmentOpenerRegistry,
|
|
26
26
|
IChatCommandRegistry,
|
|
27
|
-
IMessageFooterRegistry
|
|
27
|
+
IMessageFooterRegistry,
|
|
28
|
+
IMessagePreambleRegistry
|
|
28
29
|
} from '../registers';
|
|
29
30
|
import { ChatArea } from '../types';
|
|
30
31
|
|
|
@@ -168,6 +169,10 @@ export namespace Chat {
|
|
|
168
169
|
* The footer registry.
|
|
169
170
|
*/
|
|
170
171
|
messageFooterRegistry?: IMessageFooterRegistry;
|
|
172
|
+
/**
|
|
173
|
+
* The preamble registry for content above message body.
|
|
174
|
+
*/
|
|
175
|
+
messagePreambleRegistry?: IMessagePreambleRegistry;
|
|
171
176
|
/**
|
|
172
177
|
* The welcome message.
|
|
173
178
|
*/
|
|
@@ -18,7 +18,7 @@ const SEND_BUTTON_CLASS = 'jp-chat-send-button';
|
|
|
18
18
|
export function SendButton(
|
|
19
19
|
props: InputToolbarRegistry.IToolbarItemProps
|
|
20
20
|
): JSX.Element {
|
|
21
|
-
const { model,
|
|
21
|
+
const { model, chatCommandRegistry, edit } = props;
|
|
22
22
|
|
|
23
23
|
// Don't show this button when in edit mode
|
|
24
24
|
if (edit) {
|
|
@@ -62,12 +62,10 @@ export function SendButton(
|
|
|
62
62
|
// Run all command providers
|
|
63
63
|
await chatCommandRegistry?.onSubmit(model);
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
body: model.value
|
|
68
|
-
});
|
|
69
|
-
// clear input model value & re-focus
|
|
65
|
+
const body = model.value;
|
|
66
|
+
|
|
70
67
|
model.value = '';
|
|
68
|
+
model.send(body);
|
|
71
69
|
model.focus();
|
|
72
70
|
}
|
|
73
71
|
|
|
@@ -11,6 +11,7 @@ import React, { useEffect, useState, useRef } from 'react';
|
|
|
11
11
|
import { MessageFooterComponent } from './footer';
|
|
12
12
|
import { ChatMessageHeader } from './header';
|
|
13
13
|
import { ChatMessage } from './message';
|
|
14
|
+
import { MessagePreambleComponent } from './preamble';
|
|
14
15
|
import { Navigation } from './navigation';
|
|
15
16
|
import { WelcomeMessage } from './welcome';
|
|
16
17
|
import { ScrollContainer } from '../scroll-container';
|
|
@@ -27,8 +28,13 @@ const MESSAGE_STACKED_CLASS = 'jp-chat-message-stacked';
|
|
|
27
28
|
* The messages list component.
|
|
28
29
|
*/
|
|
29
30
|
export function ChatMessages(): JSX.Element {
|
|
30
|
-
const {
|
|
31
|
-
|
|
31
|
+
const {
|
|
32
|
+
area,
|
|
33
|
+
messageFooterRegistry,
|
|
34
|
+
messagePreambleRegistry,
|
|
35
|
+
model,
|
|
36
|
+
welcomeMessage
|
|
37
|
+
} = useChatContext();
|
|
32
38
|
|
|
33
39
|
const [messages, setMessages] = useState<IMessage[]>(model.messages);
|
|
34
40
|
const refMsgBox = useRef<HTMLDivElement>(null);
|
|
@@ -203,6 +209,9 @@ export function ChatMessages(): JSX.Element {
|
|
|
203
209
|
message={message}
|
|
204
210
|
isCurrentUser={isCurrentUser}
|
|
205
211
|
/>
|
|
212
|
+
{messagePreambleRegistry && (
|
|
213
|
+
<MessagePreambleComponent message={message} />
|
|
214
|
+
)}
|
|
206
215
|
<ChatMessage
|
|
207
216
|
message={message}
|
|
208
217
|
index={i}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { useChatContext } from '../../context';
|
|
9
|
+
import { IMessage, IMessageContent } from '../../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The preamble component properties.
|
|
13
|
+
*/
|
|
14
|
+
export interface IMessagePreambleProps {
|
|
15
|
+
message: IMessage;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Renders all registered preamble components vertically above the message body.
|
|
20
|
+
*/
|
|
21
|
+
export function MessagePreambleComponent(
|
|
22
|
+
props: IMessagePreambleProps
|
|
23
|
+
): JSX.Element | null {
|
|
24
|
+
const [message, setMessage] = useState<IMessageContent>(
|
|
25
|
+
props.message.content
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
function messageChanged() {
|
|
30
|
+
setMessage(props.message.content);
|
|
31
|
+
}
|
|
32
|
+
props.message.changed.connect(messageChanged);
|
|
33
|
+
setMessage(props.message.content);
|
|
34
|
+
return () => {
|
|
35
|
+
props.message.changed.disconnect(messageChanged);
|
|
36
|
+
};
|
|
37
|
+
}, [props.message]);
|
|
38
|
+
|
|
39
|
+
const { model, messagePreambleRegistry } = useChatContext();
|
|
40
|
+
if (!messagePreambleRegistry) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const components = messagePreambleRegistry.getComponents();
|
|
44
|
+
if (!components.length) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{components.map((Component, i) => (
|
|
51
|
+
<Component key={i} model={model} message={message} />
|
|
52
|
+
))}
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
}
|
package/src/message.ts
CHANGED
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
|
|
6
6
|
import { IRenderMime } from '@jupyterlab/rendermime';
|
|
7
7
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
IAttachment,
|
|
10
|
+
IMessageContent,
|
|
11
|
+
IMessage,
|
|
12
|
+
IMessageMetadata,
|
|
13
|
+
IUser
|
|
14
|
+
} from './types';
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* The message object.
|
|
@@ -65,6 +71,9 @@ export class Message implements IMessage {
|
|
|
65
71
|
get stacked(): boolean | undefined {
|
|
66
72
|
return this._content.stacked;
|
|
67
73
|
}
|
|
74
|
+
get metadata(): IMessageMetadata | undefined {
|
|
75
|
+
return this._content.metadata;
|
|
76
|
+
}
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
79
|
* A signal emitting when the message has been updated.
|
package/src/registers/index.ts
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Token } from '@lumino/coreutils';
|
|
7
|
+
import { IChatModel } from '../model';
|
|
8
|
+
import { IMessageContent } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The token providing the chat preamble registry.
|
|
12
|
+
*/
|
|
13
|
+
export const IMessagePreambleRegistry = new Token<IMessagePreambleRegistry>(
|
|
14
|
+
'@jupyter/chat:ChatPreambleRegistry'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The props passed to each preamble component.
|
|
19
|
+
*/
|
|
20
|
+
export type MessagePreambleProps = {
|
|
21
|
+
model: IChatModel;
|
|
22
|
+
message: IMessageContent;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The interface of a registry to provide message preamble components.
|
|
27
|
+
* Preamble components render above the message body, after the header.
|
|
28
|
+
*/
|
|
29
|
+
export interface IMessagePreambleRegistry {
|
|
30
|
+
/**
|
|
31
|
+
* Add a preamble component to the registry.
|
|
32
|
+
* Components are rendered in the order they are added.
|
|
33
|
+
*/
|
|
34
|
+
addComponent(
|
|
35
|
+
component: (props: MessagePreambleProps) => JSX.Element | null
|
|
36
|
+
): void;
|
|
37
|
+
/**
|
|
38
|
+
* Get all registered preamble components.
|
|
39
|
+
*/
|
|
40
|
+
getComponents(): ((props: MessagePreambleProps) => JSX.Element | null)[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The default implementation of the message preamble registry.
|
|
45
|
+
*/
|
|
46
|
+
export class MessagePreambleRegistry implements IMessagePreambleRegistry {
|
|
47
|
+
/**
|
|
48
|
+
* Add a preamble component to the registry.
|
|
49
|
+
*/
|
|
50
|
+
addComponent(
|
|
51
|
+
component: (props: MessagePreambleProps) => JSX.Element | null
|
|
52
|
+
): void {
|
|
53
|
+
this._components.push(component);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get all registered preamble components.
|
|
58
|
+
*/
|
|
59
|
+
getComponents(): ((props: MessagePreambleProps) => JSX.Element | null)[] {
|
|
60
|
+
return [...this._components];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private _components: ((props: MessagePreambleProps) => JSX.Element | null)[] =
|
|
64
|
+
[];
|
|
65
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -61,6 +61,20 @@ export interface IConfig {
|
|
|
61
61
|
showDeleted?: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* An empty interface to describe optional metadata attached to a chat message.
|
|
66
|
+
* Extensions can augment this interface to add custom fields:
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* declare module '@jupyter/chat' {
|
|
70
|
+
* interface IMessageMetadata {
|
|
71
|
+
* myField?: MyType;
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export interface IMessageMetadata {} /* eslint-disable-line @typescript-eslint/no-empty-object-type */
|
|
77
|
+
|
|
64
78
|
/**
|
|
65
79
|
* The chat message description.
|
|
66
80
|
*/
|
|
@@ -79,11 +93,9 @@ export type IMessageContent<T = IUser, U = IAttachment> = {
|
|
|
79
93
|
deleted?: boolean;
|
|
80
94
|
edited?: boolean;
|
|
81
95
|
stacked?: boolean;
|
|
96
|
+
metadata?: IMessageMetadata;
|
|
82
97
|
};
|
|
83
98
|
|
|
84
|
-
/**
|
|
85
|
-
*
|
|
86
|
-
*/
|
|
87
99
|
export interface IMessage extends IMessageContent {
|
|
88
100
|
/**
|
|
89
101
|
* Update one or several fields of the message.
|