@jupyter/chat 0.19.0-alpha.3 → 0.19.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 (41) hide show
  1. package/lib/__tests__/model.spec.js +2 -2
  2. package/lib/components/chat.js +29 -2
  3. package/lib/components/index.d.ts +1 -0
  4. package/lib/components/index.js +1 -0
  5. package/lib/components/input/chat-input.js +1 -25
  6. package/lib/components/input/index.d.ts +0 -1
  7. package/lib/components/input/index.js +0 -1
  8. package/lib/components/messages/footer.d.ts +2 -2
  9. package/lib/components/messages/header.d.ts +2 -2
  10. package/lib/components/messages/header.js +13 -6
  11. package/lib/components/messages/message.d.ts +2 -2
  12. package/lib/components/messages/message.js +16 -1
  13. package/lib/components/messages/messages.js +3 -2
  14. package/lib/components/messages/toolbar.js +6 -14
  15. package/lib/components/writing-indicator.d.ts +20 -0
  16. package/lib/components/{input/writing-indicator.js → writing-indicator.js} +3 -2
  17. package/lib/message.d.ts +41 -0
  18. package/lib/message.js +74 -0
  19. package/lib/model.d.ts +13 -13
  20. package/lib/model.js +9 -7
  21. package/lib/registers/footers.d.ts +2 -2
  22. package/lib/types.d.ts +21 -3
  23. package/lib/widgets/chat-widget.js +18 -11
  24. package/package.json +1 -1
  25. package/src/__tests__/model.spec.ts +7 -7
  26. package/src/components/chat.tsx +35 -1
  27. package/src/components/index.ts +1 -0
  28. package/src/components/input/chat-input.tsx +0 -28
  29. package/src/components/input/index.ts +0 -1
  30. package/src/components/messages/footer.tsx +2 -2
  31. package/src/components/messages/header.tsx +20 -8
  32. package/src/components/messages/message.tsx +23 -3
  33. package/src/components/messages/messages.tsx +9 -4
  34. package/src/components/messages/toolbar.tsx +14 -14
  35. package/src/components/{input/writing-indicator.tsx → writing-indicator.tsx} +9 -4
  36. package/src/message.ts +83 -0
  37. package/src/model.ts +25 -22
  38. package/src/registers/footers.ts +2 -2
  39. package/src/types.ts +23 -3
  40. package/src/widgets/chat-widget.tsx +22 -14
  41. package/lib/components/input/writing-indicator.d.ts +0 -15
@@ -52,7 +52,7 @@ describe('test chat model', () => {
52
52
  });
53
53
  model.messageAdded(msg);
54
54
  expect(messages).toHaveLength(1);
55
- expect(messages[0]).toBe(msg);
55
+ expect(messages[0].content).toBe(msg);
56
56
  });
57
57
  it('should format message', () => {
58
58
  model = new TestChat();
@@ -62,7 +62,7 @@ describe('test chat model', () => {
62
62
  });
63
63
  model.messageAdded({ ...msg });
64
64
  expect(messages).toHaveLength(1);
65
- expect(messages[0]).not.toBe(msg);
65
+ expect(messages[0].content).not.toBe(msg);
66
66
  expect(messages[0].body).toBe('formatted msg');
67
67
  });
68
68
  });
@@ -6,17 +6,40 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
6
6
  import SettingsIcon from '@mui/icons-material/Settings';
7
7
  import { IconButton } from '@mui/material';
8
8
  import { Box } from '@mui/system';
9
- import React, { useState } from 'react';
9
+ import React, { useEffect, useState } from 'react';
10
10
  import { ChatInput, InputToolbarRegistry } from './input';
11
11
  import { JlThemeProvider } from './jl-theme-provider';
12
12
  import { ChatMessages } from './messages';
13
+ import { WritingIndicator } from './writing-indicator';
13
14
  import { ChatReactContext } from '../context';
