@jupyter/chat 0.4.0 → 0.5.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/active-cell-manager.d.ts +3 -0
- package/lib/components/chat-input.d.ts +4 -0
- package/lib/components/chat-input.js +32 -15
- package/lib/components/chat-messages.d.ts +31 -1
- package/lib/components/chat-messages.js +55 -19
- package/lib/components/chat.js +1 -1
- package/lib/components/code-blocks/code-toolbar.js +50 -16
- package/lib/components/input/cancel-button.d.ts +12 -0
- package/lib/components/input/cancel-button.js +27 -0
- package/lib/components/input/send-button.d.ts +18 -0
- package/lib/components/input/send-button.js +143 -0
- package/lib/components/mui-extras/tooltipped-button.d.ts +41 -0
- package/lib/components/mui-extras/tooltipped-button.js +43 -0
- package/lib/icons.d.ts +1 -0
- package/lib/icons.js +5 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/model.d.ts +51 -8
- package/lib/model.js +44 -12
- package/lib/selection-watcher.d.ts +62 -0
- package/lib/selection-watcher.js +134 -0
- package/lib/types.d.ts +22 -0
- package/lib/utils.d.ts +11 -0
- package/lib/utils.js +37 -0
- package/package.json +2 -1
- package/src/active-cell-manager.ts +3 -0
- package/src/components/chat-input.tsx +48 -30
- package/src/components/chat-messages.tsx +106 -32
- package/src/components/chat.tsx +1 -1
- package/src/components/code-blocks/code-toolbar.tsx +55 -17
- package/src/components/input/cancel-button.tsx +47 -0
- package/src/components/input/send-button.tsx +210 -0
- package/src/components/mui-extras/tooltipped-button.tsx +92 -0
- package/src/icons.ts +6 -0
- package/src/index.ts +1 -0
- package/src/model.ts +77 -13
- package/src/selection-watcher.ts +221 -0
- package/src/types.ts +25 -0
- package/src/utils.ts +47 -0
- package/style/chat.css +13 -0
- package/style/icons/include-selection.svg +5 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
|
7
|
+
import SendIcon from '@mui/icons-material/Send';
|
|
8
|
+
import { Box, Menu, MenuItem, Typography } from '@mui/material';
|
|
9
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
10
|
+
|
|
11
|
+
import { IChatModel } from '../../model';
|
|
12
|
+
import { TooltippedButton } from '../mui-extras/tooltipped-button';
|
|
13
|
+
import { includeSelectionIcon } from '../../icons';
|
|
14
|
+
import { Selection } from '../../types';
|
|
15
|
+
|
|
16
|
+
const SEND_BUTTON_CLASS = 'jp-chat-send-button';
|
|
17
|
+
const SEND_INCLUDE_OPENER_CLASS = 'jp-chat-send-include-opener';
|
|
18
|
+
const SEND_INCLUDE_LI_CLASS = 'jp-chat-send-include';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The send button props.
|
|
22
|
+
*/
|
|
23
|
+
export type SendButtonProps = {
|
|
24
|
+
model: IChatModel;
|
|
25
|
+
sendWithShiftEnter: boolean;
|
|
26
|
+
inputExists: boolean;
|
|
27
|
+
onSend: (selection?: Selection) => unknown;
|
|
28
|
+
hideIncludeSelection?: boolean;
|
|
29
|
+
hasButtonOnLeft?: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The send button, with optional 'include selection' menu.
|
|
34
|
+
*/
|
|
35
|
+
export function SendButton(props: SendButtonProps): JSX.Element {
|
|
36
|
+
const { activeCellManager, selectionWatcher } = props.model;
|
|
37
|
+
const hideIncludeSelection = props.hideIncludeSelection ?? false;
|
|
38
|
+
const hasButtonOnLeft = props.hasButtonOnLeft ?? false;
|
|
39
|
+
const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);
|
|
40
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
41
|
+
|
|
42
|
+
const openMenu = useCallback((el: HTMLElement | null) => {
|
|
43
|
+
setMenuAnchorEl(el);
|
|
44
|
+
setMenuOpen(true);
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
const closeMenu = useCallback(() => {
|
|
48
|
+
setMenuOpen(false);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const disabled = !props.inputExists;
|
|
52
|
+
|
|
53
|
+
const [selectionTooltip, setSelectionTooltip] = useState<string>('');
|
|
54
|
+
const [disableInclude, setDisableInclude] = useState<boolean>(true);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
/**
|
|
58
|
+
* Enable or disable the include selection button, and adapt the tooltip.
|
|
59
|
+
*/
|
|
60
|
+
const toggleIncludeState = () => {
|
|
61
|
+
setDisableInclude(
|
|
62
|
+
!(selectionWatcher?.selection || activeCellManager?.available)
|
|
63
|
+
);
|
|
64
|
+
const tooltip = selectionWatcher?.selection
|
|
65
|
+
? `${selectionWatcher.selection.numLines} line(s) selected`
|
|
66
|
+
: activeCellManager?.available
|
|
67
|
+
? 'Code from 1 active cell'
|
|
68
|
+
: 'No selection or active cell';
|
|
69
|
+
setSelectionTooltip(tooltip);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (!hideIncludeSelection) {
|
|
73
|
+
selectionWatcher?.selectionChanged.connect(toggleIncludeState);
|
|
74
|
+
activeCellManager?.availabilityChanged.connect(toggleIncludeState);
|
|
75
|
+
toggleIncludeState();
|
|
76
|
+
}
|
|
77
|
+
return () => {
|
|
78
|
+
selectionWatcher?.selectionChanged.disconnect(toggleIncludeState);
|
|
79
|
+
activeCellManager?.availabilityChanged.disconnect(toggleIncludeState);
|
|
80
|
+
};
|
|
81
|
+
}, [activeCellManager, selectionWatcher, hideIncludeSelection]);
|
|
82
|
+
|
|
83
|
+
const defaultTooltip = props.sendWithShiftEnter
|
|
84
|
+
? 'Send message (SHIFT+ENTER)'
|
|
85
|
+
: 'Send message (ENTER)';
|
|
86
|
+
const tooltip = defaultTooltip;
|
|
87
|
+
|
|
88
|
+
function sendWithSelection() {
|
|
89
|
+
// Append the selected text if exists.
|
|
90
|
+
if (selectionWatcher?.selection) {
|
|
91
|
+
props.onSend({
|
|
92
|
+
type: 'text',
|
|
93
|
+
source: selectionWatcher.selection.text
|
|
94
|
+
});
|
|
95
|
+
closeMenu();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Append the active cell content if exists.
|
|
100
|
+
if (activeCellManager?.available) {
|
|
101
|
+
props.onSend({
|
|
102
|
+
type: 'cell',
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
104
|
+
source: activeCellManager.getContent(false)!.source
|
|
105
|
+
});
|
|
106
|
+
closeMenu();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Box sx={{ display: 'flex', flexWrap: 'nowrap' }}>
|
|
113
|
+
<TooltippedButton
|
|
114
|
+
onClick={() => props.onSend()}
|
|
115
|
+
disabled={disabled}
|
|
116
|
+
tooltip={tooltip}
|
|
117
|
+
buttonProps={{
|
|
118
|
+
size: 'small',
|
|
119
|
+
title: defaultTooltip,
|
|
120
|
+
variant: 'contained',
|
|
121
|
+
className: SEND_BUTTON_CLASS
|
|
122
|
+
}}
|
|
123
|
+
sx={{
|
|
124
|
+
minWidth: 'unset',
|
|
125
|
+
borderTopLeftRadius: hasButtonOnLeft ? '0px' : '2px',
|
|
126
|
+
borderTopRightRadius: hideIncludeSelection ? '2px' : '0px',
|
|
127
|
+
borderBottomRightRadius: hideIncludeSelection ? '2px' : '0px',
|
|
128
|
+
borderBottomLeftRadius: hasButtonOnLeft ? '0px' : '2px'
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<SendIcon />
|
|
132
|
+
</TooltippedButton>
|
|
133
|
+
{!hideIncludeSelection && (
|
|
134
|
+
<>
|
|
135
|
+
<TooltippedButton
|
|
136
|
+
onClick={e => {
|
|
137
|
+
openMenu(e.currentTarget);
|
|
138
|
+
}}
|
|
139
|
+
disabled={disabled}
|
|
140
|
+
tooltip=""
|
|
141
|
+
buttonProps={{
|
|
142
|
+
variant: 'contained',
|
|
143
|
+
onKeyDown: e => {
|
|
144
|
+
if (e.key !== 'Enter' && e.key !== ' ') {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
openMenu(e.currentTarget);
|
|
148
|
+
// stopping propagation of this event prevents the prompt from being
|
|
149
|
+
// sent when the dropdown button is selected and clicked via 'Enter'.
|
|
150
|
+
e.stopPropagation();
|
|
151
|
+
},
|
|
152
|
+
className: SEND_INCLUDE_OPENER_CLASS
|
|
153
|
+
}}
|
|
154
|
+
sx={{
|
|
155
|
+
minWidth: 'unset',
|
|
156
|
+
padding: '4px 0px',
|
|
157
|
+
borderRadius: '0px 2px 2px 0px',
|
|
158
|
+
marginLeft: '1px'
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
<KeyboardArrowDown />
|
|
162
|
+
</TooltippedButton>
|
|
163
|
+
<Menu
|
|
164
|
+
open={menuOpen}
|
|
165
|
+
onClose={closeMenu}
|
|
166
|
+
anchorEl={menuAnchorEl}
|
|
167
|
+
anchorOrigin={{
|
|
168
|
+
vertical: 'top',
|
|
169
|
+
horizontal: 'right'
|
|
170
|
+
}}
|
|
171
|
+
transformOrigin={{
|
|
172
|
+
vertical: 'bottom',
|
|
173
|
+
horizontal: 'right'
|
|
174
|
+
}}
|
|
175
|
+
sx={{
|
|
176
|
+
'& .MuiMenuItem-root': {
|
|
177
|
+
display: 'flex',
|
|
178
|
+
alignItems: 'center',
|
|
179
|
+
gap: '8px'
|
|
180
|
+
},
|
|
181
|
+
'& svg': {
|
|
182
|
+
lineHeight: 0
|
|
183
|
+
}
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
<MenuItem
|
|
187
|
+
onClick={e => {
|
|
188
|
+
sendWithSelection();
|
|
189
|
+
// prevent sending second message with no selection
|
|
190
|
+
e.stopPropagation();
|
|
191
|
+
}}
|
|
192
|
+
disabled={disableInclude}
|
|
193
|
+
className={SEND_INCLUDE_LI_CLASS}
|
|
194
|
+
>
|
|
195
|
+
<includeSelectionIcon.react />
|
|
196
|
+
<Box>
|
|
197
|
+
<Typography display="block">
|
|
198
|
+
Send message with selection
|
|
199
|
+
</Typography>
|
|
200
|
+
<Typography display="block" sx={{ opacity: 0.618 }}>
|
|
201
|
+
{selectionTooltip}
|
|
202
|
+
</Typography>
|
|
203
|
+
</Box>
|
|
204
|
+
</MenuItem>
|
|
205
|
+
</Menu>
|
|
206
|
+
</>
|
|
207
|
+
)}
|
|
208
|
+
</Box>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Button, ButtonProps, SxProps, TooltipProps } from '@mui/material';
|
|
8
|
+
|
|
9
|
+
import { ContrastingTooltip } from './contrasting-tooltip';
|
|
10
|
+
|
|
11
|
+
export type TooltippedButtonProps = {
|
|
12
|
+
onClick: React.MouseEventHandler<HTMLButtonElement>;
|
|
13
|
+
tooltip: string;
|
|
14
|
+
children: JSX.Element;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
placement?: TooltipProps['placement'];
|
|
17
|
+
/**
|
|
18
|
+
* The offset of the tooltip popup.
|
|
19
|
+
*
|
|
20
|
+
* The expected syntax is defined by the Popper library:
|
|
21
|
+
* https://popper.js.org/docs/v2/modifiers/offset/
|
|
22
|
+
*/
|
|
23
|
+
offset?: [number, number];
|
|
24
|
+
'aria-label'?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Props passed directly to the MUI `Button` component.
|
|
27
|
+
*/
|
|
28
|
+
buttonProps?: ButtonProps;
|
|
29
|
+
/**
|
|
30
|
+
* Styles applied to the MUI `Button` component.
|
|
31
|
+
*/
|
|
32
|
+
sx?: SxProps;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A component that renders an MUI `Button` with a high-contrast tooltip
|
|
37
|
+
* provided by `ContrastingTooltip`. This component differs from the MUI
|
|
38
|
+
* defaults in the following ways:
|
|
39
|
+
*
|
|
40
|
+
* - Shows the tooltip on hover even if disabled.
|
|
41
|
+
* - Renders the tooltip above the button by default.
|
|
42
|
+
* - Renders the tooltip closer to the button by default.
|
|
43
|
+
* - Lowers the opacity of the Button when disabled.
|
|
44
|
+
* - Renders the Button with `line-height: 0` to avoid showing extra
|
|
45
|
+
* vertical space in SVG icons.
|
|
46
|
+
*
|
|
47
|
+
* NOTE TO DEVS: Please keep this component's features synchronized with
|
|
48
|
+
* features available to `TooltippedIconButton`.
|
|
49
|
+
*/
|
|
50
|
+
export function TooltippedButton(props: TooltippedButtonProps): JSX.Element {
|
|
51
|
+
return (
|
|
52
|
+
<ContrastingTooltip
|
|
53
|
+
title={props.tooltip}
|
|
54
|
+
placement={props.placement ?? 'top'}
|
|
55
|
+
slotProps={{
|
|
56
|
+
popper: {
|
|
57
|
+
modifiers: [
|
|
58
|
+
{
|
|
59
|
+
name: 'offset',
|
|
60
|
+
options: {
|
|
61
|
+
offset: [0, -8]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
{/*
|
|
69
|
+
By default, tooltips never appear when the Button is disabled. The
|
|
70
|
+
official way to support this feature in MUI is to wrap the child Button
|
|
71
|
+
element in a `span` element.
|
|
72
|
+
|
|
73
|
+
See: https://mui.com/material-ui/react-tooltip/#disabled-elements
|
|
74
|
+
*/}
|
|
75
|
+
<span style={{ cursor: 'default' }}>
|
|
76
|
+
<Button
|
|
77
|
+
{...props.buttonProps}
|
|
78
|
+
onClick={props.onClick}
|
|
79
|
+
disabled={props.disabled}
|
|
80
|
+
sx={{
|
|
81
|
+
lineHeight: 0,
|
|
82
|
+
...(props.disabled && { opacity: 0.5 }),
|
|
83
|
+
...props.sx
|
|
84
|
+
}}
|
|
85
|
+
aria-label={props['aria-label']}
|
|
86
|
+
>
|
|
87
|
+
{props.children}
|
|
88
|
+
</Button>
|
|
89
|
+
</span>
|
|
90
|
+
</ContrastingTooltip>
|
|
91
|
+
);
|
|
92
|
+
}
|
package/src/icons.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
9
9
|
|
|
10
10
|
import chatSvgStr from '../style/icons/chat.svg';
|
|
11
|
+
import includeSelectionIconStr from '../style/icons/include-selection.svg';
|
|
11
12
|
import readSvgStr from '../style/icons/read.svg';
|
|
12
13
|
import replaceCellSvg from '../style/icons/replace-cell.svg';
|
|
13
14
|
|
|
@@ -25,3 +26,8 @@ export const replaceCellIcon = new LabIcon({
|
|
|
25
26
|
name: 'jupyter-ai::replace-cell',
|
|
26
27
|
svgstr: replaceCellSvg
|
|
27
28
|
});
|
|
29
|
+
|
|
30
|
+
export const includeSelectionIcon = new LabIcon({
|
|
31
|
+
name: 'jupyter-chat::include',
|
|
32
|
+
svgstr: includeSelectionIconStr
|
|
33
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './model';
|
|
|
8
8
|
export * from './registry';
|
|
9
9
|
export * from './types';
|
|
10
10
|
export * from './active-cell-manager';
|
|
11
|
+
export * from './selection-watcher';
|
|
11
12
|
export * from './widgets/chat-error';
|
|
12
13
|
export * from './widgets/chat-sidebar';
|
|
13
14
|
export * from './widgets/chat-widget';
|
package/src/model.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
IUser
|
|
16
16
|
} from './types';
|
|
17
17
|
import { IActiveCellManager } from './active-cell-manager';
|
|
18
|
+
import { ISelectionWatcher } from './selection-watcher';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* The chat model interface.
|
|
@@ -55,6 +56,11 @@ export interface IChatModel extends IDisposable {
|
|
|
55
56
|
*/
|
|
56
57
|
readonly activeCellManager: IActiveCellManager | null;
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Get the selection watcher.
|
|
61
|
+
*/
|
|
62
|
+
readonly selectionWatcher: ISelectionWatcher | null;
|
|
63
|
+
|
|
58
64
|
/**
|
|
59
65
|
* A signal emitting when the messages list is updated.
|
|
60
66
|
*/
|
|
@@ -75,6 +81,11 @@ export interface IChatModel extends IDisposable {
|
|
|
75
81
|
*/
|
|
76
82
|
readonly viewportChanged?: ISignal<IChatModel, number[]>;
|
|
77
83
|
|
|
84
|
+
/**
|
|
85
|
+
* A signal emitting when the writers change.
|
|
86
|
+
*/
|
|
87
|
+
readonly writersChanged?: ISignal<IChatModel, IUser[]>;
|
|
88
|
+
|
|
78
89
|
/**
|
|
79
90
|
* A signal emitting when the focus is requested on the input.
|
|
80
91
|
*/
|
|
@@ -87,7 +98,7 @@ export interface IChatModel extends IDisposable {
|
|
|
87
98
|
* @param message - the message to send.
|
|
88
99
|
* @returns whether the message has been sent or not, or nothing if not needed.
|
|
89
100
|
*/
|
|
90
|
-
|
|
101
|
+
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
91
102
|
|
|
92
103
|
/**
|
|
93
104
|
* Optional, to update a message from the chat panel.
|
|
@@ -145,10 +156,20 @@ export interface IChatModel extends IDisposable {
|
|
|
145
156
|
*/
|
|
146
157
|
messagesDeleted(index: number, count: number): void;
|
|
147
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Update the current writers list.
|
|
161
|
+
*/
|
|
162
|
+
updateWriters(writers: IUser[]): void;
|
|
163
|
+
|
|
148
164
|
/**
|
|
149
165
|
* Function to request the focus on the input of the chat.
|
|
150
166
|
*/
|
|
151
167
|
focusInput(): void;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Function called by the input on key pressed.
|
|
171
|
+
*/
|
|
172
|
+
inputChanged?(input?: string): void;
|
|
152
173
|
}
|
|
153
174
|
|
|
154
175
|
/**
|
|
@@ -164,23 +185,19 @@ export class ChatModel implements IChatModel {
|
|
|
164
185
|
const config = options.config ?? {};
|
|
165
186
|
|
|
166
187
|
// Stack consecutive messages from the same user by default.
|
|
167
|
-
this._config = {
|
|
188
|
+
this._config = {
|
|
189
|
+
stackMessages: true,
|
|
190
|
+
sendTypingNotification: true,
|
|
191
|
+
...config
|
|
192
|
+
};
|
|
168
193
|
|
|
169
194
|
this._commands = options.commands;
|
|
170
195
|
|
|
171
196
|
this._activeCellManager = options.activeCellManager ?? null;
|
|
172
|
-
}
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
* The chat messages list.
|
|
176
|
-
*/
|
|
177
|
-
get messages(): IChatMessage[] {
|
|
178
|
-
return this._messages;
|
|
198
|
+
this._selectionWatcher = options.selectionWatcher ?? null;
|
|
179
199
|
}
|
|
180
200
|
|
|
181
|
-
get activeCellManager(): IActiveCellManager | null {
|
|
182
|
-
return this._activeCellManager;
|
|
183
|
-
}
|
|
184
201
|
/**
|
|
185
202
|
* The chat model id.
|
|
186
203
|
*/
|
|
@@ -201,6 +218,26 @@ export class ChatModel implements IChatModel {
|
|
|
201
218
|
this._name = value;
|
|
202
219
|
}
|
|
203
220
|
|
|
221
|
+
/**
|
|
222
|
+
* The chat messages list.
|
|
223
|
+
*/
|
|
224
|
+
get messages(): IChatMessage[] {
|
|
225
|
+
return this._messages;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get the active cell manager.
|
|
229
|
+
*/
|
|
230
|
+
get activeCellManager(): IActiveCellManager | null {
|
|
231
|
+
return this._activeCellManager;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get the selection watcher.
|
|
236
|
+
*/
|
|
237
|
+
get selectionWatcher(): ISelectionWatcher | null {
|
|
238
|
+
return this._selectionWatcher;
|
|
239
|
+
}
|
|
240
|
+
|
|
204
241
|
/**
|
|
205
242
|
* Timestamp of the last read message in local storage.
|
|
206
243
|
*/
|
|
@@ -338,6 +375,13 @@ export class ChatModel implements IChatModel {
|
|
|
338
375
|
return this._viewportChanged;
|
|
339
376
|
}
|
|
340
377
|
|
|
378
|
+
/**
|
|
379
|
+
* A signal emitting when the writers change.
|
|
380
|
+
*/
|
|
381
|
+
get writersChanged(): ISignal<IChatModel, IUser[]> {
|
|
382
|
+
return this._writersChanged;
|
|
383
|
+
}
|
|
384
|
+
|
|
341
385
|
/**
|
|
342
386
|
* A signal emitting when the focus is requested on the input.
|
|
343
387
|
*/
|
|
@@ -352,7 +396,7 @@ export class ChatModel implements IChatModel {
|
|
|
352
396
|
* @param message - the message to send.
|
|
353
397
|
* @returns whether the message has been sent or not.
|
|
354
398
|
*/
|
|
355
|
-
|
|
399
|
+
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void {}
|
|
356
400
|
|
|
357
401
|
/**
|
|
358
402
|
* Dispose the chat model.
|
|
@@ -452,6 +496,14 @@ export class ChatModel implements IChatModel {
|
|
|
452
496
|
this._messagesUpdated.emit();
|
|
453
497
|
}
|
|
454
498
|
|
|
499
|
+
/**
|
|
500
|
+
* Update the current writers list.
|
|
501
|
+
* This implementation only propagate the list via a signal.
|
|
502
|
+
*/
|
|
503
|
+
updateWriters(writers: IUser[]): void {
|
|
504
|
+
this._writersChanged.emit(writers);
|
|
505
|
+
}
|
|
506
|
+
|
|
455
507
|
/**
|
|
456
508
|
* Function to request the focus on the input of the chat.
|
|
457
509
|
*/
|
|
@@ -459,6 +511,11 @@ export class ChatModel implements IChatModel {
|
|
|
459
511
|
this._focusInputSignal.emit();
|
|
460
512
|
}
|
|
461
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Function called by the input on key pressed.
|
|
516
|
+
*/
|
|
517
|
+
inputChanged?(input?: string): void {}
|
|
518
|
+
|
|
462
519
|
/**
|
|
463
520
|
* Add unread messages to the list.
|
|
464
521
|
* @param indexes - list of new indexes.
|
|
@@ -517,11 +574,13 @@ export class ChatModel implements IChatModel {
|
|
|
517
574
|
private _isDisposed = false;
|
|
518
575
|
private _commands?: CommandRegistry;
|
|
519
576
|
private _activeCellManager: IActiveCellManager | null;
|
|
577
|
+
private _selectionWatcher: ISelectionWatcher | null;
|
|
520
578
|
private _notificationId: string | null = null;
|
|
521
579
|
private _messagesUpdated = new Signal<IChatModel, void>(this);
|
|
522
580
|
private _configChanged = new Signal<IChatModel, IConfig>(this);
|
|
523
581
|
private _unreadChanged = new Signal<IChatModel, number[]>(this);
|
|
524
582
|
private _viewportChanged = new Signal<IChatModel, number[]>(this);
|
|
583
|
+
private _writersChanged = new Signal<IChatModel, IUser[]>(this);
|
|
525
584
|
private _focusInputSignal = new Signal<ChatModel, void>(this);
|
|
526
585
|
}
|
|
527
586
|
|
|
@@ -544,8 +603,13 @@ export namespace ChatModel {
|
|
|
544
603
|
commands?: CommandRegistry;
|
|
545
604
|
|
|
546
605
|
/**
|
|
547
|
-
* Active cell manager
|
|
606
|
+
* Active cell manager.
|
|
548
607
|
*/
|
|
549
608
|
activeCellManager?: IActiveCellManager | null;
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Selection watcher.
|
|
612
|
+
*/
|
|
613
|
+
selectionWatcher?: ISelectionWatcher | null;
|
|
550
614
|
}
|
|
551
615
|
}
|