diff --git a/packages/jupyter-ai/src/components/chat-input.tsx b/packages/jupyter-ai/src/components/chat-input.tsx index 1c6a63730..7541b372b 100644 --- a/packages/jupyter-ai/src/components/chat-input.tsx +++ b/packages/jupyter-ai/src/components/chat-input.tsx @@ -2,13 +2,14 @@ import React from 'react'; import { Box, - IconButton, - Input, SxProps, + TextField, Theme, FormGroup, FormControlLabel, - Checkbox + Checkbox, + IconButton, + InputAdornment } from '@mui/material'; import SendIcon from '@mui/icons-material/Send'; @@ -25,23 +26,33 @@ type ChatInputProps = { }; export function ChatInput(props: ChatInputProps): JSX.Element { + function handleKeyDown(event: React.KeyboardEvent) { + if (event.key === 'Enter' && event.shiftKey) { + props.onSend(); + event.stopPropagation(); + event.preventDefault(); + } + } return ( - props.onChange(e.target.value)} + fullWidth + variant="outlined" multiline - sx={{ flexGrow: 1 }} + onKeyDown={handleKeyDown} + InputProps={{ + endAdornment: ( + + + + + + ), + }} /> - - - {props.hasSelection && ( diff --git a/packages/jupyter-ai/src/components/chat-messages.tsx b/packages/jupyter-ai/src/components/chat-messages.tsx index e9c05038b..fc2a4ea10 100644 --- a/packages/jupyter-ai/src/components/chat-messages.tsx +++ b/packages/jupyter-ai/src/components/chat-messages.tsx @@ -44,7 +44,7 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps) { {props.message.client.initials} @@ -85,8 +85,8 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps) { alignItems: 'center' }} > - {name} - + {name} + {props.timestamp} @@ -120,17 +120,20 @@ export function ChatMessages(props: ChatMessagesProps) { return ( :not(:last-child)': { borderBottom: '1px solid lightgrey' } }} + sx={{ '& > :not(:last-child)': { borderBottom: '1px solid var(--jp-border-color2)' } }} > {props.messages.map((message, i) => ( // extra div needed to ensure each bubble is on a new line - + !replaceSelection) } sx={{ - padding: 2 + paddingLeft: 4, + paddingRight: 4, + paddingTop: 2, + paddingBottom: 2, + borderTop: '1px solid var(--jp-border-color1)' }} /> diff --git a/packages/jupyter-ai/src/icons.ts b/packages/jupyter-ai/src/icons.ts index 9256e98e7..512412b68 100644 --- a/packages/jupyter-ai/src/icons.ts +++ b/packages/jupyter-ai/src/icons.ts @@ -3,8 +3,14 @@ import { LabIcon } from '@jupyterlab/ui-components'; import psychologySvgStr from '../style/icons/psychology.svg'; +import chatSvgStr from '../style/icons/chat.svg'; export const psychologyIcon = new LabIcon({ name: 'jupyter-ai::psychology', svgstr: psychologySvgStr }); + +export const chatIcon = new LabIcon({ + name: 'jupyter-ai::chat', + svgstr: chatSvgStr +}); \ No newline at end of file diff --git a/packages/jupyter-ai/src/index.ts b/packages/jupyter-ai/src/index.ts index a3cd1e252..d2bac311e 100644 --- a/packages/jupyter-ai/src/index.ts +++ b/packages/jupyter-ai/src/index.ts @@ -1,6 +1,7 @@ import { JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, + ILayoutRestorer } from '@jupyterlab/application'; import { INotebookTracker } from '@jupyterlab/notebook'; import { IEditorTracker } from '@jupyterlab/fileeditor'; @@ -50,12 +51,13 @@ const plugin: JupyterFrontEndPlugin = { id: 'jupyter_ai:plugin', autoStart: true, requires: [INotebookTracker, IEditorTracker], - optional: [IGlobalAwareness], + optional: [IGlobalAwareness, ILayoutRestorer], activate: async ( app: JupyterFrontEnd, notebookTracker: INotebookTracker, editorTracker: IEditorTracker, - globalAwareness: Awareness | null + globalAwareness: Awareness | null, + restorer: ILayoutRestorer ) => { const { commands, shell } = app; @@ -92,14 +94,20 @@ const plugin: JupyterFrontEndPlugin = { const chatHandler = new ChatHandler(); await chatHandler.initialize(); + const chatWidget = buildChatSidebar(selectionWatcher, chatHandler, globalAwareness); + /** * Add Chat widget to right sidebar */ shell.add( - buildChatSidebar(selectionWatcher, chatHandler, globalAwareness), - 'right' + chatWidget, + 'left', { rank: 2000 } ); + if (restorer) { + restorer.add(chatWidget, 'jupyter-ai-chat'); + } + /** * Register inserters */ diff --git a/packages/jupyter-ai/src/widgets/chat-sidebar.tsx b/packages/jupyter-ai/src/widgets/chat-sidebar.tsx index d75caba44..22701f29d 100644 --- a/packages/jupyter-ai/src/widgets/chat-sidebar.tsx +++ b/packages/jupyter-ai/src/widgets/chat-sidebar.tsx @@ -3,7 +3,7 @@ import { ReactWidget } from '@jupyterlab/apputils'; import type { Awareness } from 'y-protocols/awareness'; import { Chat } from '../components/chat'; -import { psychologyIcon } from '../icons'; +import { chatIcon } from '../icons'; import { SelectionWatcher } from '../selection-watcher'; import { ChatHandler } from '../chat_handler'; @@ -20,7 +20,7 @@ export function buildChatSidebar( /> ); ChatWidget.id = 'jupyter-ai::chat'; - ChatWidget.title.icon = psychologyIcon; + ChatWidget.title.icon = chatIcon; ChatWidget.title.caption = 'Jupyter AI Chat'; // TODO: i18n return ChatWidget; } diff --git a/packages/jupyter-ai/style/base.css b/packages/jupyter-ai/style/base.css index e6e9db403..ed55a78f4 100644 --- a/packages/jupyter-ai/style/base.css +++ b/packages/jupyter-ai/style/base.css @@ -5,3 +5,4 @@ */ @import './expandable-text-field.css'; +@import './react-markdown.css'; \ No newline at end of file diff --git a/packages/jupyter-ai/style/icons/chat.svg b/packages/jupyter-ai/style/icons/chat.svg new file mode 100644 index 000000000..3634ad0d9 --- /dev/null +++ b/packages/jupyter-ai/style/icons/chat.svg @@ -0,0 +1,6 @@ + + + diff --git a/packages/jupyter-ai/style/react-markdown.css b/packages/jupyter-ai/style/react-markdown.css new file mode 100644 index 000000000..52e6b84fe --- /dev/null +++ b/packages/jupyter-ai/style/react-markdown.css @@ -0,0 +1,7 @@ +.jp-RenderedHTMLCommon.jp-ai-react-markdown > pre { + margin: 0px; +} + +.jp-RenderedHTMLCommon.jp-ai-react-markdown { + padding: 0px; +} \ No newline at end of file