14
15
  export function ChatBody(props) {
15
16
  const { model } = props;
17
+ const [writers, setWriters] = useState([]);
16
18
  let { inputToolbarRegistry } = props;
17
19
  if (!inputToolbarRegistry) {
18
20
  inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
19
21
  }
22
+ /**
23
+ * Handle the changes in the writers list.
24
+ */
25
+ useEffect(() => {
26
+ var _a;
27
+ if (!model) {
28
+ return;
29
+ }
30
+ const updateWriters = (_, writers) => {
31
+ // Show all writers for now - AI generating responses will have messageID
32
+ setWriters(writers);
33
+ };
34
+ // Set initial writers state
35
+ const initialWriters = model.writers;
36
+ setWriters(initialWriters);
37
+ (_a = model.writersChanged) === null || _a === void 0 ? void 0 : _a.connect(updateWriters);
38
+ return () => {
39
+ var _a;
40
+ (_a = model === null || model === void 0 ? void 0 : model.writersChanged) === null || _a === void 0 ? void 0 : _a.disconnect(updateWriters);
41
+ };
42
+ }, [model]);
20
43
  // const horizontalPadding = props.area === 'main' ? 8 : 4;
21
44
  const horizontalPadding = 4;
22
45
  const contextValue = {
@@ -30,7 +53,11 @@ export function ChatBody(props) {
30
53
  paddingRight: horizontalPadding,
31
54
  paddingTop: 0,
32
55
  paddingBottom: 0
33
- }, model: model.input })));
56
+ }, model: model.input }),
57
+ React.createElement(WritingIndicator, { sx: {
58
+ paddingLeft: horizontalPadding,
59
+ paddingRight: horizontalPadding
60
+ }, writers: writers })));
34
61
  }
35
62
  export function Chat(props) {
36
63
  var _a;
@@ -6,3 +6,4 @@ export * from './jl-theme-provider';
6
6
  export * from './messages';
7
7
  export * from './mui-extras';
8
8
  export * from './scroll-container';
9
+ export * from './writing-indicator';
@@ -10,3 +10,4 @@ export * from './jl-theme-provider';
10
10
  export * from './messages';
11
11
  export * from './mui-extras';
12
12
  export * from './scroll-container';
13
+ export * from './writing-indicator';
@@ -8,7 +8,6 @@ import React, { useEffect, useRef, useState } from 'react';
8
8
  import { useChatCommands } from './use-chat-commands';
9
9
  import { AttachmentPreviewList } from '../attachments';
10
10
  import { useChatContext } from '../../context';
11
- import { InputWritingIndicator } from './writing-indicator';
12
11
  const INPUT_BOX_CLASS = 'jp-chat-input-container';
13
12
  const INPUT_TEXTFIELD_CLASS = 'jp-chat-input-textfield';
14
13
  const INPUT_TOOLBAR_CLASS = 'jp-chat-input-toolbar';
@@ -23,7 +22,6 @@ export function ChatInput(props) {
23
22
  const [sendWithShiftEnter, setSendWithShiftEnter] = useState((_a = model.config.sendWithShiftEnter) !== null && _a !== void 0 ? _a : false);
24
23
  const [attachments, setAttachments] = useState(model.attachments);
25
24
  const [toolbarElements, setToolbarElements] = useState([]);
26
- const [writers, setWriters] = useState([]);
27
25
  /**
28
26
  * Auto-focus the input when the component is first mounted.
29
27
  */
@@ -79,27 +77,6 @@ export function ChatInput(props) {
79
77
  inputToolbarRegistry === null || inputToolbarRegistry === void 0 ? void 0 : inputToolbarRegistry.itemsChanged.disconnect(updateToolbar);
80
78
  };
81
79
  }, [inputToolbarRegistry]);
