Allow saving the OpenAI API key in the assistant panel
This commit is contained in:
parent
d0aff65b1c
commit
a81d164ea6
6 changed files with 88 additions and 5 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -109,6 +109,7 @@ dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"isahc",
|
"isahc",
|
||||||
"language",
|
"language",
|
||||||
|
"menu",
|
||||||
"schemars",
|
"schemars",
|
||||||
"search",
|
"search",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
||||||
"dock": "right",
|
"dock": "right",
|
||||||
// Default width when the assistant is docked to the left or right.
|
// Default width when the assistant is docked to the left or right.
|
||||||
"default_width": 480,
|
"default_width": 450,
|
||||||
// Default height when the assistant is docked to the bottom.
|
// Default height when the assistant is docked to the bottom.
|
||||||
"default_height": 320,
|
"default_height": 320,
|
||||||
// OpenAI API key.
|
// OpenAI API key.
|
||||||
|
|
|
@ -15,6 +15,7 @@ editor = { path = "../editor" }
|
||||||
fs = { path = "../fs" }
|
fs = { path = "../fs" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
|
menu = { path = "../menu" }
|
||||||
search = { path = "../search" }
|
search = { path = "../search" }
|
||||||
settings = { path = "../settings" }
|
settings = { path = "../settings" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
|
|
|
@ -40,6 +40,7 @@ pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(AssistantEditor::assist);
|
cx.add_action(AssistantEditor::assist);
|
||||||
cx.capture_action(AssistantEditor::cancel_last_assist);
|
cx.capture_action(AssistantEditor::cancel_last_assist);
|
||||||
cx.add_action(AssistantEditor::quote_selection);
|
cx.add_action(AssistantEditor::quote_selection);
|
||||||
|
cx.add_action(AssistantPanel::save_api_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum AssistantPanelEvent {
|
pub enum AssistantPanelEvent {
|
||||||
|
@ -54,6 +55,7 @@ pub struct AssistantPanel {
|
||||||
width: Option<f32>,
|
width: Option<f32>,
|
||||||
height: Option<f32>,
|
height: Option<f32>,
|
||||||
pane: ViewHandle<Pane>,
|
pane: ViewHandle<Pane>,
|
||||||
|
api_key_editor: ViewHandle<Editor>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
|
@ -124,6 +126,17 @@ impl AssistantPanel {
|
||||||
});
|
});
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
pane,
|
pane,
|
||||||
|
api_key_editor: cx.add_view(|cx| {
|
||||||
|
let mut editor = Editor::single_line(
|
||||||
|
Some(Arc::new(|theme| theme.assistant.api_key_editor.clone())),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
editor.set_placeholder_text(
|
||||||
|
"sk-000000000000000000000000000000000000000000000000",
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
editor
|
||||||
|
}),
|
||||||
languages: workspace.app_state().languages.clone(),
|
languages: workspace.app_state().languages.clone(),
|
||||||
fs: workspace.app_state().fs.clone(),
|
fs: workspace.app_state().fs.clone(),
|
||||||
width: None,
|
width: None,
|
||||||
|
@ -150,6 +163,9 @@ impl AssistantPanel {
|
||||||
.clone();
|
.clone();
|
||||||
if old_openai_api_key != new_openai_api_key {
|
if old_openai_api_key != new_openai_api_key {
|
||||||
old_openai_api_key = new_openai_api_key;
|
old_openai_api_key = new_openai_api_key;
|
||||||
|
if this.has_focus(cx) {
|
||||||
|
cx.focus_self();
|
||||||
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@ -183,6 +199,17 @@ impl AssistantPanel {
|
||||||
pane.add_item(Box::new(editor), true, focus, None, cx)
|
pane.add_item(Box::new(editor), true, focus, None, cx)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn save_api_key(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
|
||||||
|
let api_key = self.api_key_editor.read(cx).text(cx);
|
||||||
|
if !api_key.is_empty() {
|
||||||
|
settings::update_settings_file::<AssistantSettings>(
|
||||||
|
self.fs.clone(),
|
||||||
|
cx,
|
||||||
|
move |settings| settings.openai_api_key = Some(api_key),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for AssistantPanel {
|
impl Entity for AssistantPanel {
|
||||||
|
@ -195,12 +222,44 @@ impl View for AssistantPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||||
ChildView::new(&self.pane, cx).into_any()
|
let style = &theme::current(cx).assistant;
|
||||||
|
if settings::get::<AssistantSettings>(cx)
|
||||||
|
.openai_api_key
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
Flex::column()
|
||||||
|
.with_child(
|
||||||
|
Text::new(
|
||||||
|
"Paste your OpenAI API key and press Enter to use the assistant",
|
||||||
|
style.api_key_prompt.text.clone(),
|
||||||
|
)
|
||||||
|
.aligned(),
|
||||||
|
)
|
||||||
|
.with_child(
|
||||||
|
ChildView::new(&self.api_key_editor, cx)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.api_key_editor.container)
|
||||||
|
.aligned(),
|
||||||
|
)
|
||||||
|
.contained()
|
||||||
|
.with_style(style.api_key_prompt.container)
|
||||||
|
.aligned()
|
||||||
|
.into_any()
|
||||||
|
} else {
|
||||||
|
ChildView::new(&self.pane, cx).into_any()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
if cx.is_self_focused() {
|
if cx.is_self_focused() {
|
||||||
cx.focus(&self.pane);
|
if settings::get::<AssistantSettings>(cx)
|
||||||
|
.openai_api_key
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
cx.focus(&self.pane);
|
||||||
|
} else {
|
||||||
|
cx.focus(&self.api_key_editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,7 +349,7 @@ impl Panel for AssistantPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_focus(&self, cx: &WindowContext) -> bool {
|
fn has_focus(&self, cx: &WindowContext) -> bool {
|
||||||
self.pane.read(cx).has_focus()
|
self.pane.read(cx).has_focus() || self.api_key_editor.is_focused(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_focus_event(event: &Self::Event) -> bool {
|
fn is_focus_event(event: &Self::Event) -> bool {
|
||||||
|
|
|
@ -976,6 +976,8 @@ pub struct AssistantStyle {
|
||||||
pub sent_at: ContainedText,
|
pub sent_at: ContainedText,
|
||||||
pub user_sender: ContainedText,
|
pub user_sender: ContainedText,
|
||||||
pub assistant_sender: ContainedText,
|
pub assistant_sender: ContainedText,
|
||||||
|
pub api_key_editor: FieldEditor,
|
||||||
|
pub api_key_prompt: ContainedText,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ColorScheme } from "../themes/common/colorScheme"
|
import { ColorScheme } from "../themes/common/colorScheme"
|
||||||
import { text, border } from "./components"
|
import { text, border, background } from "./components"
|
||||||
import editor from "./editor"
|
import editor from "./editor"
|
||||||
|
|
||||||
export default function assistant(colorScheme: ColorScheme) {
|
export default function assistant(colorScheme: ColorScheme) {
|
||||||
|
@ -22,6 +22,26 @@ export default function assistant(colorScheme: ColorScheme) {
|
||||||
sent_at: {
|
sent_at: {
|
||||||
margin: { top: 2, left: 8 },
|
margin: { top: 2, left: 8 },
|
||||||
...text(layer, "sans", "default", { size: "2xs" }),
|
...text(layer, "sans", "default", { size: "2xs" }),
|
||||||
|
},
|
||||||
|
apiKeyEditor: {
|
||||||
|
background: background(layer, "on"),
|
||||||
|
cornerRadius: 6,
|
||||||
|
text: text(layer, "mono", "on"),
|
||||||
|
placeholderText: text(layer, "mono", "on", "disabled", {
|
||||||
|
size: "xs",
|
||||||
|
}),
|
||||||
|
selection: colorScheme.players[0],
|
||||||
|
border: border(layer, "on"),
|
||||||
|
padding: {
|
||||||
|
bottom: 4,
|
||||||
|
left: 8,
|
||||||
|
right: 8,
|
||||||
|
top: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
apiKeyPrompt: {
|
||||||
|
padding: 10,
|
||||||
|
...text(layer, "sans", "default", { size: "xs" }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue