Skip to content

Commit

Permalink
Adds config option to use ENTER to send message (#164)
Browse files Browse the repository at this point in the history
* Adds new config option to swap enter, shift+enter behavior

* Updates input, docs

* Moves helperText down; removes debug stmts

* Combines effects

* Sets default behavior to "ENTER sends message"

* Change the order to put the new default on top
  • Loading branch information
JasonWeill authored May 17, 2023
1 parent 6d8ec5b commit 1555afd
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 13 deletions.
4 changes: 2 additions & 2 deletions docs/source/users/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Once you have set all the necessary keys, click the "back" (left arrow) button i
alt="Screen shot of the initial, blank, chat interface."
class="screenshot" />

To compose a message, type it in the text box at the bottom of the chat interface and press <kbd>SHIFT</kbd>+<kbd>ENTER</kbd> to send. You can press <kbd>ENTER</kbd> to add a new line. Once you have sent a message, you should see a response from Jupyternaut, the Jupyter AI chatbot.
To compose a message, type it in the text box at the bottom of the chat interface and press <kbd>ENTER</kbd> to send it. You can press <kbd>SHIFT</kbd>+<kbd>ENTER</kbd> to add a new line. (These are the default keybindings; you can change them in the chat settings pane.) Once you have sent a message, you should see a response from Jupyternaut, the Jupyter AI chatbot.

<img src="../_static/chat-hello-world.png"
alt='Screen shot of an example "Hello world" message sent to Jupyternaut, who responds with "Hello world, how are you today?"'
Expand All @@ -188,7 +188,7 @@ number of tokens in your request, which may cause your request to cost more mone
Review your model provider's cost policy before making large requests.
:::

After highlighting a portion of your notebook, check "Include selection" in the chat panel, type your message, and press <kbd>SHIFT</kbd>+<kbd>ENTER</kbd> to send your message. Your outgoing message will include your selection.
After highlighting a portion of your notebook, check "Include selection" in the chat panel, type your message, and then send your message. Your outgoing message will include your selection.

<img src="../_static/chat-interface-selection.png"
alt='Screen shot of JupyterLab with Jupyter AI&apos;s chat panel active. A Python function is selected, the user has "What does this code do?" as their prompt, and the user has chosen to include the selection with their message.'
Expand Down
1 change: 1 addition & 0 deletions packages/jupyter-ai/jupyter_ai/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,4 @@ class GlobalConfig(BaseModel):
model_provider_id: Optional[str] = None
embeddings_provider_id: Optional[str] = None
api_keys: Dict[str, str] = {}
send_with_shift_enter: Optional[bool] = None
15 changes: 12 additions & 3 deletions packages/jupyter-ai/src/components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,28 @@ type ChatInputProps = {
toggleIncludeSelection: () => unknown;
replaceSelection: boolean;
toggleReplaceSelection: () => unknown;
helperText: JSX.Element
sendWithShiftEnter: boolean;
sx?: SxProps<Theme>;
};

export function ChatInput(props: ChatInputProps): JSX.Element {

function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
if (event.key === 'Enter' && event.shiftKey) {
if (event.key === 'Enter' && (
(props.sendWithShiftEnter && event.shiftKey)
|| (!props.sendWithShiftEnter && !event.shiftKey)
)) {
props.onSend();
event.stopPropagation();
event.preventDefault();
}
}

// Set the helper text based on whether Shift+Enter is used for sending.
const helperText = props.sendWithShiftEnter
? <span>Press <b>Shift</b>+<b>Enter</b> to send message</span>
: <span>Press <b>Shift</b>+<b>Enter</b> to add a new line</span>;

return (
<Box sx={props.sx}>
<Box sx={{ display: 'flex'}}>
Expand Down Expand Up @@ -64,7 +73,7 @@ export function ChatInput(props: ChatInputProps): JSX.Element {
FormHelperTextProps={{
sx: {marginLeft: 'auto', marginRight: 0}
}}
helperText={props.value.length > 2 ? props.helperText : ' '}
helperText={props.value.length > 2 ? helperText : ' '}
/>
</Box>
{props.hasSelection && (
Expand Down
43 changes: 41 additions & 2 deletions packages/jupyter-ai/src/components/chat-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { Box } from '@mui/system';
import {
Alert,
Button,
FormControl,
FormControlLabel,
FormLabel,
MenuItem,
Radio,
RadioGroup,
TextField,
CircularProgress
} from '@mui/material';
Expand Down Expand Up @@ -42,7 +47,8 @@ export function ChatSettings() {
const [inputConfig, setInputConfig] = useState<AiService.Config>({
model_provider_id: null,
embeddings_provider_id: null,
api_keys: {}
api_keys: {},
send_with_shift_enter: null
});

// whether the form is currently saving
Expand Down Expand Up @@ -109,7 +115,8 @@ export function ChatSettings() {
const handleSave = async () => {
const inputConfigCopy: AiService.Config = {
...inputConfig,
api_keys: { ...inputConfig.api_keys }
api_keys: { ...inputConfig.api_keys },
send_with_shift_enter: inputConfig.send_with_shift_enter ?? true
};

// delete any empty api keys
Expand Down Expand Up @@ -256,6 +263,38 @@ export function ChatSettings() {
/>
)
)}
<FormControl>
<FormLabel id="send-radio-buttons-group-label">
When writing a message, press <kbd>Enter</kbd> to:
</FormLabel>
<RadioGroup
aria-labelledby="send-radio-buttons-group-label"
value={
(inputConfig.send_with_shift_enter ?? false) ? 'newline' : 'send'
}
name="send-radio-buttons-group"
onChange={e =>
setInputConfig(inputConfig => {
return ({
...inputConfig,
send_with_shift_enter: (e.target as HTMLInputElement).value === 'newline'
});
})}
>
<FormControlLabel
value="send"
control={<Radio />}
label="Send the message"
/>
<FormControlLabel
value="newline"
control={<Radio />}
label={
<>Start a new line (use <kbd>Shift</kbd>+<kbd>Enter</kbd> to send)</>
}
/>
</RadioGroup>
</FormControl>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button variant="contained" onClick={handleSave} disabled={saving}>
{saving ? 'Saving...' : 'Save changes'}
Expand Down
10 changes: 4 additions & 6 deletions packages/jupyter-ai/src/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps):
const [replaceSelection, setReplaceSelection] = useState(false);
const [input, setInput] = useState('');
const [selection, replaceSelectionFn] = useSelectionContext();
const [sendWithShiftEnter, setSendWithShiftEnter] = useState(true);

/**
* Effect: fetch history on initial render
* Effect: fetch history and config on initial render
*/
useEffect(() => {
async function fetchHistory() {
Expand All @@ -42,6 +43,7 @@ function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps):
chatHandler.getHistory(),
AiService.getConfig()
]);
setSendWithShiftEnter(config.send_with_shift_enter ?? false);
setMessages(history.messages);
if (!config.model_provider_id) {
setShowWelcomeMessage(true);
Expand Down Expand Up @@ -163,11 +165,7 @@ function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps):
paddingBottom: 0,
borderTop: '1px solid var(--jp-border-color1)'
}}
helperText={
<span>
Press <b>Shift</b> + <b>Enter</b> to submit message
</span>
}
sendWithShiftEnter={sendWithShiftEnter}
/>
</>
);
Expand Down
1 change: 1 addition & 0 deletions packages/jupyter-ai/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export namespace AiService {
model_provider_id: string | null;
embeddings_provider_id: string | null;
api_keys: Record<string, string>;
send_with_shift_enter: boolean | null;
};

export type GetConfigResponse = Config;
Expand Down

0 comments on commit 1555afd

Please sign in to comment.