82
- /**
83
- * Handle the changes in the writers list.
84
- */
85
- useEffect(() => {
86
- var _a;
87
- if (!chatModel) {
88
- return;
89
- }
90
- const updateWriters = (_, writers) => {
91
- // Show all writers for now - AI generating responses will have messageID
92
- setWriters(writers);
93
- };
94
- // Set initial writers state
95
- const initialWriters = chatModel.writers;
96
- setWriters(initialWriters);
97
- (_a = chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.connect(updateWriters);
98
- return () => {
99
- var _a;
100
- (_a = chatModel === null || chatModel === void 0 ? void 0 : chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.disconnect(updateWriters);
101
- };
102
- }, [chatModel]);
103
80
  const inputExists = !!input.trim();
104
81
  /**
105
82
  * `handleKeyDown()`: callback invoked when the user presses any key in the
@@ -232,6 +209,5 @@ export function ChatInput(props) {
232
209
  borderColor: 'var(--jp-border-color1)',
233
210
  backgroundColor: 'var(--jp-layout-color0)',
234
211
  transition: 'background-color 0.2s ease'
235
- } }, toolbarElements.map((item, index) => (React.createElement(item.element, { key: index, model: model, chatCommandRegistry: chatCommandRegistry, chatModel: chatModel, edit: props.edit }))))),
236
- React.createElement(InputWritingIndicator, { writers: writers })));
212
+ } }, toolbarElements.map((item, index) => (React.createElement(item.element, { key: index, model: model, chatCommandRegistry: chatCommandRegistry, chatModel: chatModel, edit: props.edit })))))));
237
213
  }
@@ -2,4 +2,3 @@ export * from './buttons';
2
2
  export * from './chat-input';
3
3
  export * from './toolbar-registry';
4
4
  export * from './use-chat-commands';
5
- export * from './writing-indicator';
@@ -6,4 +6,3 @@ export * from './buttons';
6
6
  export * from './chat-input';
7
7
  export * from './toolbar-registry';
8
8
  export * from './use-chat-commands';
9
- export * from './writing-indicator';
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { IChatMessage } from '../../types';
2
+ import { IMessageContent } from '../../types';
3
3
  /**
4
4
  * The chat footer component properties.
5
5
  */
@@ -7,7 +7,7 @@ export interface IMessageFootersProps {
7
7
  /**
8
8
  * The chat model.
9
9
  */
10
- message: IChatMessage;
10
+ message: IMessageContent;
11
11
  }
12
12
  /**
13
13
  * The chat footer component, which displays footer components on a row according to
@@ -1,5 +1,5 @@
1
1
  /// <reference types="react" />
2
- import { IChatMessage } from '../../types';
2
+ import { IMessage } from '../../types';
3
3
  /**
4
4
  * The message header props.
5
5
  */
@@ -7,7 +7,7 @@ type ChatMessageHeaderProps = {
7
7
  /**
8
8
  * The chat message.
9
9
  */
10
- message: IChatMessage;
10
+ message: IMessage;
11
11
  /**
12
12
  * Whether this message is from the current user.
13
13
  */
@@ -12,11 +12,7 @@ const MESSAGE_TIME_CLASS = 'jp-chat-message-time';
12
12
  */
13
13
  export function ChatMessageHeader(props) {
14
14
  var _a, _b;
15
- const message = props.message;
16
- // Don't render header for stacked messages not deleted or edited.
17
- if (message.stacked && !message.deleted && !message.edited) {
18
- return React.createElement(React.Fragment, null);
19
- }
15
+ const [message, setMessage] = useState(props.message.content);
20
16
  // Flag to display only the deleted or edited information (stacked message).
21
17
  const onlyState = message.stacked && (message.deleted || message.edited);
22
18
  const [datetime, setDatetime] = useState({});
@@ -55,9 +51,20 @@ export function ChatMessageHeader(props) {
55
51
  setDatetime(newDatetime);
56
52
  }
57
53
  });
54
+ // Listen for changes in the current message.
55
+ useEffect(() => {
56
+ function messageChanged() {
57
+ setMessage(props.message.content);
58
+ }
59
+ props.message.changed.connect(messageChanged);
60
+ return () => {
61
+ props.message.changed.disconnect(messageChanged);
62
+ };
63
+ }, [props.message]);
58
64
  const avatar = message.stacked ? null : Avatar({ user: sender });
59
65
  const name = (_b = (_a = sender.display_name) !== null && _a !== void 0 ? _a : sender.name) !== null && _b !== void 0 ? _b : (sender.username || 'User undefined');
60
- return (React.createElement(Box, { className: MESSAGE_HEADER_CLASS, sx: {
66
+ // Don't render header for stacked messages not deleted or edited.
67
+ return message.stacked && !message.deleted && !message.edited ? (React.createElement(React.Fragment, null)) : (React.createElement(Box, { className: MESSAGE_HEADER_CLASS, sx: {
61
68
  display: 'flex',
62
69
  alignItems: 'center',
63
70
  '& > :not(:last-child)': {
@@ -1,6 +1,6 @@
1
1
  import { PromiseDelegate } from '@lumino/coreutils';
2
2
  import React from 'react';
3
- import { IChatMessage } from '../../types';
3
+ import { IMessage } from '../../types';
4
4
  /**
5
5
  * The message component props.
6
6
  */
@@ -8,7 +8,7 @@ type ChatMessageProps = {
8
8
  /**
9
9
  * The message to display.
10
10
  */
11
- message: IChatMessage;
11
+ message: IMessage;
12
12
  /**
13
13
  * The index of the message in the list.
14
14
  */
@@ -13,8 +13,8 @@ import { replaceSpanToMention } from '../../utils';
13
13
  * The message component body.
14
14
  */
15
15
  export const ChatMessage = forwardRef((props, ref) => {
16
- const { message } = props;
17
16
  const { model } = useChatContext();
17
+ const [message, setMessage] = useState(props.message.content);
18
18
  const [edit, setEdit] = useState(false);
19
19
  const [deleted, setDeleted] = useState(false);
20
20
  const [canEdit, setCanEdit] = useState(false);
@@ -39,6 +39,21 @@ export const ChatMessage = forwardRef((props, ref) => {
39
39
  setCanDelete(false);
40
40
  }
41
41
  }, [model, message]);
42
+ // Listen for changes in the current message.
43
+ useEffect(() => {
44
+ function messageChanged() {
45
+ setMessage(props.message.content);
46
+ }
47
+ props.message.changed.connect(messageChanged);
48
+ // Initialize the message when the message is re-rendered.
49
+ // FIX ? This seems to be required for outofband change, to get the new value,
50
+ // even if when an outofband change occurs, all the messages are deleted and
51
+ // recreated.
52
+ setMessage(props.message.content);
53
+ return () => {
54
+ props.message.changed.disconnect(messageChanged);
55
+ };
56
+ }, [props.message]);
42
57
  // Create an input model only if the message is edited.
43
58
  const startEdition = () => {
44
59
  var _a;
@@ -13,6 +13,7 @@ import { Navigation } from './navigation';
13
13
  import { WelcomeMessage } from './welcome';
14
14
  import { ScrollContainer } from '../scroll-container';
15
15
  import { useChatContext } from '../../context';
16
+ import { Message } from '../../message';
16
17
  export const MESSAGE_CLASS = 'jp-chat-message';
17
18
  const MESSAGES_BOX_CLASS = 'jp-chat-messages-container';
18
19
  const MESSAGE_STACKED_CLASS = 'jp-chat-message-stacked';
@@ -39,7 +40,7 @@ export function ChatMessages() {
39
40
  }
40
41
  model
41
42
  .getHistory()
42
- .then(history => setMessages(history.messages))
43
+ .then(history => setMessages(history.messages.map(message => new Message({ ...message }))))
43
44
  .catch(e => console.error(e));
44
45
  }
45
46
  fetchHistory();
@@ -150,7 +151,7 @@ export function ChatMessages() {
150
151
  model.user.username === message.sender.username;
151
152
  return (
152
153
  // extra div needed to ensure each bubble is on a new line
153
- React.createElement(Box, { key: i, sx: {
154
+ React.createElement(Box, { key: message.id, sx: {
154
155
  ...(isCurrentUser && {
155
156
  marginLeft: area === 'main' ? '25%' : '10%',
156
157
  backgroundColor: 'var(--jp-layout-color2)',
@@ -2,8 +2,8 @@
2
2
  * Copyright (c) Jupyter Development Team.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- // import EditIcon from '@mui/icons-material/Edit';
6
5
  import DeleteIcon from '@mui/icons-material/Delete';
6
+ import EditIcon from '@mui/icons-material/Edit';
7
7
  import { Box } from '@mui/material';
8
8
  import React from 'react';
9
9
  import { TooltippedIconButton } from '../mui-extras';
@@ -13,19 +13,11 @@ const TOOLBAR_CLASS = 'jp-chat-toolbar';
13
13
  */
14
14
  export function MessageToolbar(props) {
15
15
  const buttons = [];
16
- // if (props.edit !== undefined) {
17
- // const editButton = (
18
- // <TooltippedIconButton
19
- // tooltip={'edit'}
20
- // onClick={props.edit}
21
- // aria-label={'Edit'}
22
- // inputToolbar={false}
23
- // >
24
- // <EditIcon />
25
- // </TooltippedIconButton>
26
- // );
27
- // buttons.push(editButton);
28
- // }
16
+ if (props.edit !== undefined) {
17
+ const editButton = (React.createElement(TooltippedIconButton, { tooltip: 'edit', onClick: props.edit, "aria-label": 'Edit', inputToolbar: false },
18
+ React.createElement(EditIcon, null)));
19
+ buttons.push(editButton);
20
+ }
29
21
  if (props.delete !== undefined) {
30
22
  const deleteButton = (React.createElement(TooltippedIconButton, { tooltip: 'Delete', onClick: props.delete, "aria-label": 'Delete', inputToolbar: false },
31
23
  React.createElement(DeleteIcon, null)));
@@ -0,0 +1,20 @@
1
+ /// <reference types="react" />
2
+ import { SxProps, Theme } from '@mui/material';
3
+ import { IChatModel } from '../model';
4
+ /**
5
+ * The input writing indicator component props.
6
+ */
7
+ export interface IInputWritingIndicatorProps {
8
+ /**
9
+ * The list of users currently writing.
10
+ */
11
+ writers: IChatModel.IWriter[];
12
+ /**
13
+ * Custom mui/material styles.
14
+ */
15
+ sx?: SxProps<Theme>;
16
+ }
17
+ /**
18
+ * The writing indicator component, displaying typing status.
19
+ */
20
+ export declare function WritingIndicator(props: IInputWritingIndicatorProps): JSX.Element;
@@ -30,13 +30,14 @@ function formatWritersText(writers) {
30
30
  }
31
31
  }
32
32
  /**
33
- * The input writing indicator component, displaying typing status in the chat input area.
33
+ * The writing indicator component, displaying typing status.
34
34
  */
35
- export function InputWritingIndicator(props) {
35
+ export function WritingIndicator(props) {
36
36
  const { writers } = props;
37
37
  // Always render the container to reserve space, even if no writers
38
38
  const writersText = writers.length > 0 ? formatWritersText(writers) : '';
39
39
  return (React.createElement(Box, { className: WRITERS_ELEMENT_CLASSNAME, sx: {
40
+ ...props.sx,
40
41
  minHeight: '16px'
41
42
  } },
42
43
  React.createElement(Typography, { variant: "caption", sx: {
@@ -0,0 +1,41 @@
1
+ import { ISignal } from '@lumino/signaling';
2
+ import { IAttachment, IMessageContent, IMessage, IUser } from './types';
3
+ /**
4
+ * The message object.
5
+ */
6
+ export declare class Message implements IMessage {
7
+ /**
8
+ * The constructor of the message.
9
+ *
10
+ * @param content: the content of the message.
11
+ */
12
+ constructor(content: IMessageContent);
13
+ /**
14
+ * The message content.
15
+ */
16
+ get content(): IMessageContent;
17
+ /**
18
+ * Getters for each attribute individually.
19
+ */
20
+ get type(): string;
21
+ get body(): string;
22
+ get id(): string;
23
+ get time(): number;
24
+ get sender(): IUser;
25
+ get attachments(): IAttachment[] | undefined;
26
+ get mentions(): IUser[] | undefined;
27
+ get raw_time(): boolean | undefined;
28
+ get deleted(): boolean | undefined;
29
+ get edited(): boolean | undefined;
30
+ get stacked(): boolean | undefined;
31
+ /**
32
+ * A signal emitting when the message has been updated.
33
+ */
34
+ get changed(): ISignal<IMessage, void>;
35
+ /**
36
+ * Update one or several fields of the message.
37
+ */
38
+ update(updated: Partial<IMessageContent>): void;
39
+ private _content;
40
+ private _changed;
41
+ }
package/lib/message.js ADDED
@@ -0,0 +1,74 @@
1
+ /*
2
+ * Copyright (c) Jupyter Development Team.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { Signal } from '@lumino/signaling';
6
+ /**
7
+ * The message object.
8
+ */
9
+ export class Message {
10
+ /**
11
+ * The constructor of the message.
12
+ *
13
+ * @param content: the content of the message.
14
+ */
15
+ constructor(content) {
16
+ this._changed = new Signal(this);
17
+ this._content = content;
18
+ }
19
+ /**
20
+ * The message content.
21
+ */
22
+ get content() {
23
+ return this._content;
24
+ }
25
+ /**
26
+ * Getters for each attribute individually.
27
+ */
28
+ get type() {
29
+ return this._content.type;
30
+ }
31
+ get body() {
32
+ return this._content.body;
33
+ }
34
+ get id() {
35
+ return this._content.id;
36
+ }
37
+ get time() {
38
+ return this._content.time;
39
+ }
40
+ get sender() {
41
+ return this._content.sender;
42
+ }
43
+ get attachments() {
44
+ return this._content.attachments;
45
+ }
46
+ get mentions() {
47
+ return this._content.mentions;
48
+ }
49
+ get raw_time() {
50
+ return this._content.raw_time;
51
+ }
52
+ get deleted() {
53
+ return this._content.deleted;
54
+ }
55
+ get edited() {
56
+ return this._content.edited;
57
+ }
58
+ get stacked() {
59
+ return this._content.stacked;
60
+ }
61
+ /**
62
+ * A signal emitting when the message has been updated.
63
+ */
64
+ get changed() {
65
+ return this._changed;
66
+ }
67
+ /**
68
+ * Update one or several fields of the message.
69
+ */
70
+ update(updated) {
71
+ this._content = { ...this._content, ...updated };
72
+ this._changed.emit();
73
+ }
74
+ }
package/lib/model.d.ts CHANGED
@@ -5,7 +5,7 @@ import { ISignal } from '@lumino/signaling';
5
5
  import { IActiveCellManager } from './active-cell-manager';
6
6
  import { IInputModel } from './input-model';
7
7
  import { ISelectionWatcher } from './selection-watcher';
8
- import { IChatHistory, INewMessage, IChatMessage, IConfig, IUser } from './types';
8
+ import { IChatHistory, IConfig, IMessage, IMessageContent, INewMessage, IUser } from './types';
9
9
  /**
10
10
  * The chat model interface.
11
11
  */
@@ -37,7 +37,7 @@ export interface IChatModel extends IDisposable {
37
37
  /**
38
38
  * The chat messages list.
39
39
  */
40
- readonly messages: IChatMessage[];
40
+ readonly messages: IMessage[];
41
41
  /**
42
42
  * The input model.
43
43
  */
@@ -100,7 +100,7 @@ export interface IChatModel extends IDisposable {
100
100
  * @param id - the unique ID of the message.
101
101
  * @param message - the updated message.
102
102
  */
103
- updateMessage?(id: string, message: IChatMessage): Promise<boolean | void> | boolean | void;
103
+ updateMessage?(id: string, message: IMessageContent): Promise<boolean | void> | boolean | void;
104
104
  /**
105
105
  * Optional, to delete a message from the chat.
106
106
  *
@@ -124,14 +124,14 @@ export interface IChatModel extends IDisposable {
124
124
  *
125
125
  * @param message - the message with user information and body.
126
126
  */
127
- messageAdded(message: IChatMessage): void;
127
+ messageAdded(message: IMessageContent): void;
128
128
  /**
129
129
  * Function called when messages are inserted.
130
130
  *
131
131
  * @param index - the index of the first message of the list.
132
132
  * @param messages - the messages list.
133
133
  */
134
- messagesInserted(index: number, messages: IChatMessage[]): void;
134
+ messagesInserted(index: number, messages: IMessageContent[]): void;
135
135
  /**
136
136
  * Function called when messages are deleted.
137
137
  *
@@ -185,7 +185,7 @@ export declare abstract class AbstractChatModel implements IChatModel {
185
185
  /**
186
186
  * The chat messages list.
187
187
  */
188
- get messages(): IChatMessage[];
188
+ get messages(): IMessage[];
189
189
  /**
190
190
  * The input model.
191
191
  */
@@ -235,7 +235,7 @@ export declare abstract class AbstractChatModel implements IChatModel {
235
235
  get messagesInViewport(): number[];
236
236
  set messagesInViewport(values: number[]);
237
237
  /**
238
- * A signal emitting when the messages list is updated.
238
+ * A signal emitting when the message list is updated.
239
239
  */
240
240
  get messagesUpdated(): ISignal<IChatModel, void>;
241
241
  /**
@@ -282,20 +282,20 @@ export declare abstract class AbstractChatModel implements IChatModel {
282
282
  * A function called before transferring the message to the panel(s).
283
283
  * Can be useful if some actions are required on the message.
284
284
  */
285
- protected formatChatMessage(message: IChatMessage): IChatMessage;
285
+ protected formatChatMessage(message: IMessageContent): IMessageContent;
286
286
  /**
287
287
  * Function to call when a message is received.
288
288
  *
289
289
  * @param message - the message with user information and body.
290
290
  */
291
- messageAdded(message: IChatMessage): void;
291
+ messageAdded(message: IMessageContent): void;
292
292
  /**
293
293
  * Function called when messages are inserted.
294
294
  *
295
295
  * @param index - the index of the first message of the list.
296
296
  * @param messages - the messages list.
297
297
  */
298
- messagesInserted(index: number, messages: IChatMessage[]): void;
298
+ messagesInserted(index: number, messages: IMessageContent[]): void;
299
299
  /**
300
300
  * Function called when messages are deleted.
301
301
  *
@@ -435,9 +435,9 @@ export interface IChatContext {
435
435
  */
436
436
  readonly name: string;
437
437
  /**
438
- * A copy of the messages.
438
+ * A copy of the messages content.
439
439
  */
440
- readonly messages: IChatMessage[];
440
+ readonly messages: IMessageContent[];
441
441
  /**
442
442
  * A list of all users who have connected to this chat.
443
443
  */
@@ -456,7 +456,7 @@ export declare abstract class AbstractChatContext implements IChatContext {
456
456
  model: IChatModel;
457
457
  });
458
458
  get name(): string;
459
- get messages(): IChatMessage[];
459
+ get messages(): IMessageContent[];
460
460
  get user(): IUser | undefined;
461
461
  /**
462
462
  * ABSTRACT: Should return a list of users who have connected to this chat.
package/lib/model.js CHANGED
@@ -3,10 +3,11 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { ArrayExt } from '@lumino/algorithm';
6
+ import { PromiseDelegate } from '@lumino/coreutils';
6
7
  import { Signal } from '@lumino/signaling';
7
8
  import { InputModel } from './input-model';
9
+ import { Message } from './message';
8
10
  import { replaceMentionToSpan } from './utils';
9
- import { PromiseDelegate } from '@lumino/coreutils';
10
11
  /**
11
12
  * An abstract implementation of IChatModel.
12
13
  *
@@ -170,12 +171,12 @@ export class AbstractChatModel {
170
171
  if (this._config.stackMessages) {
171
172
  this._messages.slice(1).forEach((message, idx) => {
172
173
  const previousUser = this._messages[idx].sender.username;
173
- message.stacked = previousUser === message.sender.username;
174
+ message.update({ stacked: previousUser === message.sender.username });
174
175
  });
175
176
  }
176
177
  else {
177
178
  this._messages.forEach(message => {
178
- delete message.stacked;
179
+ message.update({ stacked: undefined });
179
180
  });
180
181
  }
181
182
  this._messagesUpdated.emit();
@@ -225,7 +226,7 @@ export class AbstractChatModel {
225
226
  this._viewportChanged.emit(values);
226
227
  }
227
228
  /**
228
- * A signal emitting when the messages list is updated.
229
+ * A signal emitting when the message list is updated.
229
230
  */
230
231
  get messagesUpdated() {
231
232
  return this._messagesUpdated;
@@ -328,7 +329,8 @@ export class AbstractChatModel {
328
329
  const lastRead = (_a = this.lastRead) !== null && _a !== void 0 ? _a : 0;
329
330
  // Format the messages.
330
331
  messages.forEach((message, idx) => {
331
- formattedMessages.push(this.formatChatMessage(message));
332
+ const formattedMessage = this.formatChatMessage(message);
333
+ formattedMessages.push(new Message(formattedMessage));
332
334
  if (message.time > lastRead) {
333
335
  unreadIndexes.push(index + idx);
334
336
  }
@@ -344,7 +346,7 @@ export class AbstractChatModel {
344
346
  for (let idx = start; idx <= end; idx++) {
345
347
  const message = this._messages[idx];
346
348
  const previousUser = this._messages[idx - 1].sender.username;
347
- message.stacked = previousUser === message.sender.username;
349
+ message.update({ stacked: previousUser === message.sender.username });
348
350
  }
349
351
  }
350
352
  this._addUnreadMessages(unreadIndexes);
@@ -461,7 +463,7 @@ export class AbstractChatContext {
461
463
  return this._model.name;
462
464
  }
463
465
  get messages() {
464
- return [...this._model.messages];
466
+ return this._model.messages.map(message => ({ ...message.content }));
465
467
  }
466
468
  get user() {
467
469
  var _a;