@jupyter/chat 0.8.0 → 0.9.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/components/chat-input.d.ts +3 -11
- package/lib/components/chat-input.js +26 -40
- package/lib/components/chat-messages.d.ts +17 -4
- package/lib/components/chat-messages.js +9 -9
- package/lib/components/chat.d.ts +5 -5
- package/lib/components/chat.js +9 -8
- package/lib/components/code-blocks/copy-button.js +6 -3
- package/lib/components/input/buttons/attach-button.d.ts +6 -0
- package/lib/components/input/{attach-button.js → buttons/attach-button.js} +11 -8
- package/lib/components/input/buttons/cancel-button.d.ts +6 -0
- package/lib/components/input/{cancel-button.js → buttons/cancel-button.js} +5 -7
- package/lib/components/input/buttons/index.d.ts +3 -0
- package/lib/components/input/buttons/index.js +7 -0
- package/lib/components/input/buttons/send-button.d.ts +6 -0
- package/lib/components/input/{send-button.js → buttons/send-button.js} +52 -42
- package/lib/components/input/index.d.ts +3 -3
- package/lib/components/input/index.js +3 -3
- package/lib/components/input/toolbar-registry.d.ts +98 -0
- package/lib/components/input/toolbar-registry.js +85 -0
- package/lib/components/input/use-chat-commands.js +3 -2
- package/lib/components/mui-extras/tooltipped-button.d.ts +1 -1
- package/lib/components/mui-extras/tooltipped-button.js +3 -2
- package/lib/components/mui-extras/tooltipped-icon-button.js +4 -2
- package/lib/input-model.d.ts +41 -0
- package/lib/input-model.js +17 -1
- package/lib/model.d.ts +22 -0
- package/lib/model.js +18 -2
- package/lib/types.d.ts +0 -18
- package/lib/widgets/chat-widget.d.ts +5 -1
- package/lib/widgets/chat-widget.js +7 -1
- package/package.json +1 -1
- package/src/components/chat-input.tsx +40 -65
- package/src/components/chat-messages.tsx +31 -14
- package/src/components/chat.tsx +12 -21
- package/src/components/code-blocks/copy-button.tsx +9 -3
- package/src/components/input/{attach-button.tsx → buttons/attach-button.tsx} +15 -20
- package/src/components/input/{cancel-button.tsx → buttons/cancel-button.tsx} +9 -16
- package/src/components/input/buttons/index.ts +8 -0
- package/src/components/input/{send-button.tsx → buttons/send-button.tsx} +62 -61
- package/src/components/input/index.ts +3 -3
- package/src/components/input/toolbar-registry.tsx +162 -0
- package/src/components/input/use-chat-commands.tsx +8 -2
- package/src/components/mui-extras/tooltipped-button.tsx +4 -2
- package/src/components/mui-extras/tooltipped-icon-button.tsx +5 -2
- package/src/input-model.ts +58 -1
- package/src/model.ts +36 -2
- package/src/types.ts +0 -21
- package/src/widgets/chat-widget.tsx +8 -1
- package/style/base.css +1 -0
- package/style/chat.css +10 -0
- package/style/input.css +32 -0
- package/lib/components/input/attach-button.d.ts +0 -14
- package/lib/components/input/cancel-button.d.ts +0 -11
- package/lib/components/input/send-button.d.ts +0 -18
|
@@ -8,36 +8,29 @@ import SendIcon from '@mui/icons-material/Send';
|
|
|
8
8
|
import { Box, Menu, MenuItem, Typography } from '@mui/material';
|
|
9
9
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
11
|
+
import { InputToolbarRegistry } from '../toolbar-registry';
|
|
12
|
+
import { TooltippedButton } from '../../mui-extras/tooltipped-button';
|
|
13
|
+
import { includeSelectionIcon } from '../../../icons';
|
|
14
|
+
import { IInputModel, InputModel } from '../../../input-model';
|
|
15
15
|
|
|
16
16
|
const SEND_BUTTON_CLASS = 'jp-chat-send-button';
|
|
17
17
|
const SEND_INCLUDE_OPENER_CLASS = 'jp-chat-send-include-opener';
|
|
18
18
|
const SEND_INCLUDE_LI_CLASS = 'jp-chat-send-include';
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* The send button props.
|
|
22
|
-
*/
|
|
23
|
-
export type SendButtonProps = {
|
|
24
|
-
model: IInputModel;
|
|
25
|
-
sendWithShiftEnter: boolean;
|
|
26
|
-
inputExists: boolean;
|
|
27
|
-
onSend: (selection?: Selection) => unknown;
|
|
28
|
-
hideIncludeSelection?: boolean;
|
|
29
|
-
hasButtonOnLeft?: boolean;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
20
|
/**
|
|
33
21
|
* The send button, with optional 'include selection' menu.
|
|
34
22
|
*/
|
|
35
|
-
export function SendButton(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
23
|
+
export function SendButton(
|
|
24
|
+
props: InputToolbarRegistry.IToolbarItemProps
|
|
25
|
+
): JSX.Element {
|
|
26
|
+
const { model } = props;
|
|
27
|
+
const { activeCellManager, selectionWatcher } = model;
|
|
28
|
+
const hideIncludeSelection = !activeCellManager || !selectionWatcher;
|
|
29
|
+
|
|
39
30
|
const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);
|
|
40
31
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
32
|
+
const [disabled, setDisabled] = useState(false);
|
|
33
|
+
const [tooltip, setTooltip] = useState<string>('');
|
|
41
34
|
|
|
42
35
|
const openMenu = useCallback((el: HTMLElement | null) => {
|
|
43
36
|
setMenuAnchorEl(el);
|
|
@@ -48,11 +41,36 @@ export function SendButton(props: SendButtonProps): JSX.Element {
|
|
|
48
41
|
setMenuOpen(false);
|
|
49
42
|
}, []);
|
|
50
43
|
|
|
51
|
-
const disabled = !props.inputExists;
|
|
52
|
-
|
|
53
44
|
const [selectionTooltip, setSelectionTooltip] = useState<string>('');
|
|
54
45
|
const [disableInclude, setDisableInclude] = useState<boolean>(true);
|
|
55
46
|
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const inputChanged = () => {
|
|
49
|
+
const inputExist = !!model.value.trim() || model.attachments.length;
|
|
50
|
+
setDisabled(!inputExist);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
model.valueChanged.connect(inputChanged);
|
|
54
|
+
model.attachmentsChanged?.connect(inputChanged);
|
|
55
|
+
|
|
56
|
+
inputChanged();
|
|
57
|
+
|
|
58
|
+
const configChanged = (_: IInputModel, config: InputModel.IConfig) => {
|
|
59
|
+
setTooltip(
|
|
60
|
+
(config.sendWithShiftEnter ?? false)
|
|
61
|
+
? 'Send message (SHIFT+ENTER)'
|
|
62
|
+
: 'Send message (ENTER)'
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
model.configChanged.connect(configChanged);
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
model.valueChanged.disconnect(inputChanged);
|
|
69
|
+
model.attachmentsChanged?.disconnect(inputChanged);
|
|
70
|
+
model.configChanged?.disconnect(configChanged);
|
|
71
|
+
};
|
|
72
|
+
}, [model]);
|
|
73
|
+
|
|
56
74
|
useEffect(() => {
|
|
57
75
|
/**
|
|
58
76
|
* Enable or disable the include selection button, and adapt the tooltip.
|
|
@@ -78,55 +96,44 @@ export function SendButton(props: SendButtonProps): JSX.Element {
|
|
|
78
96
|
selectionWatcher?.selectionChanged.disconnect(toggleIncludeState);
|
|
79
97
|
activeCellManager?.availabilityChanged.disconnect(toggleIncludeState);
|
|
80
98
|
};
|
|
81
|
-
}, [activeCellManager, selectionWatcher
|
|
82
|
-
|
|
83
|
-
const defaultTooltip = props.sendWithShiftEnter
|
|
84
|
-
? 'Send message (SHIFT+ENTER)'
|
|
85
|
-
: 'Send message (ENTER)';
|
|
86
|
-
const tooltip = defaultTooltip;
|
|
99
|
+
}, [activeCellManager, selectionWatcher]);
|
|
87
100
|
|
|
88
101
|
function sendWithSelection() {
|
|
89
|
-
|
|
102
|
+
let source = '';
|
|
103
|
+
|
|
90
104
|
if (selectionWatcher?.selection) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return;
|
|
105
|
+
// Append the selected text if exists.
|
|
106
|
+
source = selectionWatcher.selection.text;
|
|
107
|
+
} else if (activeCellManager?.available) {
|
|
108
|
+
// Append the active cell content if exists.
|
|
109
|
+
source = activeCellManager.getContent(false)!.source;
|
|
97
110
|
}
|
|
111
|
+
let content = model.value;
|
|
112
|
+
if (source) {
|
|
113
|
+
content += `
|
|
98
114
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
104
|
-
source: activeCellManager.getContent(false)!.source
|
|
105
|
-
});
|
|
106
|
-
closeMenu();
|
|
107
|
-
return;
|
|
115
|
+
\`\`\`
|
|
116
|
+
${source}
|
|
117
|
+
\`\`\`
|
|
118
|
+
`;
|
|
108
119
|
}
|
|
120
|
+
model.send(content);
|
|
121
|
+
closeMenu();
|
|
122
|
+
model.value = '';
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
return (
|
|
112
|
-
|
|
126
|
+
<>
|
|
113
127
|
<TooltippedButton
|
|
114
|
-
onClick={() =>
|
|
128
|
+
onClick={() => model.send(model.value)}
|
|
115
129
|
disabled={disabled}
|
|
116
130
|
tooltip={tooltip}
|
|
117
131
|
buttonProps={{
|
|
118
132
|
size: 'small',
|
|
119
|
-
title:
|
|
133
|
+
title: tooltip,
|
|
120
134
|
variant: 'contained',
|
|
121
135
|
className: SEND_BUTTON_CLASS
|
|
122
136
|
}}
|
|
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
137
|
>
|
|
131
138
|
<SendIcon />
|
|
132
139
|
</TooltippedButton>
|
|
@@ -151,12 +158,6 @@ export function SendButton(props: SendButtonProps): JSX.Element {
|
|
|
151
158
|
},
|
|
152
159
|
className: SEND_INCLUDE_OPENER_CLASS
|
|
153
160
|
}}
|
|
154
|
-
sx={{
|
|
155
|
-
minWidth: 'unset',
|
|
156
|
-
padding: '4px 0px',
|
|
157
|
-
borderRadius: '0px 2px 2px 0px',
|
|
158
|
-
marginLeft: '1px'
|
|
159
|
-
}}
|
|
160
161
|
>
|
|
161
162
|
<KeyboardArrowDown />
|
|
162
163
|
</TooltippedButton>
|
|
@@ -205,6 +206,6 @@ export function SendButton(props: SendButtonProps): JSX.Element {
|
|
|
205
206
|
</Menu>
|
|
206
207
|
</>
|
|
207
208
|
)}
|
|
208
|
-
|
|
209
|
+
</>
|
|
209
210
|
);
|
|
210
211
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './
|
|
8
|
-
export * from './
|
|
6
|
+
export * from './buttons';
|
|
7
|
+
export * from './toolbar-registry';
|
|
8
|
+
export * from './use-chat-commands';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
|
|
7
|
+
import { AttachButton, CancelButton, SendButton } from './buttons';
|
|
8
|
+
import { IInputModel } from '../../input-model';
|
|
9
|
+
import { ISignal, Signal } from '@lumino/signaling';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The toolbar registry interface.
|
|
13
|
+
*/
|
|
14
|
+
export interface IInputToolbarRegistry {
|
|
15
|
+
/**
|
|
16
|
+
* A signal emitting when the items has changed.
|
|
17
|
+
*/
|
|
18
|
+
readonly itemsChanged: ISignal<IInputToolbarRegistry, void>;
|
|
19
|
+
/**
|
|
20
|
+
* Get a toolbar item.
|
|
21
|
+
*/
|
|
22
|
+
get(name: string): InputToolbarRegistry.IToolbarItem | undefined;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the list of the visible toolbar items in order.
|
|
26
|
+
*/
|
|
27
|
+
getItems(): InputToolbarRegistry.IToolbarItem[];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Add a toolbar item.
|
|
31
|
+
*/
|
|
32
|
+
addItem(name: string, item: InputToolbarRegistry.IToolbarItem): void;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Hide an element.
|
|
36
|
+
*/
|
|
37
|
+
hide(name: string): void;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Show an element.
|
|
41
|
+
*/
|
|
42
|
+
show(name: string): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The toolbar registry implementation.
|
|
47
|
+
*/
|
|
48
|
+
export class InputToolbarRegistry implements IInputToolbarRegistry {
|
|
49
|
+
/**
|
|
50
|
+
* A signal emitting when the items has changed.
|
|
51
|
+
*/
|
|
52
|
+
get itemsChanged(): ISignal<IInputToolbarRegistry, void> {
|
|
53
|
+
return this._itemsChanged;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a toolbar item.
|
|
58
|
+
*/
|
|
59
|
+
get(name: string): InputToolbarRegistry.IToolbarItem | undefined {
|
|
60
|
+
return this._items.get(name);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the list of the visible toolbar items in order.
|
|
65
|
+
*/
|
|
66
|
+
getItems(): InputToolbarRegistry.IToolbarItem[] {
|
|
67
|
+
return Array.from(this._items.values())
|
|
68
|
+
.filter(item => !item.hidden)
|
|
69
|
+
.sort((a, b) => a.position - b.position);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Add a toolbar item.
|
|
74
|
+
*/
|
|
75
|
+
addItem(name: string, item: InputToolbarRegistry.IToolbarItem): void {
|
|
76
|
+
if (!this._items.has(name)) {
|
|
77
|
+
this._items.set(name, item);
|
|
78
|
+
this._itemsChanged.emit();
|
|
79
|
+
} else {
|
|
80
|
+
console.warn(`A chat input toolbar item '${name}' is already registered`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Hide an element.
|
|
86
|
+
*/
|
|
87
|
+
hide(name: string): void {
|
|
88
|
+
const item = this._items.get(name);
|
|
89
|
+
if (item) {
|
|
90
|
+
item.hidden = true;
|
|
91
|
+
this._itemsChanged.emit();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Show an element.
|
|
97
|
+
*/
|
|
98
|
+
show(name: string): void {
|
|
99
|
+
const item = this._items.get(name);
|
|
100
|
+
if (item) {
|
|
101
|
+
item.hidden = false;
|
|
102
|
+
this._itemsChanged.emit();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private _items = new Map<string, InputToolbarRegistry.IToolbarItem>();
|
|
107
|
+
private _itemsChanged = new Signal<this, void>(this);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export namespace InputToolbarRegistry {
|
|
111
|
+
/**
|
|
112
|
+
* The toolbar item interface.
|
|
113
|
+
*/
|
|
114
|
+
export interface IToolbarItem {
|
|
115
|
+
/**
|
|
116
|
+
* The react functional component with the button.
|
|
117
|
+
*
|
|
118
|
+
* NOTE:
|
|
119
|
+
* This component must be a TooltippedButton for a good integration in the toolbar.
|
|
120
|
+
*/
|
|
121
|
+
element: React.FunctionComponent<IToolbarItemProps>;
|
|
122
|
+
/**
|
|
123
|
+
* The position of the button in the toolbar.
|
|
124
|
+
*/
|
|
125
|
+
position: number;
|
|
126
|
+
/**
|
|
127
|
+
* Whether the button is hidden or not.
|
|
128
|
+
*/
|
|
129
|
+
hidden?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The toolbar item properties, send to the button.
|
|
134
|
+
*/
|
|
135
|
+
export interface IToolbarItemProps {
|
|
136
|
+
/**
|
|
137
|
+
* The input model of the input component including the button.
|
|
138
|
+
*/
|
|
139
|
+
model: IInputModel;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The default toolbar registry if none is provided.
|
|
144
|
+
*/
|
|
145
|
+
export function defaultToolbarRegistry(): InputToolbarRegistry {
|
|
146
|
+
const registry = new InputToolbarRegistry();
|
|
147
|
+
|
|
148
|
+
registry.addItem('send', {
|
|
149
|
+
element: SendButton,
|
|
150
|
+
position: 100
|
|
151
|
+
});
|
|
152
|
+
registry.addItem('attach', {
|
|
153
|
+
element: AttachButton,
|
|
154
|
+
position: 20
|
|
155
|
+
});
|
|
156
|
+
registry.addItem('cancel', {
|
|
157
|
+
element: CancelButton,
|
|
158
|
+
position: 10
|
|
159
|
+
});
|
|
160
|
+
return registry;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -144,8 +144,14 @@ export function useChatCommands(
|
|
|
144
144
|
<Box key={key} component="li" {...listItemProps}>
|
|
145
145
|
{commandIcon}
|
|
146
146
|
<p className="jp-chat-command-name">{command.name}</p>
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
{command.description && (
|
|
148
|
+
<>
|
|
149
|
+
<span> - </span>
|
|
150
|
+
<p className="jp-chat-command-description">
|
|
151
|
+
{command.description}
|
|
152
|
+
</p>
|
|
153
|
+
</>
|
|
154
|
+
)}
|
|
149
155
|
</Box>
|
|
150
156
|
);
|
|
151
157
|
},
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React from 'react';
|
|
7
6
|
import { Button, ButtonProps, SxProps, TooltipProps } from '@mui/material';
|
|
7
|
+
import React from 'react';
|
|
8
8
|
|
|
9
9
|
import { ContrastingTooltip } from './contrasting-tooltip';
|
|
10
10
|
|
|
11
|
+
const TOOLTIPPED_WRAP_CLASS = 'jp-chat-tooltipped-wrap';
|
|
12
|
+
|
|
11
13
|
export type TooltippedButtonProps = {
|
|
12
14
|
onClick: React.MouseEventHandler<HTMLButtonElement>;
|
|
13
15
|
tooltip: string;
|
|
@@ -72,7 +74,7 @@ export function TooltippedButton(props: TooltippedButtonProps): JSX.Element {
|
|
|
72
74
|
|
|
73
75
|
See: https://mui.com/material-ui/react-tooltip/#disabled-elements
|
|
74
76
|
*/}
|
|
75
|
-
<span style={{ cursor: 'default' }}>
|
|
77
|
+
<span style={{ cursor: 'default' }} className={TOOLTIPPED_WRAP_CLASS}>
|
|
76
78
|
<Button
|
|
77
79
|
{...props.buttonProps}
|
|
78
80
|
onClick={props.onClick}
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { classes } from '@jupyterlab/ui-components';
|
|
7
7
|
import { IconButton, IconButtonProps, TooltipProps } from '@mui/material';
|
|
8
|
+
import React from 'react';
|
|
8
9
|
|
|
9
10
|
import { ContrastingTooltip } from './contrasting-tooltip';
|
|
10
11
|
|
|
12
|
+
const TOOLTIPPED_WRAP_CLASS = 'jp-chat-tooltipped-wrap';
|
|
13
|
+
|
|
11
14
|
export type TooltippedIconButtonProps = {
|
|
12
15
|
onClick: () => unknown;
|
|
13
16
|
tooltip: string;
|
|
@@ -68,7 +71,7 @@ export function TooltippedIconButton(
|
|
|
68
71
|
|
|
69
72
|
See: https://mui.com/material-ui/react-tooltip/#disabled-elements
|
|
70
73
|
*/}
|
|
71
|
-
<span className={props.className}>
|
|
74
|
+
<span className={classes(props.className, TOOLTIPPED_WRAP_CLASS)}>
|
|
72
75
|
<IconButton
|
|
73
76
|
{...props.iconButtonProps}
|
|
74
77
|
onClick={props.onClick}
|
package/src/input-model.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ISignal, Signal } from '@lumino/signaling';
|
|
|
8
8
|
import { IActiveCellManager } from './active-cell-manager';
|
|
9
9
|
import { ISelectionWatcher } from './selection-watcher';
|
|
10
10
|
import { IAttachment } from './types';
|
|
11
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
11
12
|
|
|
12
13
|
const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
13
14
|
|
|
@@ -15,6 +16,16 @@ const WHITESPACE = new Set([' ', '\n', '\t']);
|
|
|
15
16
|
* The chat input interface.
|
|
16
17
|
*/
|
|
17
18
|
export interface IInputModel extends IDisposable {
|
|
19
|
+
/**
|
|
20
|
+
* Function to send a message.
|
|
21
|
+
*/
|
|
22
|
+
send: (content: string) => void;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Optional function to cancel edition.
|
|
26
|
+
*/
|
|
27
|
+
cancel: (() => void) | undefined;
|
|
28
|
+
|
|
18
29
|
/**
|
|
19
30
|
* The entire input value.
|
|
20
31
|
*/
|
|
@@ -56,6 +67,11 @@ export interface IInputModel extends IDisposable {
|
|
|
56
67
|
*/
|
|
57
68
|
readonly selectionWatcher: ISelectionWatcher | null;
|
|
58
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Get the document manager.
|
|
72
|
+
*/
|
|
73
|
+
readonly documentManager: IDocumentManager | null;
|
|
74
|
+
|
|
59
75
|
/**
|
|
60
76
|
* The input configuration.
|
|
61
77
|
*/
|
|
@@ -112,17 +128,32 @@ export interface IInputModel extends IDisposable {
|
|
|
112
128
|
*/
|
|
113
129
|
export class InputModel implements IInputModel {
|
|
114
130
|
constructor(options: InputModel.IOptions) {
|
|
131
|
+
this._onSend = options.onSend;
|
|
115
132
|
this._value = options.value || '';
|
|
116
133
|
this._attachments = options.attachments || [];
|
|
117
134
|
this.cursorIndex = options.cursorIndex || this.value.length;
|
|
118
135
|
this._activeCellManager = options.activeCellManager ?? null;
|
|
119
136
|
this._selectionWatcher = options.selectionWatcher ?? null;
|
|
120
|
-
|
|
137
|
+
this._documentManager = options.documentManager ?? null;
|
|
121
138
|
this._config = {
|
|
122
139
|
...options.config
|
|
123
140
|
};
|
|
141
|
+
this.cancel = options.onCancel;
|
|
124
142
|
}
|
|
125
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Function to send a message.
|
|
146
|
+
*/
|
|
147
|
+
send = (input: string): void => {
|
|
148
|
+
this._onSend(input, this);
|
|
149
|
+
this.value = '';
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Optional function to cancel edition.
|
|
154
|
+
*/
|
|
155
|
+
cancel: (() => void) | undefined;
|
|
156
|
+
|
|
126
157
|
/**
|
|
127
158
|
* The entire input value.
|
|
128
159
|
*/
|
|
@@ -199,6 +230,13 @@ export class InputModel implements IInputModel {
|
|
|
199
230
|
return this._selectionWatcher;
|
|
200
231
|
}
|
|
201
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Get the document manager.
|
|
235
|
+
*/
|
|
236
|
+
get documentManager(): IDocumentManager | null {
|
|
237
|
+
return this._documentManager;
|
|
238
|
+
}
|
|
239
|
+
|
|
202
240
|
/**
|
|
203
241
|
* The input configuration.
|
|
204
242
|
*/
|
|
@@ -314,12 +352,14 @@ export class InputModel implements IInputModel {
|
|
|
314
352
|
return this._isDisposed;
|
|
315
353
|
}
|
|
316
354
|
|
|
355
|
+
private _onSend: (input: string, model?: InputModel) => void;
|
|
317
356
|
private _value: string;
|
|
318
357
|
private _cursorIndex: number | null = null;
|
|
319
358
|
private _currentWord: string | null = null;
|
|
320
359
|
private _attachments: IAttachment[];
|
|
321
360
|
private _activeCellManager: IActiveCellManager | null;
|
|
322
361
|
private _selectionWatcher: ISelectionWatcher | null;
|
|
362
|
+
private _documentManager: IDocumentManager | null;
|
|
323
363
|
private _config: InputModel.IConfig;
|
|
324
364
|
private _valueChanged = new Signal<IInputModel, string>(this);
|
|
325
365
|
private _cursorIndexChanged = new Signal<IInputModel, number | null>(this);
|
|
@@ -332,6 +372,18 @@ export class InputModel implements IInputModel {
|
|
|
332
372
|
|
|
333
373
|
export namespace InputModel {
|
|
334
374
|
export interface IOptions {
|
|
375
|
+
/**
|
|
376
|
+
* The function that should send the message.
|
|
377
|
+
* @param content - the content of the message.
|
|
378
|
+
* @param model - the model of the input sending the message.
|
|
379
|
+
*/
|
|
380
|
+
onSend: (content: string, model?: InputModel) => void;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Function that should cancel the message edition.
|
|
384
|
+
*/
|
|
385
|
+
onCancel?: () => void;
|
|
386
|
+
|
|
335
387
|
/**
|
|
336
388
|
* The initial value of the input.
|
|
337
389
|
*/
|
|
@@ -362,6 +414,11 @@ export namespace InputModel {
|
|
|
362
414
|
* Selection watcher.
|
|
363
415
|
*/
|
|
364
416
|
selectionWatcher?: ISelectionWatcher | null;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Document manager.
|
|
420
|
+
*/
|
|
421
|
+
documentManager?: IDocumentManager | null;
|
|
365
422
|
}
|
|
366
423
|
|
|
367
424
|
export interface IConfig {
|
package/src/model.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
6
7
|
import { CommandRegistry } from '@lumino/commands';
|
|
7
8
|
import { IDisposable } from '@lumino/disposable';
|
|
8
9
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
@@ -67,6 +68,11 @@ export interface IChatModel extends IDisposable {
|
|
|
67
68
|
*/
|
|
68
69
|
readonly selectionWatcher: ISelectionWatcher | null;
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Get the selection watcher.
|
|
73
|
+
*/
|
|
74
|
+
readonly documentManager: IDocumentManager | null;
|
|
75
|
+
|
|
70
76
|
/**
|
|
71
77
|
* A signal emitting when the messages list is updated.
|
|
72
78
|
*/
|
|
@@ -101,6 +107,11 @@ export interface IChatModel extends IDisposable {
|
|
|
101
107
|
*/
|
|
102
108
|
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void;
|
|
103
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Clear the message list.
|
|
112
|
+
*/
|
|
113
|
+
clearMessages(): void;
|
|
114
|
+
|
|
104
115
|
/**
|
|
105
116
|
* Optional, to update a message from the chat panel.
|
|
106
117
|
*
|
|
@@ -189,16 +200,18 @@ export class ChatModel implements IChatModel {
|
|
|
189
200
|
this._inputModel = new InputModel({
|
|
190
201
|
activeCellManager: options.activeCellManager,
|
|
191
202
|
selectionWatcher: options.selectionWatcher,
|
|
203
|
+
documentManager: options.documentManager,
|
|
192
204
|
config: {
|
|
193
205
|
sendWithShiftEnter: config.sendWithShiftEnter
|
|
194
|
-
}
|
|
206
|
+
},
|
|
207
|
+
onSend: (input: string) => this.sendMessage({ body: input })
|
|
195
208
|
});
|
|
196
209
|
|
|
197
210
|
this._commands = options.commands;
|
|
198
211
|
|
|
199
212
|
this._activeCellManager = options.activeCellManager ?? null;
|
|
200
|
-
|
|
201
213
|
this._selectionWatcher = options.selectionWatcher ?? null;
|
|
214
|
+
this._documentManager = options.documentManager ?? null;
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
/**
|
|
@@ -249,6 +262,13 @@ export class ChatModel implements IChatModel {
|
|
|
249
262
|
return this._selectionWatcher;
|
|
250
263
|
}
|
|
251
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Get the document manager.
|
|
267
|
+
*/
|
|
268
|
+
get documentManager(): IDocumentManager | null {
|
|
269
|
+
return this._documentManager;
|
|
270
|
+
}
|
|
271
|
+
|
|
252
272
|
/**
|
|
253
273
|
* Timestamp of the last read message in local storage.
|
|
254
274
|
*/
|
|
@@ -403,6 +423,14 @@ export class ChatModel implements IChatModel {
|
|
|
403
423
|
*/
|
|
404
424
|
sendMessage(message: INewMessage): Promise<boolean | void> | boolean | void {}
|
|
405
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Clear the message list.
|
|
428
|
+
*/
|
|
429
|
+
clearMessages(): void {
|
|
430
|
+
this._messages = [];
|
|
431
|
+
this._messagesUpdated.emit();
|
|
432
|
+
}
|
|
433
|
+
|
|
406
434
|
/**
|
|
407
435
|
* Dispose the chat model.
|
|
408
436
|
*/
|
|
@@ -569,6 +597,7 @@ export class ChatModel implements IChatModel {
|
|
|
569
597
|
private _commands?: CommandRegistry;
|
|
570
598
|
private _activeCellManager: IActiveCellManager | null;
|
|
571
599
|
private _selectionWatcher: ISelectionWatcher | null;
|
|
600
|
+
private _documentManager: IDocumentManager | null;
|
|
572
601
|
private _notificationId: string | null = null;
|
|
573
602
|
private _messagesUpdated = new Signal<IChatModel, void>(this);
|
|
574
603
|
private _configChanged = new Signal<IChatModel, IConfig>(this);
|
|
@@ -609,5 +638,10 @@ export namespace ChatModel {
|
|
|
609
638
|
* Selection watcher.
|
|
610
639
|
*/
|
|
611
640
|
selectionWatcher?: ISelectionWatcher | null;
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Document manager.
|
|
644
|
+
*/
|
|
645
|
+
documentManager?: IDocumentManager | null;
|
|
612
646
|
}
|
|
613
647
|
}
|
package/src/types.ts
CHANGED
|
@@ -94,24 +94,3 @@ export interface IAttachment {
|
|
|
94
94
|
* An empty interface to describe optional settings that could be fetched from server.
|
|
95
95
|
*/
|
|
96
96
|
export interface ISettings {} /* eslint-disable-line @typescript-eslint/no-empty-object-type */
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Representation of a selected text.
|
|
100
|
-
*/
|
|
101
|
-
export type TextSelection = {
|
|
102
|
-
type: 'text';
|
|
103
|
-
source: string;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Representation of a selected cell.
|
|
108
|
-
*/
|
|
109
|
-
export type CellSelection = {
|
|
110
|
-
type: 'cell';
|
|
111
|
-
source: string;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Selection object (text or cell).
|
|
116
|
-
*/
|
|
117
|
-
export type Selection = TextSelection | CellSelection;
|