diff --git a/assets/icons/download.svg b/assets/icons/download.svg
index bcf66dfdf7..2ffa65e8ac 100644
--- a/assets/icons/download.svg
+++ b/assets/icons/download.svg
@@ -1,10 +1 @@
-
+
diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs
index bae65d7731..064279bb4b 100644
--- a/crates/assistant/src/assistant_panel.rs
+++ b/crates/assistant/src/assistant_panel.rs
@@ -34,16 +34,14 @@ use gpui::{
div, percentage, point, svg, Action, Animation, AnimationExt, AnyElement, AnyView, AppContext,
AsyncWindowContext, ClipboardItem, Context as _, DismissEvent, Empty, Entity, EventEmitter,
FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Pixels,
- Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
- TextStyleRefinement, Transformation, UpdateGlobal, View, ViewContext, VisualContext, WeakView,
- WindowContext,
+ Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, Transformation,
+ UpdateGlobal, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use indexed_docs::IndexedDocsStore;
use language::{
language_settings::SoftWrap, Capability, LanguageRegistry, LspAdapterDelegate, Point, ToOffset,
};
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Role};
-use markdown::{Markdown, MarkdownStyle};
use multi_buffer::MultiBufferRow;
use picker::{Picker, PickerDelegate};
use project::{Project, ProjectLspAdapterDelegate};
@@ -59,7 +57,6 @@ use std::{
time::Duration,
};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
-use theme::ThemeSettings;
use ui::TintColor;
use ui::{
prelude::*,
@@ -3035,7 +3032,6 @@ impl Item for ContextHistory {
pub struct ConfigurationView {
fallback_handle: FocusHandle,
- using_assistant_description: View,
active_tab: Option,
}
@@ -3057,46 +3053,10 @@ impl ActiveTab {
}
}
-// TODO: We need to remove this once we have proper text and styling
-const SHOW_CONFIGURATION_TEXT: bool = false;
-
impl ConfigurationView {
- fn new(fallback_handle: FocusHandle, cx: &mut ViewContext) -> Self {
- let usage_description = cx.new_view(|cx| {
- let text = include_str!("./using-the-assistant.md");
-
- let settings = ThemeSettings::get_global(cx);
- let mut base_text_style = cx.text_style();
- base_text_style.refine(&TextStyleRefinement {
- font_family: Some(settings.ui_font.family.clone()),
- font_size: Some(TextSize::XSmall.rems(cx).into()),
- color: Some(cx.theme().colors().editor_foreground),
- background_color: Some(gpui::transparent_black()),
- ..Default::default()
- });
- let markdown_style = MarkdownStyle {
- base_text_style,
- selection_background_color: { cx.theme().players().local().selection },
- inline_code: TextStyleRefinement {
- background_color: Some(cx.theme().colors().background),
- ..Default::default()
- },
- link: TextStyleRefinement {
- underline: Some(gpui::UnderlineStyle {
- thickness: px(1.),
- color: Some(cx.theme().colors().editor_foreground),
- wavy: false,
- }),
- ..Default::default()
- },
- ..Default::default()
- };
- Markdown::new(text.to_string(), markdown_style.clone(), None, cx, None)
- });
-
+ fn new(fallback_handle: FocusHandle, _cx: &mut ViewContext) -> Self {
Self {
fallback_handle,
- using_assistant_description: usage_description,
active_tab: None,
}
}
@@ -3240,24 +3200,21 @@ impl Render for ConfigurationView {
.child(
v_flex()
.gap_2()
- .child(
- Headline::new("Get Started with the Assistant").size(HeadlineSize::Medium),
- )
- .child(
- Label::new("Configure a provider to get started with the assistant. Then create a new context.")
- .color(Color::Muted),
- ),
+ .child(Headline::new("Configure your Assistant").size(HeadlineSize::Medium)),
)
.child(
v_flex()
.gap_2()
.child(Headline::new("Configure providers").size(HeadlineSize::Small))
+ .child(
+ Label::new(
+ "At least one provider must be configured to use the assistant.",
+ )
+ .color(Color::Muted),
+ )
.child(tabs)
.children(self.render_active_tab_view(cx)),
)
- .when(SHOW_CONFIGURATION_TEXT, |this| {
- this.child(self.using_assistant_description.clone())
- })
}
}
diff --git a/crates/language_model/src/language_model.rs b/crates/language_model/src/language_model.rs
index 98abbcb56c..e92cbd652b 100644
--- a/crates/language_model/src/language_model.rs
+++ b/crates/language_model/src/language_model.rs
@@ -7,9 +7,11 @@ mod role;
pub mod settings;
use anyhow::Result;
-use client::Client;
+use client::{Client, UserStore};
use futures::{future::BoxFuture, stream::BoxStream};
-use gpui::{AnyView, AppContext, AsyncAppContext, FocusHandle, SharedString, Task, WindowContext};
+use gpui::{
+ AnyView, AppContext, AsyncAppContext, FocusHandle, Model, SharedString, Task, WindowContext,
+};
pub use model::*;
use project::Fs;
pub(crate) use rate_limiter::*;
@@ -20,9 +22,14 @@ use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use std::{future::Future, sync::Arc};
-pub fn init(client: Arc, fs: Arc, cx: &mut AppContext) {
+pub fn init(
+ user_store: Model,
+ client: Arc,
+ fs: Arc,
+ cx: &mut AppContext,
+) {
settings::init(fs, cx);
- registry::init(client, cx);
+ registry::init(user_store, client, cx);
}
pub trait LanguageModel: Send + Sync {
diff --git a/crates/language_model/src/provider/anthropic.rs b/crates/language_model/src/provider/anthropic.rs
index d92fddd493..3b96035ffb 100644
--- a/crates/language_model/src/provider/anthropic.rs
+++ b/crates/language_model/src/provider/anthropic.rs
@@ -483,7 +483,7 @@ impl Render for ConfigurationView {
.size_full()
.on_action(cx.listener(Self::save_api_key))
.children(
- INSTRUCTIONS.map(|instruction| Label::new(instruction).size(LabelSize::Small)),
+ INSTRUCTIONS.map(|instruction| Label::new(instruction)),
)
.child(
h_flex()
diff --git a/crates/language_model/src/provider/cloud.rs b/crates/language_model/src/provider/cloud.rs
index 4b8018ba46..2c5ab1ca5e 100644
--- a/crates/language_model/src/provider/cloud.rs
+++ b/crates/language_model/src/provider/cloud.rs
@@ -5,10 +5,12 @@ use crate::{
LanguageModelProviderState, LanguageModelRequest, RateLimiter,
};
use anyhow::{anyhow, Context as _, Result};
-use client::Client;
+use client::{Client, UserStore};
use collections::BTreeMap;
use futures::{future::BoxFuture, stream::BoxStream, FutureExt, StreamExt};
-use gpui::{AnyView, AppContext, AsyncAppContext, FocusHandle, ModelContext, Subscription, Task};
+use gpui::{
+ AnyView, AppContext, AsyncAppContext, FocusHandle, Model, ModelContext, Subscription, Task,
+};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
@@ -52,6 +54,7 @@ pub struct CloudLanguageModelProvider {
pub struct State {
client: Arc,
+ user_store: Model,
status: client::Status,
_subscription: Subscription,
}
@@ -71,12 +74,13 @@ impl State {
}
impl CloudLanguageModelProvider {
- pub fn new(client: Arc, cx: &mut AppContext) -> Self {
+ pub fn new(user_store: Model, client: Arc, cx: &mut AppContext) -> Self {
let mut status_rx = client.status();
let status = *status_rx.borrow();
let state = cx.new_model(|cx| State {
client: client.clone(),
+ user_store,
status,
_subscription: cx.observe_global::(|_, cx| {
cx.notify();
@@ -401,8 +405,9 @@ impl Render for ConfigurationView {
const ACCOUNT_SETTINGS_URL: &str = "https://zed.dev/settings";
let is_connected = self.state.read(cx).is_connected();
+ let plan = self.state.read(cx).user_store.read(cx).current_plan();
- let is_pro = false;
+ let is_pro = plan == Some(proto::Plan::ZedPro);
if is_connected {
v_flex()
@@ -410,7 +415,7 @@ impl Render for ConfigurationView {
.max_w_4_5()
.child(Label::new(
if is_pro {
- "You have full access to Zed's hosted models from Anthropic, OpenAI, Google through Zed Pro."
+ "You have full access to Zed's hosted models from Anthropic, OpenAI, Google with faster speeds and higher limits through Zed Pro."
} else {
"You have basic access to models from Anthropic, OpenAI, Google and more through the Zed AI Free plan."
}))
diff --git a/crates/language_model/src/provider/google.rs b/crates/language_model/src/provider/google.rs
index 368778ce29..0547d7e98c 100644
--- a/crates/language_model/src/provider/google.rs
+++ b/crates/language_model/src/provider/google.rs
@@ -398,7 +398,7 @@ impl Render for ConfigurationView {
.size_full()
.on_action(cx.listener(Self::save_api_key))
.children(
- INSTRUCTIONS.map(|instruction| Label::new(instruction).size(LabelSize::Small)),
+ INSTRUCTIONS.map(|instruction| Label::new(instruction)),
)
.child(
h_flex()
diff --git a/crates/language_model/src/provider/ollama.rs b/crates/language_model/src/provider/ollama.rs
index 934b87eb89..c2aace0ba1 100644
--- a/crates/language_model/src/provider/ollama.rs
+++ b/crates/language_model/src/provider/ollama.rs
@@ -7,7 +7,7 @@ use ollama::{
};
use settings::{Settings, SettingsStore};
use std::{future, sync::Arc, time::Duration};
-use ui::{prelude::*, ButtonLike, ElevationIndex, Indicator};
+use ui::{prelude::*, ButtonLike, Indicator};
use crate::{
settings::AllLanguageModelSettings, LanguageModel, LanguageModelId, LanguageModelName,
@@ -17,6 +17,7 @@ use crate::{
const OLLAMA_DOWNLOAD_URL: &str = "https://ollama.com/download";
const OLLAMA_LIBRARY_URL: &str = "https://ollama.com/library";
+const OLLAMA_SITE: &str = "https://ollama.com/";
const PROVIDER_ID: &str = "ollama";
const PROVIDER_NAME: &str = "Ollama";
@@ -303,81 +304,107 @@ impl ConfigurationView {
.update(cx, |state, cx| state.fetch_models(cx))
.detach_and_log_err(cx);
}
-
- fn render_download_button(&self, _cx: &mut ViewContext) -> impl IntoElement {
- ButtonLike::new("download_ollama_button")
- .style(ButtonStyle::Filled)
- .size(ButtonSize::Large)
- .layer(ElevationIndex::ModalSurface)
- .child(Label::new("Get Ollama"))
- .on_click(move |_, cx| cx.open_url(OLLAMA_DOWNLOAD_URL))
- }
-
- fn render_retry_button(&self, cx: &mut ViewContext) -> impl IntoElement {
- ButtonLike::new("retry_ollama_models")
- .style(ButtonStyle::Filled)
- .size(ButtonSize::Large)
- .layer(ElevationIndex::ModalSurface)
- .child(Label::new("Retry"))
- .on_click(cx.listener(move |this, _, cx| this.retry_connection(cx)))
- }
-
- fn render_next_steps(&self, _cx: &mut ViewContext) -> impl IntoElement {
- v_flex()
- .p_4()
- .size_full()
- .gap_2()
- .child(
- Label::new("Once Ollama is on your machine, make sure to download a model or two.")
- .size(LabelSize::Large),
- )
- .child(
- h_flex().w_full().p_4().justify_center().gap_2().child(
- ButtonLike::new("view-models")
- .style(ButtonStyle::Filled)
- .size(ButtonSize::Large)
- .layer(ElevationIndex::ModalSurface)
- .child(Label::new("View Available Models"))
- .on_click(move |_, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
- ),
- )
- }
}
impl Render for ConfigurationView {
fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement {
let is_authenticated = self.state.read(cx).is_authenticated();
- if is_authenticated {
- v_flex()
- .size_full()
- .child(
- h_flex()
- .gap_2()
- .child(Indicator::dot().color(Color::Success))
- .child(Label::new("Ollama configured").size(LabelSize::Small)),
- )
- .into_any()
- } else {
- v_flex()
+ let ollama_intro = "Get up and running with Llama 3.1, Mistral, Gemma 2, and other large language models with Ollama.";
+ let ollama_reqs =
+ "Ollama must be running with at least one model installed to use it in the assistant.";
+
+ let mut inline_code_bg = cx.theme().colors().editor_background;
+ inline_code_bg.fade_out(0.5);
+
+ v_flex()
.size_full()
- .gap_2()
- .child(Label::new("To use Ollama models via the assistant, Ollama must be running on your machine with at least one model downloaded.").size(LabelSize::Large))
+ .gap_3()
+ .child(
+ v_flex()
+ .size_full()
+ .gap_2()
+ .p_1()
+ .child(Label::new(ollama_intro))
+ .child(Label::new(ollama_reqs))
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(Label::new("Once installed, try "))
+ .child(
+ div()
+ .bg(inline_code_bg)
+ .px_1p5()
+ .rounded_md()
+ .child(Label::new("ollama run llama3.1")),
+ ),
+ ),
+ )
.child(
h_flex()
.w_full()
- .p_4()
- .justify_center()
+ .pt_2()
+ .justify_between()
.gap_2()
.child(
- self.render_download_button(cx)
- )
- .child(
- self.render_retry_button(cx)
+ h_flex()
+ .w_full()
+ .gap_2()
+ .map(|this| {
+ if is_authenticated {
+ this.child(
+ Button::new("ollama-site", "Ollama")
+ .style(ButtonStyle::Subtle)
+ .icon(IconName::ExternalLink)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .on_click(move |_, cx| cx.open_url(OLLAMA_SITE))
+ .into_any_element(),
+ )
+ } else {
+ this.child(
+ Button::new("download_ollama_button", "Download Ollama")
+ .style(ButtonStyle::Subtle)
+ .icon(IconName::ExternalLink)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .on_click(move |_, cx| cx.open_url(OLLAMA_DOWNLOAD_URL))
+ .into_any_element(),
+ )
+ }
+ })
+ .child(
+ Button::new("view-models", "All Models")
+ .style(ButtonStyle::Subtle)
+ .icon(IconName::ExternalLink)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .on_click(move |_, cx| cx.open_url(OLLAMA_LIBRARY_URL)),
+ ),
)
+ .child(if is_authenticated {
+ // This is only a button to ensure the spacing is correct
+ // it should stay disabled
+ ButtonLike::new("connected")
+ .disabled(true)
+ // Since this won't ever be clickable, we can use the arrow cursor
+ .cursor_style(gpui::CursorStyle::Arrow)
+ .child(
+ h_flex()
+ .gap_2()
+ .child(Indicator::dot().color(Color::Success))
+ .child(Label::new("Connected"))
+ .into_any_element(),
+ )
+ .into_any_element()
+ } else {
+ Button::new("retry_ollama_models", "Connect")
+ .icon_position(IconPosition::Start)
+ .icon(IconName::ArrowCircle)
+ .on_click(cx.listener(move |this, _, cx| this.retry_connection(cx)))
+ .into_any_element()
+ }),
)
- .child(self.render_next_steps(cx))
.into_any()
- }
}
}
diff --git a/crates/language_model/src/registry.rs b/crates/language_model/src/registry.rs
index 94a69a9d2f..067573bc38 100644
--- a/crates/language_model/src/registry.rs
+++ b/crates/language_model/src/registry.rs
@@ -7,16 +7,16 @@ use crate::{
LanguageModel, LanguageModelId, LanguageModelProvider, LanguageModelProviderId,
LanguageModelProviderState,
};
-use client::Client;
+use client::{Client, UserStore};
use collections::BTreeMap;
use gpui::{AppContext, EventEmitter, Global, Model, ModelContext};
use std::sync::Arc;
use ui::Context;
-pub fn init(client: Arc, cx: &mut AppContext) {
+pub fn init(user_store: Model, client: Arc, cx: &mut AppContext) {
let registry = cx.new_model(|cx| {
let mut registry = LanguageModelRegistry::default();
- register_language_model_providers(&mut registry, client, cx);
+ register_language_model_providers(&mut registry, user_store, client, cx);
registry
});
cx.set_global(GlobalLanguageModelRegistry(registry));
@@ -24,6 +24,7 @@ pub fn init(client: Arc, cx: &mut AppContext) {
fn register_language_model_providers(
registry: &mut LanguageModelRegistry,
+ user_store: Model,
client: Arc,
cx: &mut ModelContext,
) {
@@ -48,10 +49,14 @@ fn register_language_model_providers(
registry.register_provider(CopilotChatLanguageModelProvider::new(cx), cx);
cx.observe_flag::(move |enabled, cx| {
+ let user_store = user_store.clone();
let client = client.clone();
LanguageModelRegistry::global(cx).update(cx, move |registry, cx| {
if enabled {
- registry.register_provider(CloudLanguageModelProvider::new(client.clone(), cx), cx);
+ registry.register_provider(
+ CloudLanguageModelProvider::new(user_store.clone(), client.clone(), cx),
+ cx,
+ );
} else {
registry.unregister_provider(
LanguageModelProviderId::from(crate::provider::cloud::PROVIDER_ID.to_string()),
diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs
index a6c4ea02fb..147781b37b 100644
--- a/crates/zed/src/main.rs
+++ b/crates/zed/src/main.rs
@@ -174,7 +174,12 @@ fn init_common(app_state: Arc, cx: &mut AppContext) {
cx,
);
supermaven::init(app_state.client.clone(), cx);
- language_model::init(app_state.client.clone(), app_state.fs.clone(), cx);
+ language_model::init(
+ app_state.user_store.clone(),
+ app_state.client.clone(),
+ app_state.fs.clone(),
+ cx,
+ );
snippet_provider::init(cx);
inline_completion_registry::init(app_state.client.telemetry().clone(), cx);
assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs
index 6d7b47a158..ccc989f0c7 100644
--- a/crates/zed/src/zed.rs
+++ b/crates/zed/src/zed.rs
@@ -3467,7 +3467,12 @@ mod tests {
app_state.client.http_client().clone(),
cx,
);
- language_model::init(app_state.client.clone(), app_state.fs.clone(), cx);
+ language_model::init(
+ app_state.user_store.clone(),
+ app_state.client.clone(),
+ app_state.fs.clone(),
+ cx,
+ );
assistant::init(app_state.fs.clone(), app_state.client.clone(), cx);
repl::init(
app_state.fs.clone(),