Compare commits
4 Commits
fix_devcon
...
devcontain
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ebb401c4 | ||
|
|
ca47822667 | ||
|
|
a34fe06bb1 | ||
|
|
0ce484e66c |
@@ -278,6 +278,7 @@ pub struct AcpThreadView {
|
||||
thread_retry_status: Option<RetryStatus>,
|
||||
thread_error: Option<ThreadError>,
|
||||
thread_error_markdown: Option<Entity<Markdown>>,
|
||||
token_limit_callout_dismissed: bool,
|
||||
thread_feedback: ThreadFeedbackState,
|
||||
list_state: ListState,
|
||||
auth_task: Option<Task<()>>,
|
||||
@@ -430,13 +431,13 @@ impl AcpThreadView {
|
||||
message_editor,
|
||||
model_selector: None,
|
||||
profile_selector: None,
|
||||
|
||||
notifications: Vec::new(),
|
||||
notification_subscriptions: HashMap::default(),
|
||||
list_state: list_state,
|
||||
thread_retry_status: None,
|
||||
thread_error: None,
|
||||
thread_error_markdown: None,
|
||||
token_limit_callout_dismissed: false,
|
||||
thread_feedback: Default::default(),
|
||||
auth_task: None,
|
||||
expanded_tool_calls: HashSet::default(),
|
||||
@@ -1394,6 +1395,7 @@ impl AcpThreadView {
|
||||
fn clear_thread_error(&mut self, cx: &mut Context<Self>) {
|
||||
self.thread_error = None;
|
||||
self.thread_error_markdown = None;
|
||||
self.token_limit_callout_dismissed = true;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -5391,22 +5393,26 @@ impl AcpThreadView {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn render_token_limit_callout(
|
||||
&self,
|
||||
line_height: Pixels,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<Callout> {
|
||||
fn render_token_limit_callout(&self, cx: &mut Context<Self>) -> Option<Callout> {
|
||||
if self.token_limit_callout_dismissed {
|
||||
return None;
|
||||
}
|
||||
|
||||
let token_usage = self.thread()?.read(cx).token_usage()?;
|
||||
let ratio = token_usage.ratio();
|
||||
|
||||
let (severity, title) = match ratio {
|
||||
let (severity, icon, title) = match ratio {
|
||||
acp_thread::TokenUsageRatio::Normal => return None,
|
||||
acp_thread::TokenUsageRatio::Warning => {
|
||||
(Severity::Warning, "Thread reaching the token limit soon")
|
||||
}
|
||||
acp_thread::TokenUsageRatio::Exceeded => {
|
||||
(Severity::Error, "Thread reached the token limit")
|
||||
}
|
||||
acp_thread::TokenUsageRatio::Warning => (
|
||||
Severity::Warning,
|
||||
IconName::Warning,
|
||||
"Thread reaching the token limit soon",
|
||||
),
|
||||
acp_thread::TokenUsageRatio::Exceeded => (
|
||||
Severity::Error,
|
||||
IconName::XCircle,
|
||||
"Thread reached the token limit",
|
||||
),
|
||||
};
|
||||
|
||||
let burn_mode_available = self.as_native_thread(cx).is_some_and(|thread| {
|
||||
@@ -5426,7 +5432,7 @@ impl AcpThreadView {
|
||||
Some(
|
||||
Callout::new()
|
||||
.severity(severity)
|
||||
.line_height(line_height)
|
||||
.icon(icon)
|
||||
.title(title)
|
||||
.description(description)
|
||||
.actions_slot(
|
||||
@@ -5458,7 +5464,8 @@ impl AcpThreadView {
|
||||
})),
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.dismiss_action(self.dismiss_error_button(cx)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5892,7 +5899,7 @@ impl AcpThreadView {
|
||||
fn dismiss_error_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
IconButton::new("dismiss", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Dismiss Error"))
|
||||
.tooltip(Tooltip::text("Dismiss"))
|
||||
.on_click(cx.listener({
|
||||
move |this, _, _, cx| {
|
||||
this.clear_thread_error(cx);
|
||||
@@ -6152,7 +6159,7 @@ impl Render for AcpThreadView {
|
||||
if let Some(usage_callout) = self.render_usage_callout(line_height, cx) {
|
||||
Some(usage_callout.into_any_element())
|
||||
} else {
|
||||
self.render_token_limit_callout(line_height, cx)
|
||||
self.render_token_limit_callout(cx)
|
||||
.map(|token_limit_callout| token_limit_callout.into_any_element())
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "JSONC"
|
||||
grammar = "jsonc"
|
||||
path_suffixes = ["jsonc", "bun.lock", "tsconfig.json", "pyrightconfig.json"]
|
||||
path_suffixes = ["jsonc", "bun.lock", "devcontainer.json", "pyrightconfig.json", "tsconfig.json"]
|
||||
line_comments = ["// "]
|
||||
autoclose_before = ",]}"
|
||||
brackets = [
|
||||
|
||||
@@ -1293,34 +1293,13 @@ impl Project {
|
||||
cx.subscribe(&worktree_store, Self::on_worktree_store_event)
|
||||
.detach();
|
||||
if init_worktree_trust {
|
||||
let trust_remote_project = match &connection_options {
|
||||
RemoteConnectionOptions::Ssh(..) | RemoteConnectionOptions::Wsl(..) => false,
|
||||
RemoteConnectionOptions::Docker(..) => true,
|
||||
};
|
||||
let remote_host = RemoteHostLocation::from(connection_options);
|
||||
trusted_worktrees::track_worktree_trust(
|
||||
worktree_store.clone(),
|
||||
Some(remote_host.clone()),
|
||||
Some(RemoteHostLocation::from(connection_options)),
|
||||
None,
|
||||
Some((remote_proto.clone(), REMOTE_SERVER_PROJECT_ID)),
|
||||
cx,
|
||||
);
|
||||
if trust_remote_project {
|
||||
if let Some(trusted_worktres) = TrustedWorktrees::try_get_global(cx) {
|
||||
trusted_worktres.update(cx, |trusted_worktres, cx| {
|
||||
trusted_worktres.trust(
|
||||
worktree_store
|
||||
.read(cx)
|
||||
.worktrees()
|
||||
.map(|worktree| worktree.read(cx).id())
|
||||
.map(PathTrust::Worktree)
|
||||
.collect(),
|
||||
Some(remote_host),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let weak_self = cx.weak_entity();
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::AsyncWindowContext;
|
||||
use gpui::{
|
||||
Action, AsyncWindowContext, DismissEvent, EventEmitter, FocusHandle, Focusable, RenderOnce,
|
||||
};
|
||||
use node_runtime::NodeRuntime;
|
||||
use serde::Deserialize;
|
||||
use settings::DevContainerConnection;
|
||||
use smol::fs;
|
||||
use workspace::Workspace;
|
||||
use ui::{
|
||||
App, Color, Context, Headline, HeadlineSize, Icon, IconName, InteractiveElement, IntoElement,
|
||||
Label, ListItem, ListSeparator, ModalHeader, Navigable, NavigableEntry, ParentElement, Render,
|
||||
Styled, StyledExt, Toggleable, Window, div, rems,
|
||||
};
|
||||
use workspace::{ModalView, Workspace, with_active_or_new_workspace};
|
||||
|
||||
use crate::remote_connections::Connection;
|
||||
|
||||
@@ -53,9 +60,7 @@ async fn check_for_docker() -> Result<(), DevContainerError> {
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_devcontainer_cli(
|
||||
node_runtime: &NodeRuntime,
|
||||
) -> Result<(PathBuf, bool), DevContainerError> {
|
||||
async fn ensure_devcontainer_cli(node_runtime: NodeRuntime) -> Result<PathBuf, DevContainerError> {
|
||||
let mut command = util::command::new_smol_command(&dev_container_cli());
|
||||
command.arg("--version");
|
||||
|
||||
@@ -65,42 +70,23 @@ async fn ensure_devcontainer_cli(
|
||||
e
|
||||
);
|
||||
|
||||
let Ok(node_runtime_path) = node_runtime.binary_path().await else {
|
||||
return Err(DevContainerError::NodeRuntimeNotAvailable);
|
||||
};
|
||||
|
||||
let datadir_cli_path = paths::devcontainer_dir()
|
||||
.join("node_modules")
|
||||
.join("@devcontainers")
|
||||
.join("cli")
|
||||
.join(format!("{}.js", &dev_container_cli()));
|
||||
|
||||
log::debug!(
|
||||
"devcontainer not found in path, using local location: ${}",
|
||||
datadir_cli_path.display()
|
||||
);
|
||||
.join(".bin")
|
||||
.join(&dev_container_cli());
|
||||
|
||||
let mut command =
|
||||
util::command::new_smol_command(node_runtime_path.as_os_str().display().to_string());
|
||||
command.arg(datadir_cli_path.display().to_string());
|
||||
util::command::new_smol_command(&datadir_cli_path.as_os_str().display().to_string());
|
||||
command.arg("--version");
|
||||
|
||||
match command.output().await {
|
||||
Err(e) => log::error!(
|
||||
if let Err(e) = command.output().await {
|
||||
log::error!(
|
||||
"Unable to find devcontainer CLI in Data dir. Will try to install. Error: {:?}",
|
||||
e
|
||||
),
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
log::info!("Found devcontainer CLI in Data dir");
|
||||
return Ok((datadir_cli_path.clone(), false));
|
||||
} else {
|
||||
log::error!(
|
||||
"Could not run devcontainer CLI from data_dir. Will try once more to install. Output: {:?}",
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
log::info!("Found devcontainer CLI in Data dir");
|
||||
return Ok(datadir_cli_path.clone());
|
||||
}
|
||||
|
||||
if let Err(e) = fs::create_dir_all(paths::devcontainer_dir()).await {
|
||||
@@ -122,9 +108,7 @@ async fn ensure_devcontainer_cli(
|
||||
return Err(DevContainerError::DevContainerCliNotAvailable);
|
||||
};
|
||||
|
||||
let mut command =
|
||||
util::command::new_smol_command(node_runtime_path.as_os_str().display().to_string());
|
||||
command.arg(datadir_cli_path.display().to_string());
|
||||
let mut command = util::command::new_smol_command(&datadir_cli_path.display().to_string());
|
||||
command.arg("--version");
|
||||
if let Err(e) = command.output().await {
|
||||
log::error!(
|
||||
@@ -133,42 +117,22 @@ async fn ensure_devcontainer_cli(
|
||||
);
|
||||
Err(DevContainerError::DevContainerCliNotAvailable)
|
||||
} else {
|
||||
Ok((datadir_cli_path, false))
|
||||
Ok(datadir_cli_path)
|
||||
}
|
||||
} else {
|
||||
log::info!("Found devcontainer cli on $PATH, using it");
|
||||
Ok((PathBuf::from(&dev_container_cli()), true))
|
||||
Ok(PathBuf::from(&dev_container_cli()))
|
||||
}
|
||||
}
|
||||
|
||||
async fn devcontainer_up(
|
||||
path_to_cli: &PathBuf,
|
||||
found_in_path: bool,
|
||||
node_runtime: &NodeRuntime,
|
||||
path: Arc<Path>,
|
||||
) -> Result<DevContainerUp, DevContainerError> {
|
||||
let Ok(node_runtime_path) = node_runtime.binary_path().await else {
|
||||
log::error!("Unable to find node runtime path");
|
||||
return Err(DevContainerError::NodeRuntimeNotAvailable);
|
||||
};
|
||||
|
||||
let mut command = if found_in_path {
|
||||
let mut command = util::command::new_smol_command(path_to_cli.display().to_string());
|
||||
command.arg("up");
|
||||
command.arg("--workspace-folder");
|
||||
command.arg(path.display().to_string());
|
||||
command
|
||||
} else {
|
||||
let mut command =
|
||||
util::command::new_smol_command(node_runtime_path.as_os_str().display().to_string());
|
||||
command.arg(path_to_cli.display().to_string());
|
||||
command.arg("up");
|
||||
command.arg("--workspace-folder");
|
||||
command.arg(path.display().to_string());
|
||||
command
|
||||
};
|
||||
|
||||
log::debug!("Running full devcontainer up command: {:?}", command);
|
||||
let mut command = util::command::new_smol_command(path_to_cli.display().to_string());
|
||||
command.arg("up");
|
||||
command.arg("--workspace-folder");
|
||||
command.arg(path.display().to_string());
|
||||
|
||||
match command.output().await {
|
||||
Ok(output) => {
|
||||
@@ -278,7 +242,7 @@ pub(crate) async fn start_dev_container(
|
||||
) -> Result<(Connection, String), DevContainerError> {
|
||||
check_for_docker().await?;
|
||||
|
||||
let (path_to_devcontainer_cli, found_in_path) = ensure_devcontainer_cli(&node_runtime).await?;
|
||||
let path_to_devcontainer_cli = ensure_devcontainer_cli(node_runtime).await?;
|
||||
|
||||
let Some(directory) = project_directory(cx) else {
|
||||
return Err(DevContainerError::DevContainerNotFound);
|
||||
@@ -288,13 +252,7 @@ pub(crate) async fn start_dev_container(
|
||||
container_id,
|
||||
remote_workspace_folder,
|
||||
..
|
||||
}) = devcontainer_up(
|
||||
&path_to_devcontainer_cli,
|
||||
found_in_path,
|
||||
&node_runtime,
|
||||
directory.clone(),
|
||||
)
|
||||
.await
|
||||
}) = devcontainer_up(&path_to_devcontainer_cli, directory.clone()).await
|
||||
{
|
||||
let project_name = get_project_name(
|
||||
&path_to_devcontainer_cli,
|
||||
@@ -322,7 +280,122 @@ pub(crate) enum DevContainerError {
|
||||
DevContainerUpFailed,
|
||||
DevContainerNotFound,
|
||||
DevContainerParseFailed,
|
||||
NodeRuntimeNotAvailable,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[action(namespace = containers)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct InitDevContainer;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.on_action(|_: &InitDevContainer, cx| {
|
||||
with_active_or_new_workspace(cx, move |workspace, window, cx| {
|
||||
workspace.toggle_modal(window, cx, |window, cx| DevContainerModal::new(window, cx));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
struct DevContainerModal {
|
||||
focus_handle: FocusHandle,
|
||||
search_navigable_entry: NavigableEntry,
|
||||
other_navigable_entry: NavigableEntry,
|
||||
}
|
||||
|
||||
impl DevContainerModal {
|
||||
fn new(window: &mut Window, cx: &mut App) -> Self {
|
||||
let search_navigable_entry = NavigableEntry::focusable(cx);
|
||||
let other_navigable_entry = NavigableEntry::focusable(cx);
|
||||
let focus_handle = cx.focus_handle();
|
||||
DevContainerModal {
|
||||
focus_handle,
|
||||
search_navigable_entry,
|
||||
other_navigable_entry,
|
||||
}
|
||||
}
|
||||
|
||||
fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
||||
impl ModalView for DevContainerModal {}
|
||||
impl EventEmitter<DismissEvent> for DevContainerModal {}
|
||||
impl Focusable for DevContainerModal {
|
||||
fn focus_handle(&self, _cx: &App) -> FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for DevContainerModal {
|
||||
fn render(
|
||||
&mut self,
|
||||
window: &mut ui::Window,
|
||||
cx: &mut ui::Context<Self>,
|
||||
) -> impl ui::IntoElement {
|
||||
let mut view =
|
||||
Navigable::new(
|
||||
div()
|
||||
.child(div().track_focus(&self.focus_handle).child(
|
||||
ModalHeader::new().child(
|
||||
Headline::new("Create Dev Container").size(HeadlineSize::XSmall),
|
||||
),
|
||||
))
|
||||
.child(ListSeparator)
|
||||
.child(
|
||||
div()
|
||||
.track_focus(&self.search_navigable_entry.focus_handle)
|
||||
.on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
|
||||
println!("action on search containers");
|
||||
}))
|
||||
.child(
|
||||
ListItem::new("li-search-containers")
|
||||
.inset(true)
|
||||
.spacing(ui::ListItemSpacing::Sparse)
|
||||
.start_slot(Icon::new(IconName::Pencil).color(Color::Muted))
|
||||
.toggle_state(
|
||||
self.search_navigable_entry
|
||||
.focus_handle
|
||||
.contains_focused(window, cx),
|
||||
)
|
||||
.child(Label::new("Search for dev containers in registry")),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.track_focus(&self.other_navigable_entry.focus_handle)
|
||||
.on_action(cx.listener(|this, _: &menu::Confirm, window, cx| {
|
||||
println!("action on other containers");
|
||||
}))
|
||||
.child(
|
||||
ListItem::new("li-search-containers")
|
||||
.inset(true)
|
||||
.spacing(ui::ListItemSpacing::Sparse)
|
||||
.start_slot(Icon::new(IconName::Pencil).color(Color::Muted))
|
||||
.toggle_state(
|
||||
self.other_navigable_entry
|
||||
.focus_handle
|
||||
.contains_focused(window, cx),
|
||||
)
|
||||
.child(Label::new("Do another thing")),
|
||||
),
|
||||
)
|
||||
.into_any_element(),
|
||||
);
|
||||
view = view.entry(self.search_navigable_entry.clone());
|
||||
view = view.entry(self.other_navigable_entry.clone());
|
||||
|
||||
// // This is an interesting edge. Can't focus in render, or you'll just override whatever was focused before.
|
||||
// // self.search_navigable_entry.focus_handle.focus(window, cx);
|
||||
|
||||
// view.render(window, cx).into_any_element()
|
||||
div()
|
||||
.elevation_3(cx)
|
||||
.w(rems(34.))
|
||||
// WHY IS THIS NEEDED FOR ACTION DISPATCH OMG
|
||||
.key_context("ContainerModal")
|
||||
.on_action(cx.listener(Self::dismiss))
|
||||
.child(view.render(window, cx).into_any_element())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod dev_container;
|
||||
pub mod dev_container;
|
||||
mod dev_container_suggest;
|
||||
pub mod disconnected_overlay;
|
||||
mod remote_connections;
|
||||
|
||||
@@ -33,7 +33,7 @@ use assets::Assets;
|
||||
use node_runtime::{NodeBinaryOptions, NodeRuntime};
|
||||
use parking_lot::Mutex;
|
||||
use project::{project_settings::ProjectSettings, trusted_worktrees};
|
||||
use recent_projects::{SshSettings, open_remote_project};
|
||||
use recent_projects::{SshSettings, dev_container, open_remote_project};
|
||||
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
|
||||
use session::{AppSession, Session};
|
||||
use settings::{BaseKeymap, Settings, SettingsStore, watch_config_file};
|
||||
@@ -616,6 +616,7 @@ fn main() {
|
||||
agent_ui_v2::agents_panel::init(cx);
|
||||
repl::init(app_state.fs.clone(), cx);
|
||||
recent_projects::init(cx);
|
||||
dev_container::init(cx);
|
||||
|
||||
load_embedded_fonts(cx);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user