Skip to content

Commit

Permalink
AI settings (jupyterlab#4)
Browse files Browse the repository at this point in the history
* Add a menu option to open the AI settings

* Remove the input option from the setting widget
  • Loading branch information
brichet authored Oct 15, 2024
1 parent b7e25f4 commit 8095691
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 32 deletions.
22 changes: 22 additions & 0 deletions packages/jupyter-ai/schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,28 @@
"preventDefault": false
}
],
"jupyter.lab.menus": {
"main": [
{
"id": "jp-mainmenu-settings",
"items": [
{
"type": "separator",
"rank": 110

},
{
"command": "jupyter-ai:open-settings",
"rank": 110
},
{
"type": "separator",
"rank": 110
}
]
}
]
},
"additionalProperties": false,
"type": "object"
}
69 changes: 39 additions & 30 deletions packages/jupyter-ai/src/components/chat-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type ChatSettingsProps = {
rmRegistry: IRenderMimeRegistry;
completionProvider: IJaiCompletionProvider | null;
openInlineCompleterSettings: () => void;
// The temporary input options, should be removed when the collaborative chat is
// the only chat.
inputOptions?: boolean;
};

/**
Expand Down Expand Up @@ -511,36 +514,42 @@ export function ChatSettings(props: ChatSettingsProps): JSX.Element {
onSuccess={server.refetchApiKeys}
/>

{/* Input */}
<h2 className="jp-ai-ChatSettings-header">Input</h2>
<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={sendWse ? 'newline' : 'send'}
name="send-radio-buttons-group"
onChange={e => {
setSendWse(e.target.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>
{/* Input - to remove when the collaborative chat is the only chat */}
{(props.inputOptions ?? true) && (
<>
<h2 className="jp-ai-ChatSettings-header">Input</h2>
<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={sendWse ? 'newline' : 'send'}
name="send-radio-buttons-group"
onChange={e => {
setSendWse(e.target.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
50 changes: 48 additions & 2 deletions packages/jupyter-ai/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
import {
IWidgetTracker,
ReactWidget,
IThemeManager
IThemeManager,
MainAreaWidget,
ICommandPalette
} from '@jupyterlab/apputils';
import { IDocumentWidget } from '@jupyterlab/docregistry';
import { IGlobalAwareness } from '@jupyter/collaboration';
Expand All @@ -25,6 +27,7 @@ import { ActiveCellManager } from './contexts/active-cell-context';
import { autocompletion } from './slash-autocompletion';
import { Signal } from '@lumino/signaling';
import { menuPlugin } from './plugins/menu-plugin';
import { buildAiSettings } from './widgets/settings-widget';

export type DocumentTracker = IWidgetTracker<IDocumentWidget>;

Expand All @@ -33,6 +36,10 @@ export namespace CommandIDs {
* Command to focus the input.
*/
export const focusChatInput = 'jupyter-ai:focus-chat-input';
/**
* Command to open the AI settings.
*/
export const openAiSettings = 'jupyter-ai:open-settings';
}

/**
Expand All @@ -43,6 +50,7 @@ const plugin: JupyterFrontEndPlugin<IJaiCore> = {
autoStart: true,
requires: [IRenderMimeRegistry],
optional: [
ICommandPalette,
IGlobalAwareness,
ILayoutRestorer,
IThemeManager,
Expand All @@ -53,6 +61,7 @@ const plugin: JupyterFrontEndPlugin<IJaiCore> = {
activate: async (
app: JupyterFrontEnd,
rmRegistry: IRenderMimeRegistry,
palette: ICommandPalette | null,
globalAwareness: Awareness | null,
restorer: ILayoutRestorer | null,
themeManager: IThemeManager | null,
Expand Down Expand Up @@ -82,9 +91,46 @@ const plugin: JupyterFrontEndPlugin<IJaiCore> = {

const focusInputSignal = new Signal<unknown, void>({});

let chatWidget: ReactWidget;
// Create a AI settings widget.
let aiSettings: MainAreaWidget<ReactWidget>;
let settingsWidget: ReactWidget;
try {
await chatHandler.initialize();
settingsWidget = buildAiSettings(
rmRegistry,
completionProvider,
openInlineCompleterSettings
);
} catch (e) {
settingsWidget = buildErrorWidget(themeManager);
}

// Add a command to open settings widget in main area.
app.commands.addCommand(CommandIDs.openAiSettings, {
execute: () => {
if (!aiSettings || aiSettings.isDisposed) {
aiSettings = new MainAreaWidget({ content: settingsWidget });
aiSettings.id = 'jupyter-ai-settings';
aiSettings.title.label = 'AI settings';
aiSettings.title.closable = true;
}
if (!aiSettings.isAttached) {
app?.shell.add(aiSettings, 'main');
}
app.shell.activateById(aiSettings.id);
},
label: 'AI settings'
});

if (palette) {
palette.addItem({
category: 'jupyter-ai',
command: CommandIDs.openAiSettings
});
}

let chatWidget: ReactWidget;
try {
chatWidget = buildChatSidebar(
selectionWatcher,
chatHandler,
Expand Down
26 changes: 26 additions & 0 deletions packages/jupyter-ai/src/widgets/settings-widget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { ReactWidget } from '@jupyterlab/apputils';
import { settingsIcon } from '@jupyterlab/ui-components';

import { IJaiCompletionProvider } from '../tokens';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { ChatSettings } from '../components/chat-settings';

export function buildAiSettings(
rmRegistry: IRenderMimeRegistry,
completionProvider: IJaiCompletionProvider | null,
openInlineCompleterSettings: () => void
): ReactWidget {
const SettingsWidget = ReactWidget.create(
<ChatSettings
rmRegistry={rmRegistry}
completionProvider={completionProvider}
openInlineCompleterSettings={openInlineCompleterSettings}
inputOptions={false}
/>
);
SettingsWidget.id = 'jupyter-ai::settings';
SettingsWidget.title.icon = settingsIcon;
SettingsWidget.title.caption = 'Jupyter AI Settings'; // TODO: i18n
return SettingsWidget;
}

0 comments on commit 8095691

Please sign in to comment.