mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
Allow moving the assistant panel to other docks
This commit is contained in:
parent
55c8c6d3fb
commit
d0aff65b1c
7 changed files with 135 additions and 24 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -104,13 +104,16 @@ dependencies = [
|
|||
"chrono",
|
||||
"collections",
|
||||
"editor",
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"isahc",
|
||||
"language",
|
||||
"schemars",
|
||||
"search",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"theme",
|
||||
"util",
|
||||
"workspace",
|
||||
|
|
|
@ -81,6 +81,16 @@
|
|||
// Default width of the project panel.
|
||||
"default_width": 240
|
||||
},
|
||||
"assistant": {
|
||||
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
|
||||
"dock": "right",
|
||||
// Default width when the assistant is docked to the left or right.
|
||||
"default_width": 480,
|
||||
// Default height when the assistant is docked to the bottom.
|
||||
"default_height": 320,
|
||||
// OpenAI API key.
|
||||
"openai_api_key": null
|
||||
},
|
||||
// Whether the screen sharing icon is shown in the os status bar.
|
||||
"show_call_status_icon": true,
|
||||
// Whether to use language servers to provide code intelligence.
|
||||
|
|
|
@ -12,9 +12,11 @@ doctest = false
|
|||
assets = { path = "../assets"}
|
||||
collections = { path = "../collections"}
|
||||
editor = { path = "../editor" }
|
||||
fs = { path = "../fs" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
search = { path = "../search" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
workspace = { path = "../workspace" }
|
||||
|
@ -23,6 +25,7 @@ anyhow.workspace = true
|
|||
chrono = "0.4"
|
||||
futures.workspace = true
|
||||
isahc.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod assistant;
|
||||
mod assistant_settings;
|
||||
|
||||
pub use assistant::AssistantPanel;
|
||||
use gpui::{actions, AppContext};
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use crate::{OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role};
|
||||
use crate::{
|
||||
assistant_settings::{AssistantDockPosition, AssistantSettings},
|
||||
OpenAIRequest, OpenAIResponseStreamEvent, RequestMessage, Role,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Local};
|
||||
use collections::HashMap;
|
||||
use editor::{Editor, ExcerptId, ExcerptRange, MultiBuffer};
|
||||
use fs::Fs;
|
||||
use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
|
||||
use gpui::{
|
||||
actions, elements::*, executor::Background, Action, AppContext, AsyncAppContext, Entity,
|
||||
|
@ -11,6 +15,7 @@ use gpui::{
|
|||
};
|
||||
use isahc::{http::StatusCode, Request, RequestExt};
|
||||
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
|
||||
use settings::SettingsStore;
|
||||
use std::{io, sync::Arc};
|
||||
use util::{post_inc, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
|
@ -22,6 +27,7 @@ use workspace::{
|
|||
actions!(assistant, [NewContext, Assist, QuoteSelection, ToggleFocus]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
settings::register::<AssistantSettings>(cx);
|
||||
cx.add_action(
|
||||
|workspace: &mut Workspace, _: &NewContext, cx: &mut ViewContext<Workspace>| {
|
||||
if let Some(this) = workspace.panel::<AssistantPanel>(cx) {
|
||||
|
@ -41,12 +47,15 @@ pub enum AssistantPanelEvent {
|
|||
ZoomOut,
|
||||
Focus,
|
||||
Close,
|
||||
DockPositionChanged,
|
||||
}
|
||||
|
||||
pub struct AssistantPanel {
|
||||
width: Option<f32>,
|
||||
height: Option<f32>,
|
||||
pane: ViewHandle<Pane>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
|
@ -113,17 +122,40 @@ impl AssistantPanel {
|
|||
.update(cx, |toolbar, cx| toolbar.add_item(buffer_search_bar, cx));
|
||||
pane
|
||||
});
|
||||
let subscriptions = vec![
|
||||
cx.observe(&pane, |_, _, cx| cx.notify()),
|
||||
cx.subscribe(&pane, Self::handle_pane_event),
|
||||
];
|
||||
|
||||
Self {
|
||||
let mut this = Self {
|
||||
pane,
|
||||
languages: workspace.app_state().languages.clone(),
|
||||
fs: workspace.app_state().fs.clone(),
|
||||
width: None,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
height: None,
|
||||
_subscriptions: Default::default(),
|
||||
};
|
||||
|
||||
let mut old_dock_position = this.position(cx);
|
||||
let mut old_openai_api_key = settings::get::<AssistantSettings>(cx)
|
||||
.openai_api_key
|
||||
.clone();
|
||||
this._subscriptions = vec![
|
||||
cx.observe(&this.pane, |_, _, cx| cx.notify()),
|
||||
cx.subscribe(&this.pane, Self::handle_pane_event),
|
||||
cx.observe_global::<SettingsStore, _>(move |this, cx| {
|
||||
let new_dock_position = this.position(cx);
|
||||
if new_dock_position != old_dock_position {
|
||||
old_dock_position = new_dock_position;
|
||||
cx.emit(AssistantPanelEvent::DockPositionChanged);
|
||||
}
|
||||
|
||||
let new_openai_api_key = settings::get::<AssistantSettings>(cx)
|
||||
.openai_api_key
|
||||
.clone();
|
||||
if old_openai_api_key != new_openai_api_key {
|
||||
old_openai_api_key = new_openai_api_key;
|
||||
cx.notify();
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
this
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -174,24 +206,44 @@ impl View for AssistantPanel {
|
|||
}
|
||||
|
||||
impl Panel for AssistantPanel {
|
||||
fn position(&self, _: &WindowContext) -> DockPosition {
|
||||
DockPosition::Right
|
||||
fn position(&self, cx: &WindowContext) -> DockPosition {
|
||||
match settings::get::<AssistantSettings>(cx).dock {
|
||||
AssistantDockPosition::Left => DockPosition::Left,
|
||||
AssistantDockPosition::Bottom => DockPosition::Bottom,
|
||||
AssistantDockPosition::Right => DockPosition::Right,
|
||||
}
|
||||
}
|
||||
|
||||
fn position_is_valid(&self, position: DockPosition) -> bool {
|
||||
matches!(position, DockPosition::Right)
|
||||
fn position_is_valid(&self, _: DockPosition) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_position(&mut self, _: DockPosition, _: &mut ViewContext<Self>) {
|
||||
// TODO!
|
||||
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
|
||||
settings::update_settings_file::<AssistantSettings>(self.fs.clone(), cx, move |settings| {
|
||||
let dock = match position {
|
||||
DockPosition::Left => AssistantDockPosition::Left,
|
||||
DockPosition::Bottom => AssistantDockPosition::Bottom,
|
||||
DockPosition::Right => AssistantDockPosition::Right,
|
||||
};
|
||||
settings.dock = Some(dock);
|
||||
});
|
||||
}
|
||||
|
||||
fn size(&self, _: &WindowContext) -> f32 {
|
||||
self.width.unwrap_or(480.)
|
||||
fn size(&self, cx: &WindowContext) -> f32 {
|
||||
let settings = settings::get::<AssistantSettings>(cx);
|
||||
match self.position(cx) {
|
||||
DockPosition::Left | DockPosition::Right => {
|
||||
self.width.unwrap_or_else(|| settings.default_width)
|
||||
}
|
||||
DockPosition::Bottom => self.height.unwrap_or_else(|| settings.default_height),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
|
||||
self.width = Some(size);
|
||||
match self.position(cx) {
|
||||
DockPosition::Left | DockPosition::Right => self.width = Some(size),
|
||||
DockPosition::Bottom => self.height = Some(size),
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -225,9 +277,8 @@ impl Panel for AssistantPanel {
|
|||
("Assistant Panel".into(), Some(Box::new(ToggleFocus)))
|
||||
}
|
||||
|
||||
fn should_change_position_on_event(_: &Self::Event) -> bool {
|
||||
// TODO!
|
||||
false
|
||||
fn should_change_position_on_event(event: &Self::Event) -> bool {
|
||||
matches!(event, AssistantPanelEvent::DockPositionChanged)
|
||||
}
|
||||
|
||||
fn should_activate_on_event(_: &Self::Event) -> bool {
|
||||
|
@ -289,7 +340,10 @@ impl Assistant {
|
|||
stream: true,
|
||||
};
|
||||
|
||||
if let Some(api_key) = std::env::var("OPENAI_API_KEY").log_err() {
|
||||
if let Some(api_key) = settings::get::<AssistantSettings>(cx)
|
||||
.openai_api_key
|
||||
.clone()
|
||||
{
|
||||
let stream = stream_completion(api_key, cx.background().clone(), request);
|
||||
let response = self.push_message(Role::Assistant, cx);
|
||||
self.push_message(Role::User, cx);
|
||||
|
|
42
crates/ai/src/assistant_settings.rs
Normal file
42
crates/ai/src/assistant_settings.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use anyhow;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AssistantDockPosition {
|
||||
Left,
|
||||
Right,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct AssistantSettings {
|
||||
pub dock: AssistantDockPosition,
|
||||
pub default_width: f32,
|
||||
pub default_height: f32,
|
||||
pub openai_api_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
pub struct AssistantSettingsContent {
|
||||
pub dock: Option<AssistantDockPosition>,
|
||||
pub default_width: Option<f32>,
|
||||
pub default_height: Option<f32>,
|
||||
pub openai_api_key: Option<String>,
|
||||
}
|
||||
|
||||
impl Setting for AssistantSettings {
|
||||
const KEY: Option<&'static str> = Some("assistant");
|
||||
|
||||
type FileContent = AssistantSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &gpui::AppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
|
@ -37,8 +37,6 @@ lazy_static.workspace = true
|
|||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
|
|
Loading…
Reference in a new issue