@jupyter/chat 0.19.0-alpha.1 → 0.19.0-alpha.3

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 (73) hide show
  1. package/lib/components/attachments.js +3 -3
  2. package/lib/components/chat.d.ts +3 -7
  3. package/lib/components/chat.js +9 -5
  4. package/lib/components/code-blocks/code-toolbar.js +8 -28
  5. package/lib/components/code-blocks/copy-button.js +4 -11
  6. package/lib/components/input/buttons/attach-button.js +3 -10
  7. package/lib/components/input/buttons/cancel-button.js +6 -10
  8. package/lib/components/input/buttons/save-edit-button.js +6 -13
  9. package/lib/components/input/buttons/send-button.js +8 -23
  10. package/lib/components/input/buttons/stop-button.js +6 -23
  11. package/lib/components/input/chat-input.d.ts +0 -20
  12. package/lib/components/input/chat-input.js +22 -22
  13. package/lib/components/messages/footer.d.ts +5 -5
  14. package/lib/components/messages/footer.js +7 -2
  15. package/lib/components/messages/header.js +10 -8
  16. package/lib/components/messages/message-renderer.d.ts +0 -10
  17. package/lib/components/messages/message-renderer.js +5 -3
  18. package/lib/components/messages/message.d.ts +8 -4
  19. package/lib/components/messages/message.js +4 -2
  20. package/lib/components/messages/messages.d.ts +1 -39
  21. package/lib/components/messages/messages.js +31 -13
  22. package/lib/components/messages/navigation.d.ts +1 -2
  23. package/lib/components/messages/navigation.js +2 -1
  24. package/lib/components/messages/toolbar.js +12 -26
  25. package/lib/components/messages/welcome.d.ts +10 -2
  26. package/lib/components/messages/welcome.js +2 -1
  27. package/lib/components/mui-extras/tooltipped-button.d.ts +6 -0
  28. package/lib/components/mui-extras/tooltipped-button.js +6 -7
  29. package/lib/components/mui-extras/tooltipped-icon-button.d.ts +12 -17
  30. package/lib/components/mui-extras/tooltipped-icon-button.js +10 -7
  31. package/lib/context.d.ts +3 -2
  32. package/lib/context.js +9 -2
  33. package/lib/index.d.ts +1 -0
  34. package/lib/index.js +1 -0
  35. package/lib/theme-provider.d.ts +19 -0
  36. package/lib/theme-provider.js +74 -3
  37. package/lib/tokens.d.ts +11 -0
  38. package/lib/tokens.js +9 -0
  39. package/lib/types.d.ts +4 -0
  40. package/lib/widgets/chat-widget.d.ts +9 -0
  41. package/lib/widgets/chat-widget.js +44 -1
  42. package/lib/widgets/multichat-panel.d.ts +1 -6
  43. package/lib/widgets/multichat-panel.js +4 -14
  44. package/package.json +3 -1
  45. package/src/components/attachments.tsx +3 -3
  46. package/src/components/chat.tsx +13 -24
  47. package/src/components/code-blocks/code-toolbar.tsx +29 -55
  48. package/src/components/code-blocks/copy-button.tsx +13 -20
  49. package/src/components/input/buttons/attach-button.tsx +4 -12
  50. package/src/components/input/buttons/cancel-button.tsx +11 -18
  51. package/src/components/input/buttons/save-edit-button.tsx +13 -22
  52. package/src/components/input/buttons/send-button.tsx +16 -34
  53. package/src/components/input/buttons/stop-button.tsx +13 -34
  54. package/src/components/input/chat-input.tsx +25 -44
  55. package/src/components/messages/footer.tsx +12 -10
  56. package/src/components/messages/header.tsx +23 -17
  57. package/src/components/messages/message-renderer.tsx +5 -13
  58. package/src/components/messages/message.tsx +4 -7
  59. package/src/components/messages/messages.tsx +73 -97
  60. package/src/components/messages/navigation.tsx +3 -3
  61. package/src/components/messages/toolbar.tsx +19 -33
  62. package/src/components/messages/welcome.tsx +13 -2
  63. package/src/components/mui-extras/tooltipped-button.tsx +10 -5
  64. package/src/components/mui-extras/tooltipped-icon-button.tsx +19 -24
  65. package/src/context.ts +15 -5
  66. package/src/index.ts +1 -0
  67. package/src/theme-provider.ts +95 -3
  68. package/src/tokens.ts +24 -0
  69. package/src/types.ts +4 -0
  70. package/src/widgets/chat-widget.tsx +52 -1
  71. package/src/widgets/multichat-panel.tsx +12 -27
  72. package/style/chat.css +10 -0
  73. package/style/input.css +4 -0
