@jupyter/chat 0.18.2 → 0.19.0-alpha.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/attachments.js +47 -21
- package/lib/components/chat.d.ts +5 -0
- package/lib/components/chat.js +7 -6
- package/lib/components/code-blocks/code-toolbar.js +29 -10
- package/lib/components/code-blocks/copy-button.js +11 -3
- package/lib/components/index.d.ts +0 -1
- package/lib/components/index.js +0 -1
- package/lib/components/input/buttons/attach-button.js +7 -2
- package/lib/components/input/buttons/cancel-button.js +12 -10
- package/lib/components/input/buttons/index.d.ts +2 -0
- package/lib/components/input/buttons/index.js +2 -0
- package/lib/components/input/buttons/save-edit-button.d.ts +6 -0
- package/lib/components/input/buttons/save-edit-button.js +51 -0
- package/lib/components/input/buttons/send-button.d.ts +1 -1
- package/lib/components/input/buttons/send-button.js +35 -122
- package/lib/components/input/buttons/stop-button.d.ts +6 -0
- package/lib/components/input/buttons/stop-button.js +63 -0
- package/lib/components/input/chat-input.d.ts +15 -0
- package/lib/components/input/chat-input.js +109 -46
- package/lib/components/input/index.d.ts +1 -0
- package/lib/components/input/index.js +1 -0
- package/lib/components/input/toolbar-registry.d.ts +10 -0
- package/lib/components/input/toolbar-registry.js +10 -1
- package/lib/components/input/use-chat-commands.js +9 -4
- package/lib/components/input/writing-indicator.d.ts +15 -0
- package/lib/components/input/writing-indicator.js +50 -0
- package/lib/components/messages/header.d.ts +4 -0
- package/lib/components/messages/header.js +4 -0
- package/lib/components/messages/index.d.ts +0 -1
- package/lib/components/messages/index.js +0 -1
- package/lib/components/messages/message.js +1 -1
- package/lib/components/messages/messages.d.ts +5 -0
- package/lib/components/messages/messages.js +24 -14
- package/lib/components/messages/toolbar.js +37 -15
- package/lib/input-model.d.ts +14 -0
- package/lib/input-model.js +12 -4
- package/lib/model.d.ts +8 -0
- package/lib/model.js +6 -0
- package/lib/types.d.ts +4 -0
- package/lib/widgets/chat-widget.d.ts +4 -0
- package/lib/widgets/chat-widget.js +36 -11
- package/lib/widgets/multichat-panel.js +2 -1
- package/package.json +1 -1
- package/src/components/attachments.tsx +70 -33
- package/src/components/chat.tsx +13 -4
- package/src/components/code-blocks/code-toolbar.tsx +56 -28
- package/src/components/code-blocks/copy-button.tsx +21 -12
- package/src/components/index.ts +0 -1
- package/src/components/input/buttons/attach-button.tsx +8 -2
- package/src/components/input/buttons/cancel-button.tsx +20 -15
- package/src/components/input/buttons/index.ts +2 -0
- package/src/components/input/buttons/save-edit-button.tsx +75 -0
- package/src/components/input/buttons/send-button.tsx +50 -167
- package/src/components/input/buttons/stop-button.tsx +88 -0
- package/src/components/input/chat-input.tsx +188 -83
- package/src/components/input/index.ts +1 -0
- package/src/components/input/toolbar-registry.tsx +25 -1
- package/src/components/input/use-chat-commands.tsx +25 -5
- package/src/components/input/writing-indicator.tsx +83 -0
- package/src/components/messages/header.tsx +8 -0
- package/src/components/messages/index.ts +0 -1
- package/src/components/messages/message.tsx +1 -0
- package/src/components/messages/messages.tsx +63 -39
- package/src/components/messages/toolbar.tsx +51 -21
- package/src/input-model.ts +21 -0
- package/src/model.ts +12 -0
- package/src/types.ts +5 -0
- package/src/widgets/chat-widget.tsx +43 -12
- package/src/widgets/multichat-panel.tsx +2 -1
- package/style/chat.css +13 -141
- package/style/input.css +0 -58
- package/lib/components/messages/writers.d.ts +0 -16
- package/lib/components/messages/writers.js +0 -39
- package/src/components/messages/writers.tsx +0 -81
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import CloseIcon from '@mui/icons-material/Close';
|
|
6
|
-
import { Box } from '@mui/material';
|
|
6
|
+
import { Box, Button, Tooltip } from '@mui/material';
|
|
7
7
|
import React, { useContext } from 'react';
|
|
8
8
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
9
9
|
import { UUID } from '@lumino/coreutils';
|
|
10
|
-
import { TooltippedButton } from './mui-extras/tooltipped-button';
|
|
11
10
|
import { AttachmentOpenerContext } from '../context';
|
|
12
|
-
const ATTACHMENTS_CLASS = 'jp-chat-attachments';
|
|
13
11
|
const ATTACHMENT_CLASS = 'jp-chat-attachment';
|
|
14
12
|
const ATTACHMENT_CLICKABLE_CLASS = 'jp-chat-attachment-clickable';
|
|
15
13
|
const REMOVE_BUTTON_CLASS = 'jp-chat-attachment-remove';
|
|
@@ -41,7 +39,13 @@ function getAttachmentDisplayName(attachment) {
|
|
|
41
39
|
* The Attachments component.
|
|
42
40
|
*/
|
|
43
41
|
export function AttachmentPreviewList(props) {
|
|
44
|
-
return (React.createElement(Box, {
|
|
42
|
+
return (React.createElement(Box, { sx: {
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexWrap: 'wrap',
|
|
45
|
+
gap: 1,
|
|
46
|
+
rowGap: 1,
|
|
47
|
+
columnGap: 2
|
|
48
|
+
} }, props.attachments.map(attachment => (React.createElement(AttachmentPreview, { key: `${PathExt.basename(attachment.value)}-${UUID.uuid4()}`, ...props, attachment: attachment })))));
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* The Attachment component.
|
|
@@ -49,21 +53,43 @@ export function AttachmentPreviewList(props) {
|
|
|
49
53
|
export function AttachmentPreview(props) {
|
|
50
54
|
const remove_tooltip = 'Remove attachment';
|
|
51
55
|
const attachmentOpenerRegistry = useContext(AttachmentOpenerContext);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
const isClickable = !!(attachmentOpenerRegistry === null || attachmentOpenerRegistry === void 0 ? void 0 : attachmentOpenerRegistry.get(props.attachment.type));
|
|
57
|
+
return (React.createElement(Box, { className: ATTACHMENT_CLASS, sx: {
|
|
58
|
+
border: '1px solid var(--jp-border-color1)',
|
|
59
|
+
borderRadius: '2px',
|
|
60
|
+
px: 1,
|
|
61
|
+
py: 0.5,
|
|
62
|
+
backgroundColor: 'var(--jp-layout-color2)',
|
|
63
|
+
display: 'flex',
|
|
64
|
+
alignItems: 'center',
|
|
65
|
+
gap: 0.5,
|
|
66
|
+
fontSize: '0.8125rem'
|
|
67
|
+
} },
|
|
68
|
+
React.createElement(Tooltip, { title: props.attachment.value, placement: "top", arrow: true },
|
|
69
|
+
React.createElement(Box, { className: (attachmentOpenerRegistry === null || attachmentOpenerRegistry === void 0 ? void 0 : attachmentOpenerRegistry.get(props.attachment.type))
|
|
70
|
+
? ATTACHMENT_CLICKABLE_CLASS
|
|
71
|
+
: '', component: "span", onClick: () => {
|
|
72
|
+
var _a;
|
|
73
|
+
return (_a = attachmentOpenerRegistry === null || attachmentOpenerRegistry === void 0 ? void 0 : attachmentOpenerRegistry.get(props.attachment.type)) === null || _a === void 0 ? void 0 : _a(props.attachment);
|
|
74
|
+
}, sx: {
|
|
75
|
+
cursor: isClickable ? 'pointer' : 'default',
|
|
76
|
+
'&:hover': isClickable
|
|
77
|
+
? {
|
|
78
|
+
textDecoration: 'underline'
|
|
79
|
+
}
|
|
80
|
+
: {}
|
|
81
|
+
} }, getAttachmentDisplayName(props.attachment))),
|
|
82
|
+
props.onRemove && (React.createElement(Tooltip, { title: remove_tooltip, placement: "top", arrow: true },
|
|
83
|
+
React.createElement("span", null,
|
|
84
|
+
React.createElement(Button, { onClick: () => props.onRemove(props.attachment), size: "small", className: REMOVE_BUTTON_CLASS, "aria-label": remove_tooltip, sx: {
|
|
85
|
+
minWidth: 'unset',
|
|
86
|
+
padding: 0,
|
|
87
|
+
lineHeight: 0,
|
|
88
|
+
color: 'var(--jp-ui-font-color2)',
|
|
89
|
+
'&:hover': {
|
|
90
|
+
color: 'var(--jp-ui-font-color0)',
|
|
91
|
+
backgroundColor: 'transparent'
|
|
92
|
+
}
|
|
93
|
+
} },
|
|
94
|
+
React.createElement(CloseIcon, { fontSize: "small" })))))));
|
|
69
95
|
}
|
package/lib/components/chat.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
|
4
4
|
import { IInputToolbarRegistry } from './input';
|
|
5
5
|
import { IChatModel } from '../model';
|
|
6
6
|
import { IAttachmentOpenerRegistry, IChatCommandRegistry, IMessageFooterRegistry } from '../registers';
|
|
7
|
+
import { ChatArea } from '../types';
|
|
7
8
|
export declare function ChatBody(props: Chat.IChatBodyProps): JSX.Element;
|
|
8
9
|
export declare function Chat(props: Chat.IOptions): JSX.Element;
|
|
9
10
|
/**
|
|
@@ -42,6 +43,10 @@ export declare namespace Chat {
|
|
|
42
43
|
* The welcome message.
|
|
43
44
|
*/
|
|
44
45
|
welcomeMessage?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The area where the chat is displayed.
|
|
48
|
+
*/
|
|
49
|
+
area?: ChatArea;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* The options to build the Chat UI.
|
package/lib/components/chat.js
CHANGED
|
@@ -17,15 +17,16 @@ export function ChatBody(props) {
|
|
|
17
17
|
if (!inputToolbarRegistry) {
|
|
18
18
|
inputToolbarRegistry = InputToolbarRegistry.defaultToolbarRegistry();
|
|
19
19
|
}
|
|
20
|
+
// const horizontalPadding = props.area === 'main' ? 8 : 4;
|
|
21
|
+
const horizontalPadding = 4;
|
|
20
22
|
return (React.createElement(AttachmentOpenerContext.Provider, { value: props.attachmentOpenerRegistry },
|
|
21
|
-
React.createElement(ChatMessages, { rmRegistry: props.rmRegistry, model: model, chatCommandRegistry: props.chatCommandRegistry, inputToolbarRegistry: inputToolbarRegistry, messageFooterRegistry: props.messageFooterRegistry, welcomeMessage: props.welcomeMessage }),
|
|
23
|
+
React.createElement(ChatMessages, { rmRegistry: props.rmRegistry, model: model, chatCommandRegistry: props.chatCommandRegistry, inputToolbarRegistry: inputToolbarRegistry, messageFooterRegistry: props.messageFooterRegistry, welcomeMessage: props.welcomeMessage, area: props.area }),
|
|
22
24
|
React.createElement(ChatInput, { sx: {
|
|
23
|
-
paddingLeft:
|
|
24
|
-
paddingRight:
|
|
25
|
+
paddingLeft: horizontalPadding,
|
|
26
|
+
paddingRight: horizontalPadding,
|
|
25
27
|
paddingTop: 0,
|
|
26
|
-
paddingBottom: 0
|
|
27
|
-
|
|
28
|
-
}, model: model.input, chatCommandRegistry: props.chatCommandRegistry, toolbarRegistry: inputToolbarRegistry })));
|
|
28
|
+
paddingBottom: 0
|
|
29
|
+
}, model: model.input, chatCommandRegistry: props.chatCommandRegistry, toolbarRegistry: inputToolbarRegistry, area: props.area, chatModel: model })));
|
|
29
30
|
}
|
|
30
31
|
export function Chat(props) {
|
|
31
32
|
var _a;
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
import { addAboveIcon, addBelowIcon } from '@jupyterlab/ui-components';
|
|
6
|
-
import { Box } from '@mui/material';
|
|
6
|
+
import { Box, IconButton, Tooltip } from '@mui/material';
|
|
7
7
|
import React, { useEffect, useState } from 'react';
|
|
8
8
|
import { CopyButton } from './copy-button';
|
|
9
|
-
import { TooltippedIconButton } from '../mui-extras/tooltipped-icon-button';
|
|
10
9
|
import { replaceCellIcon } from '../../icons';
|
|
11
10
|
const CODE_TOOLBAR_CLASS = 'jp-chat-code-toolbar';
|
|
12
11
|
const CODE_TOOLBAR_ITEM_CLASS = 'jp-chat-code-toolbar-item';
|
|
@@ -53,8 +52,7 @@ export function CodeToolbar(props) {
|
|
|
53
52
|
alignItems: 'center',
|
|
54
53
|
padding: '2px 2px',
|
|
55
54
|
marginBottom: '1em',
|
|
56
|
-
border: '
|
|
57
|
-
borderTop: 'none'
|
|
55
|
+
border: 'none'
|
|
58
56
|
}, className: CODE_TOOLBAR_CLASS },
|
|
59
57
|
React.createElement(InsertAboveButton, { ...toolbarBtnProps, className: CODE_TOOLBAR_ITEM_CLASS }),
|
|
60
58
|
React.createElement(InsertBelowButton, { ...toolbarBtnProps, className: CODE_TOOLBAR_ITEM_CLASS }),
|
|
@@ -65,15 +63,29 @@ function InsertAboveButton(props) {
|
|
|
65
63
|
const tooltip = props.activeCellAvailable
|
|
66
64
|
? 'Insert above active cell'
|
|
67
65
|
: 'Insert above active cell (no active cell)';
|
|
68
|
-
return (React.createElement(
|
|
69
|
-
React.createElement(
|
|
66
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
67
|
+
React.createElement("span", null,
|
|
68
|
+
React.createElement(IconButton, { className: props.className, onClick: () => { var _a; return (_a = props.activeCellManager) === null || _a === void 0 ? void 0 : _a.insertAbove(props.content); }, disabled: !props.activeCellAvailable, "aria-label": tooltip, sx: {
|
|
69
|
+
lineHeight: 0,
|
|
70
|
+
'&.Mui-disabled': {
|
|
71
|
+
opacity: 0.5
|
|
72
|
+
}
|
|
73
|
+
} },
|
|
74
|
+
React.createElement(addAboveIcon.react, { height: "16px", width: "16px" })))));
|
|
70
75
|
}
|
|
71
76
|
function InsertBelowButton(props) {
|
|
72
77
|
const tooltip = props.activeCellAvailable
|
|
73
78
|
? 'Insert below active cell'
|
|
74
79
|
: 'Insert below active cell (no active cell)';
|
|
75
|
-
return (React.createElement(
|
|
76
|
-
React.createElement(
|
|
80
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
81
|
+
React.createElement("span", null,
|
|
82
|
+
React.createElement(IconButton, { className: props.className, disabled: !props.activeCellAvailable, onClick: () => { var _a; return (_a = props.activeCellManager) === null || _a === void 0 ? void 0 : _a.insertBelow(props.content); }, "aria-label": tooltip, sx: {
|
|
83
|
+
lineHeight: 0,
|
|
84
|
+
'&.Mui-disabled': {
|
|
85
|
+
opacity: 0.5
|
|
86
|
+
}
|
|
87
|
+
} },
|
|
88
|
+
React.createElement(addBelowIcon.react, { height: "16px", width: "16px" })))));
|
|
77
89
|
}
|
|
78
90
|
function ReplaceButton(props) {
|
|
79
91
|
var _a, _b;
|
|
@@ -99,6 +111,13 @@ function ReplaceButton(props) {
|
|
|
99
111
|
(_c = props.activeCellManager) === null || _c === void 0 ? void 0 : _c.replace(props.content);
|
|
100
112
|
}
|
|
101
113
|
};
|
|
102
|
-
return (React.createElement(
|
|
103
|
-
React.createElement(
|
|
114
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
115
|
+
React.createElement("span", null,
|
|
116
|
+
React.createElement(IconButton, { className: props.className, disabled: disabled, onClick: replace, "aria-label": tooltip, sx: {
|
|
117
|
+
lineHeight: 0,
|
|
118
|
+
'&.Mui-disabled': {
|
|
119
|
+
opacity: 0.5
|
|
120
|
+
}
|
|
121
|
+
} },
|
|
122
|
+
React.createElement(replaceCellIcon.react, { height: "16px", width: "16px" })))));
|
|
104
123
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React, { useState, useCallback, useRef } from 'react';
|
|
6
6
|
import { copyIcon } from '@jupyterlab/ui-components';
|
|
7
|
-
import {
|
|
7
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
8
8
|
var CopyStatus;
|
|
9
9
|
(function (CopyStatus) {
|
|
10
10
|
CopyStatus[CopyStatus["None"] = 0] = "None";
|
|
@@ -41,6 +41,14 @@ export function CopyButton(props) {
|
|
|
41
41
|
}
|
|
42
42
|
timeoutId.current = window.setTimeout(() => setCopyStatus(CopyStatus.None), 1000);
|
|
43
43
|
}, [copyStatus, props.value]);
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const tooltip = COPYBTN_TEXT_BY_STATUS[copyStatus];
|
|
45
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
46
|
+
React.createElement("span", null,
|
|
47
|
+
React.createElement(IconButton, { disabled: isCopyDisabled, className: props.className, onClick: copy, "aria-label": "Copy to clipboard", sx: {
|
|
48
|
+
lineHeight: 0,
|
|
49
|
+
'&.Mui-disabled': {
|
|
50
|
+
opacity: 0.5
|
|
51
|
+
}
|
|
52
|
+
} },
|
|
53
|
+
React.createElement(copyIcon.react, { height: "16px", width: "16px" })))));
|
|
46
54
|
}
|
package/lib/components/index.js
CHANGED
|
@@ -40,9 +40,14 @@ export function AttachButton(props) {
|
|
|
40
40
|
};
|
|
41
41
|
return (React.createElement(TooltippedButton, { onClick: onclick, tooltip: tooltip, buttonProps: {
|
|
42
42
|
size: 'small',
|
|
43
|
-
variant: '
|
|
43
|
+
variant: 'text',
|
|
44
44
|
title: tooltip,
|
|
45
45
|
className: ATTACH_BUTTON_CLASS
|
|
46
|
+
}, sx: {
|
|
47
|
+
width: '24px',
|
|
48
|
+
height: '24px',
|
|
49
|
+
minWidth: '24px',
|
|
50
|
+
color: 'gray'
|
|
46
51
|
} },
|
|
47
|
-
React.createElement(AttachFileIcon,
|
|
52
|
+
React.createElement(AttachFileIcon, { sx: { fontSize: '16px ' } })));
|
|
48
53
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Jupyter Development Team.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
6
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
6
7
|
import React from 'react';
|
|
7
|
-
import { TooltippedButton } from '../../mui-extras/tooltipped-button';
|
|
8
8
|
const CANCEL_BUTTON_CLASS = 'jp-chat-cancel-button';
|
|
9
9
|
/**
|
|
10
10
|
* The cancel button.
|
|
@@ -13,12 +13,14 @@ export function CancelButton(props) {
|
|
|
13
13
|
if (!props.model.cancel) {
|
|
14
14
|
return React.createElement(React.Fragment, null);
|
|
15
15
|
}
|
|
16
|
-
const tooltip = 'Cancel
|
|
17
|
-
return (React.createElement(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const tooltip = 'Cancel editing';
|
|
17
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
18
|
+
React.createElement("span", null,
|
|
19
|
+
React.createElement(IconButton, { onClick: props.model.cancel, className: CANCEL_BUTTON_CLASS, "aria-label": tooltip, sx: {
|
|
20
|
+
width: '24px',
|
|
21
|
+
height: '24px',
|
|
22
|
+
padding: 0,
|
|
23
|
+
lineHeight: 0
|
|
24
|
+
} },
|
|
25
|
+
React.createElement(CloseIcon, { sx: { fontSize: '16px' } })))));
|
|
24
26
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import CheckIcon from '@mui/icons-material/Check';
|
|
6
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
8
|
+
const SAVE_EDIT_BUTTON_CLASS = 'jp-chat-save-edit-button';
|
|
9
|
+
/**
|
|
10
|
+
* The save edit button.
|
|
11
|
+
*/
|
|
12
|
+
export function SaveEditButton(props) {
|
|
13
|
+
const { model, chatCommandRegistry, edit } = props;
|
|
14
|
+
// Don't show this button when not in edit mode
|
|
15
|
+
if (!edit) {
|
|
16
|
+
return React.createElement(React.Fragment, null);
|
|
17
|
+
}
|
|
18
|
+
const [disabled, setDisabled] = useState(false);
|
|
19
|
+
const tooltip = 'Save edits';
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
var _a;
|
|
22
|
+
const inputChanged = () => {
|
|
23
|
+
const inputExist = !!model.value.trim() || model.attachments.length;
|
|
24
|
+
setDisabled(!inputExist);
|
|
25
|
+
};
|
|
26
|
+
model.valueChanged.connect(inputChanged);
|
|
27
|
+
(_a = model.attachmentsChanged) === null || _a === void 0 ? void 0 : _a.connect(inputChanged);
|
|
28
|
+
inputChanged();
|
|
29
|
+
return () => {
|
|
30
|
+
var _a;
|
|
31
|
+
model.valueChanged.disconnect(inputChanged);
|
|
32
|
+
(_a = model.attachmentsChanged) === null || _a === void 0 ? void 0 : _a.disconnect(inputChanged);
|
|
33
|
+
};
|
|
34
|
+
}, [model]);
|
|
35
|
+
async function save() {
|
|
36
|
+
await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
|
|
37
|
+
model.send(model.value);
|
|
38
|
+
}
|
|
39
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
40
|
+
React.createElement("span", null,
|
|
41
|
+
React.createElement(IconButton, { onClick: save, disabled: disabled, className: SAVE_EDIT_BUTTON_CLASS, "aria-label": tooltip, sx: {
|
|
42
|
+
width: '24px',
|
|
43
|
+
height: '24px',
|
|
44
|
+
padding: 0,
|
|
45
|
+
lineHeight: 0,
|
|
46
|
+
'&.Mui-disabled': {
|
|
47
|
+
opacity: 0.5
|
|
48
|
+
}
|
|
49
|
+
} },
|
|
50
|
+
React.createElement(CheckIcon, { sx: { fontSize: '16px' } })))));
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { InputToolbarRegistry } from '../toolbar-registry';
|
|
3
3
|
/**
|
|
4
|
-
* The send button
|
|
4
|
+
* The send button.
|
|
5
5
|
*/
|
|
6
6
|
export declare function SendButton(props: InputToolbarRegistry.IToolbarItemProps): JSX.Element;
|
|
@@ -2,35 +2,21 @@
|
|
|
2
2
|
* Copyright (c) Jupyter Development Team.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
9
|
-
import { TooltippedButton } from '../../mui-extras/tooltipped-button';
|
|
10
|
-
import { includeSelectionIcon } from '../../../icons';
|
|
5
|
+
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
|
6
|
+
import { Button, Tooltip } from '@mui/material';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
11
8
|
const SEND_BUTTON_CLASS = 'jp-chat-send-button';
|
|
12
|
-
const SEND_INCLUDE_OPENER_CLASS = 'jp-chat-send-include-opener';
|
|
13
|
-
const SEND_INCLUDE_LI_CLASS = 'jp-chat-send-include';
|
|
14
9
|
/**
|
|
15
|
-
* The send button
|
|
10
|
+
* The send button.
|
|
16
11
|
*/
|
|
17
12
|
export function SendButton(props) {
|
|
18
|
-
const { model, chatCommandRegistry } = props;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
const { model, chatModel, chatCommandRegistry, edit } = props;
|
|
14
|
+
// Don't show this button when in edit mode
|
|
15
|
+
if (edit) {
|
|
16
|
+
return React.createElement(React.Fragment, null);
|
|
17
|
+
}
|
|
23
18
|
const [disabled, setDisabled] = useState(false);
|
|
24
19
|
const [tooltip, setTooltip] = useState('');
|
|
25
|
-
const openMenu = useCallback((el) => {
|
|
26
|
-
setMenuAnchorEl(el);
|
|
27
|
-
setMenuOpen(true);
|
|
28
|
-
}, []);
|
|
29
|
-
const closeMenu = useCallback(() => {
|
|
30
|
-
setMenuOpen(false);
|
|
31
|
-
}, []);
|
|
32
|
-
const [selectionTooltip, setSelectionTooltip] = useState('');
|
|
33
|
-
const [disableInclude, setDisableInclude] = useState(true);
|
|
34
20
|
useEffect(() => {
|
|
35
21
|
var _a;
|
|
36
22
|
const inputChanged = () => {
|
|
@@ -54,110 +40,37 @@ export function SendButton(props) {
|
|
|
54
40
|
(_b = model.configChanged) === null || _b === void 0 ? void 0 : _b.disconnect(configChanged);
|
|
55
41
|
};
|
|
56
42
|
}, [model]);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
/**
|
|
59
|
-
* Enable or disable the include selection button, and adapt the tooltip.
|
|
60
|
-
*/
|
|
61
|
-
const toggleIncludeState = () => {
|
|
62
|
-
setDisableInclude(!((selectionWatcher === null || selectionWatcher === void 0 ? void 0 : selectionWatcher.selection) || (activeCellManager === null || activeCellManager === void 0 ? void 0 : activeCellManager.available)));
|
|
63
|
-
const tooltip = (selectionWatcher === null || selectionWatcher === void 0 ? void 0 : selectionWatcher.selection)
|
|
64
|
-
? `${selectionWatcher.selection.numLines} line(s) selected`
|
|
65
|
-
: (activeCellManager === null || activeCellManager === void 0 ? void 0 : activeCellManager.available)
|
|
66
|
-
? 'Code from 1 active cell'
|
|
67
|
-
: 'No selection or active cell';
|
|
68
|
-
setSelectionTooltip(tooltip);
|
|
69
|
-
};
|
|
70
|
-
if (!hideIncludeSelection) {
|
|
71
|
-
selectionWatcher === null || selectionWatcher === void 0 ? void 0 : selectionWatcher.selectionChanged.connect(toggleIncludeState);
|
|
72
|
-
activeCellManager === null || activeCellManager === void 0 ? void 0 : activeCellManager.availabilityChanged.connect(toggleIncludeState);
|
|
73
|
-
toggleIncludeState();
|
|
74
|
-
}
|
|
75
|
-
return () => {
|
|
76
|
-
selectionWatcher === null || selectionWatcher === void 0 ? void 0 : selectionWatcher.selectionChanged.disconnect(toggleIncludeState);
|
|
77
|
-
activeCellManager === null || activeCellManager === void 0 ? void 0 : activeCellManager.availabilityChanged.disconnect(toggleIncludeState);
|
|
78
|
-
};
|
|
79
|
-
}, [activeCellManager, selectionWatcher]);
|
|
80
43
|
async function send() {
|
|
44
|
+
// Run all command providers
|
|
81
45
|
await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
|
|
88
|
-
let language;
|
|
89
|
-
if (selectionWatcher === null || selectionWatcher === void 0 ? void 0 : selectionWatcher.selection) {
|
|
90
|
-
// Append the selected text if exists.
|
|
91
|
-
source = selectionWatcher.selection.text;
|
|
92
|
-
language = selectionWatcher.selection.language;
|
|
93
|
-
}
|
|
94
|
-
else if (activeCellManager === null || activeCellManager === void 0 ? void 0 : activeCellManager.available) {
|
|
95
|
-
// Append the active cell content if exists.
|
|
96
|
-
const content = activeCellManager.getContent(false);
|
|
97
|
-
source = content.source;
|
|
98
|
-
language = content === null || content === void 0 ? void 0 : content.language;
|
|
99
|
-
}
|
|
100
|
-
let content = model.value;
|
|
101
|
-
if (source) {
|
|
102
|
-
content += `
|
|
103
|
-
|
|
104
|
-
\`\`\`${language !== null && language !== void 0 ? language : ''}
|
|
105
|
-
${source}
|
|
106
|
-
\`\`\`
|
|
107
|
-
`;
|
|
108
|
-
}
|
|
109
|
-
model.send(content);
|
|
110
|
-
closeMenu();
|
|
46
|
+
// send message through chat model
|
|
47
|
+
await (chatModel === null || chatModel === void 0 ? void 0 : chatModel.sendMessage({
|
|
48
|
+
body: model.value
|
|
49
|
+
}));
|
|
50
|
+
// clear input model value & re-focus
|
|
111
51
|
model.value = '';
|
|
52
|
+
model.focus();
|
|
112
53
|
}
|
|
113
|
-
return (React.createElement(
|
|
114
|
-
React.createElement(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (e.key !== 'Enter' && e.key !== ' ') {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
openMenu(e.currentTarget);
|
|
131
|
-
// stopping propagation of this event prevents the prompt from being
|
|
132
|
-
// sent when the dropdown button is selected and clicked via 'Enter'.
|
|
133
|
-
e.stopPropagation();
|
|
134
|
-
},
|
|
135
|
-
className: SEND_INCLUDE_OPENER_CLASS
|
|
136
|
-
} },
|
|
137
|
-
React.createElement(KeyboardArrowDown, null)),
|
|
138
|
-
React.createElement(Menu, { open: menuOpen, onClose: closeMenu, anchorEl: menuAnchorEl, anchorOrigin: {
|
|
139
|
-
vertical: 'top',
|
|
140
|
-
horizontal: 'right'
|
|
141
|
-
}, transformOrigin: {
|
|
142
|
-
vertical: 'bottom',
|
|
143
|
-
horizontal: 'right'
|
|
144
|
-
}, sx: {
|
|
145
|
-
'& .MuiMenuItem-root': {
|
|
146
|
-
display: 'flex',
|
|
147
|
-
alignItems: 'center',
|
|
148
|
-
gap: '8px'
|
|
54
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
55
|
+
React.createElement("span", null,
|
|
56
|
+
React.createElement(Button, { onClick: send, disabled: disabled, size: "small", variant: "contained", className: SEND_BUTTON_CLASS, "aria-label": tooltip, sx: {
|
|
57
|
+
backgroundColor: 'var(--jp-brand-color1)',
|
|
58
|
+
color: 'white',
|
|
59
|
+
minWidth: '24px',
|
|
60
|
+
width: '24px',
|
|
61
|
+
height: '24px',
|
|
62
|
+
borderRadius: '4px',
|
|
63
|
+
boxShadow: 'none',
|
|
64
|
+
lineHeight: 0,
|
|
65
|
+
'&:hover': {
|
|
66
|
+
backgroundColor: 'var(--jp-brand-color0)',
|
|
67
|
+
boxShadow: 'none'
|
|
149
68
|
},
|
|
150
|
-
'
|
|
151
|
-
|
|
69
|
+
'&.Mui-disabled': {
|
|
70
|
+
backgroundColor: 'var(--jp-border-color2)',
|
|
71
|
+
color: 'var(--jp-ui-font-color3)',
|
|
72
|
+
opacity: 0.5
|
|
152
73
|
}
|
|
153
74
|
} },
|
|
154
|
-
React.createElement(
|
|
155
|
-
sendWithSelection();
|
|
156
|
-
// prevent sending second message with no selection
|
|
157
|
-
e.stopPropagation();
|
|
158
|
-
}, disabled: disableInclude, className: SEND_INCLUDE_LI_CLASS },
|
|
159
|
-
React.createElement(includeSelectionIcon.react, null),
|
|
160
|
-
React.createElement(Box, null,
|
|
161
|
-
React.createElement(Typography, { display: "block" }, "Send message with selection"),
|
|
162
|
-
React.createElement(Typography, { display: "block", sx: { opacity: 0.618 } }, selectionTooltip))))))));
|
|
75
|
+
React.createElement(ArrowUpwardIcon, { sx: { fontSize: '16px' } })))));
|
|
163
76
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import StopIcon from '@mui/icons-material/Stop';
|
|
6
|
+
import { Button, Tooltip } from '@mui/material';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
8
|
+
const STOP_BUTTON_CLASS = 'jp-chat-stop-button';
|
|
9
|
+
/**
|
|
10
|
+
* The stop button.
|
|
11
|
+
*/
|
|
12
|
+
export function StopButton(props) {
|
|
13
|
+
const { chatModel } = props;
|
|
14
|
+
const [disabled, setDisabled] = useState(true);
|
|
15
|
+
const tooltip = 'Stop generating';
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
var _a;
|
|
18
|
+
if (!chatModel) {
|
|
19
|
+
setDisabled(true);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const checkWriters = () => {
|
|
23
|
+
// Check if there's at least one AI agent writer (bot)
|
|
24
|
+
const hasAIWriter = chatModel.writers.some(writer => writer.user.bot);
|
|
25
|
+
setDisabled(!hasAIWriter);
|
|
26
|
+
};
|
|
27
|
+
// Check initially
|
|
28
|
+
checkWriters();
|
|
29
|
+
// Listen to writers changes
|
|
30
|
+
(_a = chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.connect(checkWriters);
|
|
31
|
+
return () => {
|
|
32
|
+
var _a;
|
|
33
|
+
(_a = chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.disconnect(checkWriters);
|
|
34
|
+
};
|
|
35
|
+
}, [chatModel]);
|
|
36
|
+
function stop() {
|
|
37
|
+
// TODO: Implement stop functionality
|
|
38
|
+
// This will need to be implemented based on how the chat model handles stopping AI responses
|
|
39
|
+
console.log('Stop button clicked');
|
|
40
|
+
}
|
|
41
|
+
return (React.createElement(Tooltip, { title: tooltip, placement: "top", arrow: true },
|
|
42
|
+
React.createElement("span", null,
|
|
43
|
+
React.createElement(Button, { onClick: stop, disabled: disabled, size: "small", variant: "contained", className: STOP_BUTTON_CLASS, "aria-label": tooltip, sx: {
|
|
44
|
+
backgroundColor: 'var(--jp-error-color1)',
|
|
45
|
+
color: 'white',
|
|
46
|
+
minWidth: '24px',
|
|
47
|
+
width: '24px',
|
|
48
|
+
height: '24px',
|
|
49
|
+
borderRadius: '4px',
|
|
50
|
+
boxShadow: 'none',
|
|
51
|
+
lineHeight: 0,
|
|
52
|
+
'&:hover': {
|
|
53
|
+
backgroundColor: 'var(--jp-error-color0)',
|
|
54
|
+
boxShadow: 'none'
|
|
55
|
+
},
|
|
56
|
+
'&.Mui-disabled': {
|
|
57
|
+
backgroundColor: 'var(--jp-border-color2)',
|
|
58
|
+
color: 'var(--jp-ui-font-color3)',
|
|
59
|
+
opacity: 0.5
|
|
60
|
+
}
|
|
61
|
+
} },
|
|
62
|
+
React.createElement(StopIcon, { sx: { fontSize: '16px' } })))));
|
|
63
|
+
}
|