@@ -4,10 +4,10 @@
4
4
  */
5
5
  import CloseIcon from '@mui/icons-material/Close';
6
6
  import { Box, Button, Tooltip } from '@mui/material';
7
- import React, { useContext } from 'react';
7
+ import React from 'react';
8
8
  import { PathExt } from '@jupyterlab/coreutils';
9
9
  import { UUID } from '@lumino/coreutils';
10
- import { AttachmentOpenerContext } from '../context';
10
+ import { useChatContext } from '../context';
11
11
  const ATTACHMENT_CLASS = 'jp-chat-attachment';
12
12
  const ATTACHMENT_CLICKABLE_CLASS = 'jp-chat-attachment-clickable';
13
13
  const REMOVE_BUTTON_CLASS = 'jp-chat-attachment-remove';
@@ -52,7 +52,7 @@ export function AttachmentPreviewList(props) {
52
52
  */
53
53
  export function AttachmentPreview(props) {
54
54
  const remove_tooltip = 'Remove attachment';
55
- const attachmentOpenerRegistry = useContext(AttachmentOpenerContext);
55
+ const { attachmentOpenerRegistry } = useChatContext();
56
56
  const isClickable = !!(attachmentOpenerRegistry === null || attachmentOpenerRegistry === void 0 ? void 0 : attachmentOpenerRegistry.get(props.attachment.type));
57
57
  return (React.createElement(Box, { className: ATTACHMENT_CLASS, sx: {
58
58
  border: '1px solid var(--jp-border-color1)',
@@ -5,7 +5,7 @@ import { IInputToolbarRegistry } from './input';
5
5
  import { IChatModel } from '../model';
6
6
  import { IAttachmentOpenerRegistry, IChatCommandRegistry, IMessageFooterRegistry } from '../registers';
7
7
  import { ChatArea } from '../types';
8
- export declare function ChatBody(props: Chat.IChatBodyProps): JSX.Element;
8
+ export declare function ChatBody(props: Chat.IChatProps): JSX.Element;
9
9
  export declare function Chat(props: Chat.IOptions): JSX.Element;
10
10
  /**
11
11
  * The chat UI namespace
@@ -14,7 +14,7 @@ export declare namespace Chat {
14
14
  /**
15
15
  * The props for the chat body component.
16
16
  */
17
- interface IChatBodyProps {
17
+ interface IChatProps {
18
18
  /**
19
19
  * The chat model.
20
20
  */
@@ -51,15 +51,11 @@ export declare namespace Chat {
51
51
  /**
52
52
  * The options to build the Chat UI.
53
53
  */
54
- interface IOptions extends IChatBodyProps {
54
+ interface IOptions extends IChatProps {
55
55
  /**
56
56
  * The theme manager.
57
57
  */
58
58
  themeManager?: IThemeManager | null;
59
- /**
60
- * The view to render.
61
- */
62
- chatView?: View;
63
59
  /**
64
60
  * A settings panel that can be used for dedicated settings (e.g. jupyter-ai)
65
61
  */
@@ -10,7 +10,7 @@ import React, { 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 { AttachmentOpenerContext } from '../context';
13
+ import { ChatReactContext } from '../context';
14
14
  export function ChatBody(props) {
15
15
  const { model } = props;
16
16
  let { inputToolbarRegistry } = props;
@@ -19,18 +19,22 @@ export function ChatBody(props) {
19
19
  }
20
20
  // const horizontalPadding = props.area === 'main' ? 8 : 4;
21
21
  const horizontalPadding = 4;
22
- return (React.createElement(AttachmentOpenerContext.Provider, { value: props.attachmentOpenerRegistry },
23
- React.createElement(ChatMessages, { rmRegistry: props.rmRegistry, model: model, chatCommandRegistry: props.chatCommandRegistry, inputToolbarRegistry: inputToolbarRegistry, messageFooterRegistry: props.messageFooterRegistry, welcomeMessage: props.welcomeMessage, area: props.area }),
22
+ const contextValue = {
23
+ ...props,
24
+ inputToolbarRegistry
25
+ };
26
+ return (React.createElement(ChatReactContext.Provider, { value: contextValue },
27
+ React.createElement(ChatMessages, null),
24
28
  React.createElement(ChatInput, { sx: {
25
29
  paddingLeft: horizontalPadding,
26
30
  paddingRight: horizontalPadding,
27
31
  paddingTop: 0,
28
32
  paddingBottom: 0
29
- }, model: model.input, chatCommandRegistry: props.chatCommandRegistry, toolbarRegistry: inputToolbarRegistry, area: props.area, chatModel: model })));
33
+ }, model: model.input })));
30
34
  }
31
35
  export function Chat(props) {
32
36
  var _a;
33
- const [view, setView] = useState(props.chatView || Chat.View.chat);
37
+ const [view, setView] = useState(Chat.View.chat);
34
38
  return (React.createElement(JlThemeProvider, { themeManager: (_a = props.themeManager) !== null && _a !== void 0 ? _a : null },
35
39
  React.createElement(Box
36
40
  // Add .jp-ThemedContainer for CSS compatibility in both JL <4.3.0 and >=4.3.0.
@@ -3,9 +3,10 @@
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, IconButton, Tooltip } from '@mui/material';
6
+ import { Box } from '@mui/material';
7
7
  import React, { useEffect, useState } from 'react';
8
8
  import { CopyButton } from './copy-button';
9
+ import { TooltippedIconButton } from '../mui-extras';
9
10
  import { replaceCellIcon } from '../../icons';
10
11
  const CODE_TOOLBAR_CLASS = 'jp-chat-code-toolbar';
11
12
  const CODE_TOOLBAR_ITEM_CLASS = 'jp-chat-code-toolbar-item';
@@ -63,29 +64,15 @@ function InsertAboveButton(props) {
63
64
  const tooltip = props.activeCellAvailable
64
65
  ? 'Insert above active cell'
65
66
  : 'Insert above active cell (no active cell)';
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" })))));
67
+ return (React.createElement(TooltippedIconButton, { className: props.className, tooltip: tooltip, onClick: () => { var _a; return (_a = props.activeCellManager) === null || _a === void 0 ? void 0 : _a.insertAbove(props.content); }, disabled: !props.activeCellAvailable, inputToolbar: false },
68
+ React.createElement(addAboveIcon.react, { height: "16px", width: "16px" })));
75
69
  }
76
70
  function InsertBelowButton(props) {
77
71
  const tooltip = props.activeCellAvailable
78
72
  ? 'Insert below active cell'
79
73
  : 'Insert below active cell (no active cell)';
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" })))));
74
+ return (React.createElement(TooltippedIconButton, { className: props.className, tooltip: tooltip, disabled: !props.activeCellAvailable, onClick: () => { var _a; return (_a = props.activeCellManager) === null || _a === void 0 ? void 0 : _a.insertBelow(props.content); }, inputToolbar: false },
75
+ React.createElement(addBelowIcon.react, { height: "16px", width: "16px" })));
89
76
  }
90
77
  function ReplaceButton(props) {
91
78
  var _a, _b;
@@ -111,13 +98,6 @@ function ReplaceButton(props) {
111
98
  (_c = props.activeCellManager) === null || _c === void 0 ? void 0 : _c.replace(props.content);
112
99
  }
113
100
  };
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" })))));
101
+ return (React.createElement(TooltippedIconButton, { className: props.className, tooltip: tooltip, disabled: disabled, onClick: replace, inputToolbar: false },
102
+ React.createElement(replaceCellIcon.react, { height: "16px", width: "16px" })));
123
103
  }
@@ -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 React, { useState, useCallback, useRef } from 'react';
6
5
  import { copyIcon } from '@jupyterlab/ui-components';
7
- import { IconButton, Tooltip } from '@mui/material';
6
+ import React, { useState, useCallback, useRef } from 'react';
7
+ import { TooltippedIconButton } from '../mui-extras';
8
8
  var CopyStatus;
9
9
  (function (CopyStatus) {
10
10
  CopyStatus[CopyStatus["None"] = 0] = "None";
@@ -42,13 +42,6 @@ export function CopyButton(props) {
42
42
  timeoutId.current = window.setTimeout(() => setCopyStatus(CopyStatus.None), 1000);
43
43
  }, [copyStatus, props.value]);
44
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" })))));
45
+ return (React.createElement(TooltippedIconButton, { disabled: isCopyDisabled, className: props.className, tooltip: tooltip, placement: "top", onClick: copy, "aria-label": "Copy to clipboard", inputToolbar: false },
46
+ React.createElement(copyIcon.react, { height: "16px", width: "16px" })));
54
47
  }
@@ -5,7 +5,7 @@
5
5
  import { FileDialog } from '@jupyterlab/filebrowser';
6
6
  import AttachFileIcon from '@mui/icons-material/AttachFile';
7
7
  import React from 'react';
8
- import { TooltippedButton } from '../../mui-extras/tooltipped-button';
8
+ import { TooltippedIconButton } from '../../mui-extras';
9
9
  const ATTACH_BUTTON_CLASS = 'jp-chat-attach-button';
10
10
  /**
11
11
  * The attach button.
@@ -38,16 +38,9 @@ export function AttachButton(props) {
38
38
  console.warn('Error selecting files to attach', e);
39
39
  }
40
40
  };
41
- return (React.createElement(TooltippedButton, { onClick: onclick, tooltip: tooltip, buttonProps: {
42
- size: 'small',
43
- variant: 'text',
41
+ return (React.createElement(TooltippedIconButton, { onClick: onclick, tooltip: tooltip, buttonProps: {
44
42
  title: tooltip,
45
43
  className: ATTACH_BUTTON_CLASS
46
- }, sx: {
47
- width: '24px',
48
- height: '24px',
49
- minWidth: '24px',
50
- color: 'gray'
51
44
  } },
52
- React.createElement(AttachFileIcon, { sx: { fontSize: '16px ' } })));
45
+ React.createElement(AttachFileIcon, null)));
53
46
  }
@@ -3,8 +3,8 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import CloseIcon from '@mui/icons-material/Close';
6
- import { IconButton, Tooltip } from '@mui/material';
7
6
  import React from 'react';
7
+ import { TooltippedIconButton } from '../../mui-extras';
8
8
  const CANCEL_BUTTON_CLASS = 'jp-chat-cancel-button';
9
9
  /**
10
10
  * The cancel button.
@@ -14,13 +14,9 @@ export function CancelButton(props) {
14
14
  return React.createElement(React.Fragment, null);
15
15
  }
16
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' } })))));
17
+ return (React.createElement(TooltippedIconButton, { onClick: props.model.cancel, tooltip: tooltip, buttonProps: {
18
+ title: tooltip,
19
+ className: CANCEL_BUTTON_CLASS
20
+ } },
21
+ React.createElement(CloseIcon, null)));
26
22
  }
@@ -3,8 +3,8 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import CheckIcon from '@mui/icons-material/Check';
6
- import { IconButton, Tooltip } from '@mui/material';
7
6
  import React, { useEffect, useState } from 'react';
7
+ import { TooltippedIconButton } from '../../mui-extras';
8
8
  const SAVE_EDIT_BUTTON_CLASS = 'jp-chat-save-edit-button';
9
9
  /**
10
10
  * The save edit button.
@@ -36,16 +36,9 @@ export function SaveEditButton(props) {
36
36
  await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
37
37
  model.send(model.value);
38
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' } })))));
39
+ return (React.createElement(TooltippedIconButton, { onClick: save, tooltip: tooltip, disabled: disabled, buttonProps: {
40
+ title: tooltip,
41
+ className: SAVE_EDIT_BUTTON_CLASS
42
+ }, "aria-label": tooltip },
43
+ React.createElement(CheckIcon, null)));
51
44
  }
@@ -3,8 +3,8 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
6
- import { Button, Tooltip } from '@mui/material';
7
6
  import React, { useEffect, useState } from 'react';
7
+ import { TooltippedIconButton } from '../../mui-extras';
8
8
  const SEND_BUTTON_CLASS = 'jp-chat-send-button';
9
9
  /**
10
10
  * The send button.
@@ -33,6 +33,8 @@ export function SendButton(props) {
33
33
  : 'Send message (ENTER)');
34
34
  };
35
35
  model.configChanged.connect(configChanged);
36
+ // Initialize the tooltip.
37
+ configChanged(model, model.config);
36
38
  return () => {
37
39
  var _a, _b;
38
40
  model.valueChanged.disconnect(inputChanged);
@@ -51,26 +53,9 @@ export function SendButton(props) {
51
53
  model.value = '';
52
54
  model.focus();
53
55
  }
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'
68
- },
69
- '&.Mui-disabled': {
70
- backgroundColor: 'var(--jp-border-color2)',
71
- color: 'var(--jp-ui-font-color3)',
72
- opacity: 0.5
73
- }
74
- } },
75
- React.createElement(ArrowUpwardIcon, { sx: { fontSize: '16px' } })))));
56
+ return (React.createElement(TooltippedIconButton, { onClick: send, tooltip: tooltip, disabled: disabled, buttonProps: {
57
+ title: tooltip,
58
+ className: SEND_BUTTON_CLASS
59
+ }, "aria-label": tooltip },
60
+ React.createElement(ArrowUpwardIcon, null)));
76
61
  }
@@ -3,8 +3,8 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import StopIcon from '@mui/icons-material/Stop';
6
- import { Button, Tooltip } from '@mui/material';
7
6
  import React, { useEffect, useState } from 'react';
7
+ import { TooltippedIconButton } from '../../mui-extras';
8
8
  const STOP_BUTTON_CLASS = 'jp-chat-stop-button';
9
9
  /**
10
10
  * The stop button.
@@ -38,26 +38,9 @@ export function StopButton(props) {
38
38
  // This will need to be implemented based on how the chat model handles stopping AI responses
39
39
  console.log('Stop button clicked');
40
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' } })))));
41
+ return (React.createElement(TooltippedIconButton, { onClick: stop, tooltip: tooltip, disabled: disabled, buttonProps: {
42
+ title: tooltip,
43
+ className: STOP_BUTTON_CLASS
44
+ }, "aria-label": tooltip },
45
+ React.createElement(StopIcon, null)));
63
46
  }
@@ -1,10 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { SxProps, Theme } from '@mui/material';
3
- import { IInputToolbarRegistry } from '.';
4
3
  import { IInputModel } from '../../input-model';
5
- import { IChatCommandRegistry } from '../../registers';
6
- import { ChatArea } from '../../types';
7
- import { IChatModel } from '../../model';
8
4
  export declare function ChatInput(props: ChatInput.IProps): JSX.Element;
9
5
  /**
10
6
  * The chat input namespace.
@@ -18,10 +14,6 @@ export declare namespace ChatInput {
18
14
  * The input model.
19
15
  */
20
16
  model: IInputModel;
21
- /**
22
- * The toolbar registry.
23
- */
24
- toolbarRegistry: IInputToolbarRegistry;
25
17
  /**
26
18
  * The function to be called to cancel editing.
27
19
  */
@@ -30,18 +22,6 @@ export declare namespace ChatInput {
30
22
  * Custom mui/material styles.
31
23
  */
32
24
  sx?: SxProps<Theme>;
33
- /**
34
- * Chat command registry.
35
- */
36
- chatCommandRegistry?: IChatCommandRegistry;
37
- /**
38
- * The area where the chat is displayed.
39
- */
40
- area?: ChatArea;
41
- /**
42
- * The chat model.
43
- */
44
- chatModel?: IChatModel;
45
25
  /**
46
26
  * Whether the input is in edit mode (editing an existing message).
47
27
  * Defaults to false (new message mode).
@@ -5,22 +5,24 @@
5
5
  import { Autocomplete, Box, TextField } from '@mui/material';
6
6
  import clsx from 'clsx';
7
7
  import React, { useEffect, useRef, useState } from 'react';
8
+ import { useChatCommands } from './use-chat-commands';
8
9
  import { AttachmentPreviewList } from '../attachments';
9
- import { useChatCommands } from '.';
10
+ import { useChatContext } from '../../context';
10
11
  import { InputWritingIndicator } from './writing-indicator';
11
12
  const INPUT_BOX_CLASS = 'jp-chat-input-container';
12
13
  const INPUT_TEXTFIELD_CLASS = 'jp-chat-input-textfield';
13
14
  const INPUT_TOOLBAR_CLASS = 'jp-chat-input-toolbar';
14
15
  export function ChatInput(props) {
15
16
  var _a;
16
- const { model, toolbarRegistry } = props;
17
+ const { model } = props;
18
+ const { area, chatCommandRegistry, inputToolbarRegistry } = useChatContext();
19
+ const chatModel = useChatContext().model;
17
20
  const [input, setInput] = useState(model.value);
18
21
  const inputRef = useRef();
19
- const chatCommands = useChatCommands(model, props.chatCommandRegistry);
22
+ const chatCommands = useChatCommands(model, chatCommandRegistry);
20
23
  const [sendWithShiftEnter, setSendWithShiftEnter] = useState((_a = model.config.sendWithShiftEnter) !== null && _a !== void 0 ? _a : false);
21
24
  const [attachments, setAttachments] = useState(model.attachments);
22
25
  const [toolbarElements, setToolbarElements] = useState([]);
23
- const [isFocused, setIsFocused] = useState(false);
24
26
  const [writers, setWriters] = useState([]);
25
27
  /**
26
28
  * Auto-focus the input when the component is first mounted.
@@ -69,20 +71,20 @@ export function ChatInput(props) {
69
71
  */
70
72
  useEffect(() => {
71
73
  const updateToolbar = () => {
72
- setToolbarElements(toolbarRegistry.getItems());
74
+ setToolbarElements((inputToolbarRegistry === null || inputToolbarRegistry === void 0 ? void 0 : inputToolbarRegistry.getItems()) || []);
73
75
  };
74
- toolbarRegistry.itemsChanged.connect(updateToolbar);
76
+ inputToolbarRegistry === null || inputToolbarRegistry === void 0 ? void 0 : inputToolbarRegistry.itemsChanged.connect(updateToolbar);
75
77
  updateToolbar();
76
78
  return () => {
77
- toolbarRegistry.itemsChanged.disconnect(updateToolbar);
79
+ inputToolbarRegistry === null || inputToolbarRegistry === void 0 ? void 0 : inputToolbarRegistry.itemsChanged.disconnect(updateToolbar);
78
80
  };
79
- }, [toolbarRegistry]);
81
+ }, [inputToolbarRegistry]);
80
82
  /**
81
83
  * Handle the changes in the writers list.
82
84
  */
83
85
  useEffect(() => {
84
86
  var _a;
85
- if (!props.chatModel) {
87
+ if (!chatModel) {
86
88
  return;
87
89
  }
88
90
  const updateWriters = (_, writers) => {
@@ -90,14 +92,14 @@ export function ChatInput(props) {
90
92
  setWriters(writers);
91
93
  };
92
94
  // Set initial writers state
93
- const initialWriters = props.chatModel.writers;
95
+ const initialWriters = chatModel.writers;
94
96
  setWriters(initialWriters);
95
- (_a = props.chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.connect(updateWriters);
97
+ (_a = chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.connect(updateWriters);
96
98
  return () => {
97
- var _a, _b;
98
- (_b = (_a = props.chatModel) === null || _a === void 0 ? void 0 : _a.writersChanged) === null || _b === void 0 ? void 0 : _b.disconnect(updateWriters);
99
+ var _a;
100
+ (_a = chatModel === null || chatModel === void 0 ? void 0 : chatModel.writersChanged) === null || _a === void 0 ? void 0 : _a.disconnect(updateWriters);
99
101
  };
100
- }, [props.chatModel]);
102
+ }, [chatModel]);
101
103
  const inputExists = !!input.trim();
102
104
  /**
103
105
  * `handleKeyDown()`: callback invoked when the user presses any key in the
@@ -106,7 +108,6 @@ export function ChatInput(props) {
106
108
  * component.
107
109
  */
108
110
  async function handleKeyDown(event) {
109
- var _a;
110
111
  /**
111
112
  * IMPORTANT: This statement ensures that arrow keys can be used to navigate
112
113
  * the multiline input when the chat commands menu is closed.
@@ -151,19 +152,17 @@ export function ChatInput(props) {
151
152
  if ((sendWithShiftEnter && event.shiftKey) ||
152
153
  (!sendWithShiftEnter && !event.shiftKey)) {
153
154
  // Run all command providers
154
- await ((_a = props.chatCommandRegistry) === null || _a === void 0 ? void 0 : _a.onSubmit(model));
155
+ await (chatCommandRegistry === null || chatCommandRegistry === void 0 ? void 0 : chatCommandRegistry.onSubmit(model));
155
156
  model.send(model.value);
156
157
  event.stopPropagation();
157
158
  event.preventDefault();
158
159
  }
159
160
  }
160
- const horizontalPadding = props.area === 'sidebar' ? 1.5 : 2;
161
+ const horizontalPadding = area === 'sidebar' ? 1.5 : 2;
161
162
  return (React.createElement(Box, { sx: props.sx, className: clsx(INPUT_BOX_CLASS), "data-input-id": model.id },
162
163
  React.createElement(Box, { sx: {
163
164
  border: '1px solid',
164
- borderColor: isFocused
165
- ? 'var(--jp-brand-color1)'
166
- : 'var(--jp-border-color1)',
165
+ borderColor: 'var(--jp-border-color1)',
167
166
  borderRadius: 2,
168
167
  transition: 'border-color 0.2s ease',
169
168
  display: 'flex',
@@ -186,9 +185,10 @@ export function ChatInput(props) {
186
185
  padding: 0
187
186
  }
188
187
  }
189
- }, renderInput: params => (React.createElement(TextField, { ...params, fullWidth: true, variant: "standard", className: INPUT_TEXTFIELD_CLASS, multiline: true, onKeyDown: handleKeyDown, placeholder: "Type a chat message, @ to mention...", inputRef: inputRef, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), onSelect: () => { var _a, _b; return (model.cursorIndex = (_b = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.selectionStart) !== null && _b !== void 0 ? _b : null); }, sx: {
188
+ }, renderInput: params => (React.createElement(TextField, { ...params, fullWidth: true, variant: "standard", className: INPUT_TEXTFIELD_CLASS, multiline: true, maxRows: 10, onKeyDown: handleKeyDown, placeholder: "Type a chat message, @ to mention...", inputRef: inputRef, onSelect: () => { var _a, _b; return (model.cursorIndex = (_b = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.selectionStart) !== null && _b !== void 0 ? _b : null); }, sx: {
190
189
  padding: 1.5,
191
190
  margin: 0,
191
+ boxSizing: 'border-box',
192
192
  backgroundColor: 'var(--jp-layout-color0)',
193
193
  transition: 'background-color 0.2s ease',
194
194
  '& .MuiInputBase-root': {
@@ -232,6 +232,6 @@ export function ChatInput(props) {
232
232
  borderColor: 'var(--jp-border-color1)',
233
233
  backgroundColor: 'var(--jp-layout-color0)',
234
234
  transition: 'background-color 0.2s ease'
235
- } }, toolbarElements.map((item, index) => (React.createElement(item.element, { key: index, model: model, chatCommandRegistry: props.chatCommandRegistry, chatModel: props.chatModel, edit: props.edit }))))),
235
+ } }, toolbarElements.map((item, index) => (React.createElement(item.element, { key: index, model: model, chatCommandRegistry: chatCommandRegistry, chatModel: chatModel, edit: props.edit }))))),
236
236
  React.createElement(InputWritingIndicator, { writers: writers })));
237
237
  }
@@ -1,16 +1,16 @@
1
1
  /// <reference types="react" />
2
- import { IMessageFooterRegistry, MessageFooterSectionProps } from '../../registers';
2
+ import { IChatMessage } from '../../types';
3
3
  /**
4
4
  * The chat footer component properties.
5
5
  */
6
- export interface IMessageFootersProps extends MessageFooterSectionProps {
6
+ export interface IMessageFootersProps {
7
7
  /**
8
- * The chat footer registry.
8
+ * The chat model.
9
9
  */
10
- registry: IMessageFooterRegistry;
10
+ message: IChatMessage;
11
11
  }
12
12
  /**
13
13
  * The chat footer component, which displays footer components on a row according to
14
14
  * their respective positions.
15
15
  */
16
- export declare function MessageFooterComponent(props: IMessageFootersProps): JSX.Element;
16
+ export declare function MessageFooterComponent(props: IMessageFootersProps): JSX.Element | null;
@@ -4,14 +4,19 @@
4
4
  */
5
5
  import { Box } from '@mui/material';
6
6
  import React from 'react';
7
+ import { useChatContext } from '../../context';
7
8
  /**
8
9
  * The chat footer component, which displays footer components on a row according to
9
10
  * their respective positions.
10
11
  */
11
12
  export function MessageFooterComponent(props) {
12
13
  var _a, _b, _c;
13
- const { message, model, registry } = props;
14
- const footer = registry.getFooter();
14
+ const { message } = props;
15
+ const { model, messageFooterRegistry } = useChatContext();
16
+ if (!messageFooterRegistry) {
17
+ return null;
18
+ }
19
+ const footer = messageFooterRegistry.getFooter();
15
20
  return (React.createElement(Box, { sx: { display: 'flex', justifyContent: 'space-between' } },
16
21
  ((_a = footer.left) === null || _a === void 0 ? void 0 : _a.component) ? (React.createElement(footer.left.component, { message: message, model: model })) : (React.createElement("div", null)),
17
22
  ((_b = footer.center) === null || _b === void 0 ? void 0 : _b.component) ? (React.createElement(footer.center.component, { message: message, model: model })) : (React.createElement("div", null)),
@@ -12,12 +12,14 @@ const MESSAGE_TIME_CLASS = 'jp-chat-message-time';
12
12
  */
13
13
  export function ChatMessageHeader(props) {
14
14
  var _a, _b;
15
- // Don't render header for stacked messages or current user messages
16
- if (props.message.stacked || props.isCurrentUser) {
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) {
17
18
  return React.createElement(React.Fragment, null);
18
19
  }
20
+ // Flag to display only the deleted or edited information (stacked message).
21
+ const onlyState = message.stacked && (message.deleted || message.edited);
19
22
  const [datetime, setDatetime] = useState({});
20
- const message = props.message;
21
23
  const sender = message.sender;
22
24
  /**
23
25
  * Effect: update cached datetime strings upon receiving a new message.
@@ -61,9 +63,9 @@ export function ChatMessageHeader(props) {
61
63
  '& > :not(:last-child)': {
62
64
  marginRight: 3
63
65
  },
64
- marginBottom: message.stacked ? '0px' : '12px'
66
+ marginBottom: message.stacked || props.isCurrentUser ? '0px' : '12px'
65
67
  } },
66
- avatar,
68
+ !props.isCurrentUser && !onlyState && avatar,
67
69
  React.createElement(Box, { sx: {
68
70
  display: 'flex',
69
71
  flexGrow: 1,
@@ -72,7 +74,7 @@ export function ChatMessageHeader(props) {
72
74
  alignItems: 'center'
73
75
  } },
74
76
  React.createElement(Box, { sx: { display: 'flex', alignItems: 'center' } },
75
- !message.stacked && (React.createElement(Typography, { sx: {
77
+ !onlyState && !props.isCurrentUser && (React.createElement(Typography, { sx: {
76
78
  fontWeight: 700,
77
79
  color: 'var(--jp-ui-font-color1)',
78
80
  paddingRight: '0.5em'
@@ -81,9 +83,9 @@ export function ChatMessageHeader(props) {
81
83
  fontStyle: 'italic',
82
84
  fontSize: 'var(--jp-content-font-size0)'
83
85
  } }, message.deleted ? '(message deleted)' : '(edited)'))),
84
- React.createElement(Typography, { className: MESSAGE_TIME_CLASS, sx: {
86
+ !onlyState && (React.createElement(Typography, { className: MESSAGE_TIME_CLASS, sx: {
85
87
  fontSize: '0.8em',
86
88
  color: 'var(--jp-ui-font-color2)',
87
89
  fontWeight: 300
88
- }, title: message.raw_time ? 'Unverified time' : '' }, `${datetime[message.time]}${message.raw_time ? '*' : ''}`))));
90
+ }, title: message.raw_time ? 'Unverified time' : '' }, `${datetime[message.time]}${message.raw_time ? '*' : ''}`)))));
89
91
  }