mirror of
https://github.com/rust-kotlin/ashell.git
synced 2026-09-22 00:00:59 +00:00
style: run cargo fmt
This commit is contained in:
@@ -128,12 +128,20 @@ pub(crate) const WORKSPACE_ACTIONS: &[WorkspaceAction] = &[
|
||||
WorkspaceAction {
|
||||
id: "Copy",
|
||||
label_key: "settings_copy",
|
||||
default_suffix: if cfg!(target_os = "macos") { "c" } else { "shift-c" },
|
||||
default_suffix: if cfg!(target_os = "macos") {
|
||||
"c"
|
||||
} else {
|
||||
"shift-c"
|
||||
},
|
||||
},
|
||||
WorkspaceAction {
|
||||
id: "Paste",
|
||||
label_key: "settings_paste",
|
||||
default_suffix: if cfg!(target_os = "macos") { "v" } else { "shift-v" },
|
||||
default_suffix: if cfg!(target_os = "macos") {
|
||||
"v"
|
||||
} else {
|
||||
"shift-v"
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -305,7 +313,15 @@ impl KeybindingsPage {
|
||||
let groups = [
|
||||
(
|
||||
"settings_group_keybind_general",
|
||||
vec!["OpenSettings", "OpenSession", "OpenTransfers", "NewSsh", "OpenSearch", "Copy", "Paste"],
|
||||
vec![
|
||||
"OpenSettings",
|
||||
"OpenSession",
|
||||
"OpenTransfers",
|
||||
"NewSsh",
|
||||
"OpenSearch",
|
||||
"Copy",
|
||||
"Paste",
|
||||
],
|
||||
),
|
||||
(
|
||||
"settings_group_keybind_zoom",
|
||||
|
||||
+13
-18
@@ -4,17 +4,15 @@ use std::{
|
||||
};
|
||||
|
||||
use gpui::{
|
||||
Along, AnyElement, App, Axis, Bounds, Context, Element, ElementId, Empty, Entity,
|
||||
EventEmitter, InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent,
|
||||
ParentElement, Pixels, Render, RenderOnce, Style, StyleRefinement, Styled, Window, div,
|
||||
prelude::FluentBuilder, AppContext as _, IsZero as _,
|
||||
Along, AnyElement, App, AppContext as _, Axis, Bounds, Context, Element, ElementId, Empty,
|
||||
Entity, EventEmitter, InteractiveElement as _, IntoElement, IsZero as _, MouseMoveEvent,
|
||||
MouseUpEvent, ParentElement, Pixels, Render, RenderOnce, Style, StyleRefinement, Styled,
|
||||
Window, div, prelude::FluentBuilder,
|
||||
};
|
||||
|
||||
use gpui_component::{
|
||||
AxisExt, ElementExt, h_flex, v_flex, StyledExt,
|
||||
};
|
||||
use gpui_component::{AxisExt, ElementExt, StyledExt, h_flex, v_flex};
|
||||
|
||||
use super::{ResizableState, resizable_panel, resize_handle, PANEL_MIN_SIZE, ResizablePanelEvent};
|
||||
use super::{PANEL_MIN_SIZE, ResizablePanelEvent, ResizableState, resizable_panel, resize_handle};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DragPanel;
|
||||
@@ -305,16 +303,13 @@ impl RenderOnce for ResizablePanel {
|
||||
let handle = if self.locked {
|
||||
handle
|
||||
} else {
|
||||
handle.on_drag(
|
||||
DragPanel,
|
||||
move |drag_panel, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
state.update(cx, |state, _| {
|
||||
state.resizing_panel_ix = Some(ix);
|
||||
});
|
||||
cx.new(|_| drag_panel.deref().clone())
|
||||
},
|
||||
)
|
||||
handle.on_drag(DragPanel, move |drag_panel, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
state.update(cx, |state, _| {
|
||||
state.resizing_panel_ix = Some(ix);
|
||||
});
|
||||
cx.new(|_| drag_panel.deref().clone())
|
||||
})
|
||||
};
|
||||
this.child(handle)
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, AnyElement, App, Axis, Element, ElementId, Entity,
|
||||
GlobalElementId, InteractiveElement, IntoElement, MouseDownEvent, MouseUpEvent,
|
||||
ParentElement as _, Pixels, Point, Render, StatefulInteractiveElement, Styled as _, Window,
|
||||
AnyElement, App, Axis, Element, ElementId, Entity, GlobalElementId, InteractiveElement,
|
||||
IntoElement, MouseDownEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render,
|
||||
StatefulInteractiveElement, Styled as _, Window, div, prelude::FluentBuilder as _, px,
|
||||
};
|
||||
|
||||
use gpui_component::{ActiveTheme as _, AxisExt as _, dock::DockPlacement};
|
||||
@@ -126,14 +126,13 @@ impl<T: 'static, E: 'static + Render> Element for ResizeHandle<T, E> {
|
||||
)
|
||||
})
|
||||
.map(|this| match self.placement {
|
||||
Some(DockPlacement::Left) => {
|
||||
this.when(has_drag, |this| this.cursor_col_resize())
|
||||
.top_0()
|
||||
.right(px(1.))
|
||||
.h_full()
|
||||
.w(HANDLE_SIZE)
|
||||
.pl(HANDLE_PADDING)
|
||||
}
|
||||
Some(DockPlacement::Left) => this
|
||||
.when(has_drag, |this| this.cursor_col_resize())
|
||||
.top_0()
|
||||
.right(px(1.))
|
||||
.h_full()
|
||||
.w(HANDLE_SIZE)
|
||||
.pl(HANDLE_PADDING),
|
||||
_ => this
|
||||
.when(axis.is_horizontal(), |this| {
|
||||
this.when(has_drag, |this| this.cursor_col_resize())
|
||||
|
||||
+31
-29
@@ -1,13 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use gpui::{
|
||||
Context, Focusable as _, Hsla, IntoElement, InteractiveElement as _, MouseButton,
|
||||
Context, Focusable as _, Hsla, InteractiveElement as _, IntoElement, MouseButton,
|
||||
ParentElement as _, Styled as _, Window, div, prelude::FluentBuilder as _, px, rems,
|
||||
};
|
||||
use gpui_component::{
|
||||
ActiveTheme as _, Disableable as _, ElementExt as _, IconName, Sizable as _,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex, input::Input,
|
||||
h_flex,
|
||||
input::Input,
|
||||
};
|
||||
use rust_i18n::t;
|
||||
|
||||
@@ -323,23 +324,19 @@ impl Ashell {
|
||||
}
|
||||
|
||||
/// Render the search button (used in the tab bar).
|
||||
pub(crate) fn render_search_button(
|
||||
&self,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl gpui::IntoElement {
|
||||
pub(crate) fn render_search_button(&self, cx: &mut Context<Self>) -> impl gpui::IntoElement {
|
||||
// Wrap in a div so .hover() doesn't conflict with Button's internal hover.
|
||||
div()
|
||||
.child(
|
||||
Button::new("search-btn")
|
||||
.ghost()
|
||||
.small()
|
||||
.rounded(px(999.))
|
||||
.icon(IconName::Search)
|
||||
.tooltip(t!("search").to_string())
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.toggle_search(window, cx);
|
||||
})),
|
||||
)
|
||||
div().child(
|
||||
Button::new("search-btn")
|
||||
.ghost()
|
||||
.small()
|
||||
.rounded(px(999.))
|
||||
.icon(IconName::Search)
|
||||
.tooltip(t!("search").to_string())
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.toggle_search(window, cx);
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
/// Render the expanded search bar overlay (when search is active).
|
||||
@@ -369,10 +366,13 @@ impl Ashell {
|
||||
this.search_bar_bounds = Some(bounds);
|
||||
});
|
||||
})
|
||||
.on_mouse_down(MouseButton::Left, cx.listener(|this, _, window, cx| {
|
||||
this.refocus_search_input(window, cx);
|
||||
cx.stop_propagation();
|
||||
}))
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.refocus_search_input(window, cx);
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
@@ -385,13 +385,15 @@ impl Ashell {
|
||||
.child(
|
||||
div()
|
||||
.w(px(200.))
|
||||
.on_key_down(cx.listener(|this, event: &gpui::KeyDownEvent, window, cx| {
|
||||
if event.keystroke.key.as_str() == "escape" {
|
||||
this.close_search(window, cx);
|
||||
window.prevent_default();
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}))
|
||||
.on_key_down(cx.listener(
|
||||
|this, event: &gpui::KeyDownEvent, window, cx| {
|
||||
if event.keystroke.key.as_str() == "escape" {
|
||||
this.close_search(window, cx);
|
||||
window.prevent_default();
|
||||
cx.stop_propagation();
|
||||
}
|
||||
},
|
||||
))
|
||||
.child(Input::new(&self.search_input).small()),
|
||||
)
|
||||
.when(!current_display.is_empty(), |this| {
|
||||
|
||||
+11
-4
@@ -179,7 +179,14 @@ pub(crate) fn sync_macos_launch_environment() {
|
||||
}
|
||||
|
||||
fn read_proxy_from_env() -> Option<(String, String, Option<u16>, String, String)> {
|
||||
let vars = ["ALL_PROXY", "all_proxy", "HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
|
||||
let vars = [
|
||||
"ALL_PROXY",
|
||||
"all_proxy",
|
||||
"HTTPS_PROXY",
|
||||
"https_proxy",
|
||||
"HTTP_PROXY",
|
||||
"http_proxy",
|
||||
];
|
||||
for var in vars {
|
||||
if let Ok(val) = std::env::var(var) {
|
||||
if val.is_empty() {
|
||||
@@ -301,13 +308,13 @@ pub(crate) fn open_main_window(cx: &mut App) {
|
||||
let focus_handle = view.read(cx).focus_handle.clone();
|
||||
window.focus(&focus_handle, cx);
|
||||
|
||||
|
||||
|
||||
let view_clone = view.clone();
|
||||
window.on_window_should_close(cx, move |window: &mut gpui::Window, cx: &mut gpui::App| {
|
||||
let handle = window.window_handle();
|
||||
if !cx.windows().contains(&handle) {
|
||||
tracing::warn!("[ui] window not found in app during close, skipping save layout state.");
|
||||
tracing::warn!(
|
||||
"[ui] window not found in app during close, skipping save layout state."
|
||||
);
|
||||
return true;
|
||||
}
|
||||
view_clone.read(cx).save_layout_state(window, cx);
|
||||
|
||||
+4
-1
@@ -17,7 +17,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
pub(crate) static USING_SYSTEM_MAPLE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub(crate) fn load_fonts(cx: &mut App) -> Result<()> {
|
||||
let has_system_maple = cx.text_system().all_font_names().contains(&"Maple Mono NF CN".to_string());
|
||||
let has_system_maple = cx
|
||||
.text_system()
|
||||
.all_font_names()
|
||||
.contains(&"Maple Mono NF CN".to_string());
|
||||
if has_system_maple {
|
||||
USING_SYSTEM_MAPLE.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
|
||||
+70
-49
@@ -1,3 +1,4 @@
|
||||
use crate::app::resizable::{h_resizable, resizable_panel, v_resizable};
|
||||
use gpui::{
|
||||
Context, ElementId, Focusable as _, FontWeight, Hsla, InteractiveElement as _, IntoElement,
|
||||
MouseButton, MouseDownEvent, ParentElement as _, PathBuilder, Pixels, Render,
|
||||
@@ -17,7 +18,6 @@ use gpui_component::{
|
||||
tab::{Tab, TabBar},
|
||||
v_flex,
|
||||
};
|
||||
use crate::app::resizable::{h_resizable, resizable_panel, v_resizable};
|
||||
use rust_i18n::t;
|
||||
|
||||
use crate::{
|
||||
@@ -1984,7 +1984,7 @@ impl Ashell {
|
||||
) -> impl IntoElement {
|
||||
let is_macos = cfg!(target_os = "macos");
|
||||
let is_fullscreen = window.is_fullscreen();
|
||||
|
||||
|
||||
let is_active = cx.active_window() == Some(window.window_handle());
|
||||
|
||||
h_flex()
|
||||
@@ -1999,9 +1999,17 @@ impl Ashell {
|
||||
.id("window-close")
|
||||
.size(px(12.))
|
||||
.rounded_full()
|
||||
.bg(if is_active { hsla(3.0 / 360.0, 1.0, 0.67, 1.0) } else { hsla(0.0, 0.0, 0.8, 1.0) }) // Red or Inactive Grey
|
||||
.group_hover("window-controls", |s| s.bg(hsla(3.0 / 360.0, 1.0, 0.67, 1.0)))
|
||||
.when(!is_macos, |this| this.window_control_area(gpui::WindowControlArea::Close))
|
||||
.bg(if is_active {
|
||||
hsla(3.0 / 360.0, 1.0, 0.67, 1.0)
|
||||
} else {
|
||||
hsla(0.0, 0.0, 0.8, 1.0)
|
||||
}) // Red or Inactive Grey
|
||||
.group_hover("window-controls", |s| {
|
||||
s.bg(hsla(3.0 / 360.0, 1.0, 0.67, 1.0))
|
||||
})
|
||||
.when(!is_macos, |this| {
|
||||
this.window_control_area(gpui::WindowControlArea::Close)
|
||||
})
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.save_layout_state(window, cx);
|
||||
window.remove_window();
|
||||
@@ -2020,7 +2028,7 @@ impl Ashell {
|
||||
.text_color(hsla(3.0 / 360.0, 1.0, 0.15, 0.7))
|
||||
.opacity(0.0)
|
||||
.group_hover("window-controls", |s| s.opacity(1.0))
|
||||
.child("✕")
|
||||
.child("✕"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -2028,9 +2036,17 @@ impl Ashell {
|
||||
.id("window-minimize")
|
||||
.size(px(12.))
|
||||
.rounded_full()
|
||||
.bg(if is_active { hsla(39.0 / 360.0, 1.0, 0.59, 1.0) } else { hsla(0.0, 0.0, 0.8, 1.0) }) // Yellow or Inactive Grey
|
||||
.group_hover("window-controls", |s| s.bg(hsla(39.0 / 360.0, 1.0, 0.59, 1.0)))
|
||||
.when(!is_macos, |this| this.window_control_area(gpui::WindowControlArea::Min))
|
||||
.bg(if is_active {
|
||||
hsla(39.0 / 360.0, 1.0, 0.59, 1.0)
|
||||
} else {
|
||||
hsla(0.0, 0.0, 0.8, 1.0)
|
||||
}) // Yellow or Inactive Grey
|
||||
.group_hover("window-controls", |s| {
|
||||
s.bg(hsla(39.0 / 360.0, 1.0, 0.59, 1.0))
|
||||
})
|
||||
.when(!is_macos, |this| {
|
||||
this.window_control_area(gpui::WindowControlArea::Min)
|
||||
})
|
||||
.on_click(|_, window, _| window.minimize_window())
|
||||
.hover(|s| s.bg(hsla(39.0 / 360.0, 1.0, 0.49, 1.0)))
|
||||
.active(|s| s.bg(hsla(39.0 / 360.0, 1.0, 0.39, 1.0)))
|
||||
@@ -2046,7 +2062,7 @@ impl Ashell {
|
||||
.text_color(hsla(39.0 / 360.0, 1.0, 0.15, 0.8))
|
||||
.opacity(0.0)
|
||||
.group_hover("window-controls", |s| s.opacity(1.0))
|
||||
.child("−")
|
||||
.child("−"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -2054,9 +2070,17 @@ impl Ashell {
|
||||
.id("window-maximize")
|
||||
.size(px(12.))
|
||||
.rounded_full()
|
||||
.bg(if is_active { hsla(127.0 / 360.0, 0.68, 0.47, 1.0) } else { hsla(0.0, 0.0, 0.8, 1.0) }) // Green or Inactive Grey
|
||||
.group_hover("window-controls", |s| s.bg(hsla(127.0 / 360.0, 0.68, 0.47, 1.0)))
|
||||
.when(!is_macos, |this| this.window_control_area(gpui::WindowControlArea::Max))
|
||||
.bg(if is_active {
|
||||
hsla(127.0 / 360.0, 0.68, 0.47, 1.0)
|
||||
} else {
|
||||
hsla(0.0, 0.0, 0.8, 1.0)
|
||||
}) // Green or Inactive Grey
|
||||
.group_hover("window-controls", |s| {
|
||||
s.bg(hsla(127.0 / 360.0, 0.68, 0.47, 1.0))
|
||||
})
|
||||
.when(!is_macos, |this| {
|
||||
this.window_control_area(gpui::WindowControlArea::Max)
|
||||
})
|
||||
.on_click(|_, window, _| {
|
||||
if window.is_fullscreen() {
|
||||
window.toggle_fullscreen();
|
||||
@@ -2081,7 +2105,7 @@ impl Ashell {
|
||||
.text_color(hsla(127.0 / 360.0, 1.0, 0.15, 0.8))
|
||||
.opacity(0.0)
|
||||
.group_hover("window-controls", |s| s.opacity(1.0))
|
||||
.child("+")
|
||||
.child("+"),
|
||||
),
|
||||
)
|
||||
})
|
||||
@@ -2334,7 +2358,10 @@ impl Ashell {
|
||||
let font_size = px(this.terminal_font_size);
|
||||
let line_height = px(this.terminal_line_height());
|
||||
let cell_width = px(this.terminal_cell_width());
|
||||
let is_url_hovered = this.hovered_url.as_ref().map_or(false, |hu| hu.tab_id == *tab_id);
|
||||
let is_url_hovered = this
|
||||
.hovered_url
|
||||
.as_ref()
|
||||
.map_or(false, |hu| hu.tab_id == *tab_id);
|
||||
let mut el = div()
|
||||
.size_full()
|
||||
.overflow_hidden()
|
||||
@@ -2375,44 +2402,38 @@ impl Ashell {
|
||||
.and_then(|tab| tab.disconnected_reason.clone());
|
||||
if let Some(reason) = disconnected_reason {
|
||||
let tab_id_for_reconnect = tab_id.clone();
|
||||
el = div()
|
||||
.size_full()
|
||||
.relative()
|
||||
.child(el)
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.left_0()
|
||||
.right_0()
|
||||
el = div().size_full().relative().child(el).child(
|
||||
div().absolute().bottom_0().left_0().right_0().child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.px_3()
|
||||
.py_1()
|
||||
.bg(cx.theme().danger.opacity(0.15))
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.px_3()
|
||||
.py_1()
|
||||
.bg(cx.theme().danger.opacity(0.15))
|
||||
div()
|
||||
.text_size(rems(0.85))
|
||||
.text_color(cx.theme().danger)
|
||||
.child(
|
||||
div()
|
||||
.text_size(rems(0.85))
|
||||
.text_color(cx.theme().danger)
|
||||
.child(t!("session_disconnected", "reason" = reason).to_string()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(rems(0.85))
|
||||
.text_color(cx.theme().muted_foreground)
|
||||
.child(format!("— {}", t!("press_enter_to_reconnect"))),
|
||||
)
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(move |this, _, _, cx| {
|
||||
this.retry_disconnected_tab(&tab_id_for_reconnect, cx);
|
||||
}),
|
||||
t!("session_disconnected", "reason" = reason)
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(rems(0.85))
|
||||
.text_color(cx.theme().muted_foreground)
|
||||
.child(format!("— {}", t!("press_enter_to_reconnect"))),
|
||||
)
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(move |this, _, _, cx| {
|
||||
this.retry_disconnected_tab(&tab_id_for_reconnect, cx);
|
||||
}),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
let indicator_color = this
|
||||
.tabs
|
||||
|
||||
+5
-5
@@ -322,7 +322,9 @@ async fn connect_and_authenticate(
|
||||
break;
|
||||
}
|
||||
Ok(false) => {
|
||||
tracing::debug!("[ssh] public key auth failed with algorithm, trying next");
|
||||
tracing::debug!(
|
||||
"[ssh] public key auth failed with algorithm, trying next"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -344,8 +346,7 @@ async fn connect_and_authenticate(
|
||||
success
|
||||
} else {
|
||||
let success =
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase)
|
||||
.await?;
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase).await?;
|
||||
if !success {
|
||||
return Err(anyhow::anyhow!(
|
||||
"public key authentication failed for {}@{}:{} - no valid default key found in ~/.ssh/",
|
||||
@@ -417,8 +418,7 @@ async fn connect_and_authenticate(
|
||||
),
|
||||
});
|
||||
let success =
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase)
|
||||
.await?;
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase).await?;
|
||||
if !success {
|
||||
return Err(anyhow::anyhow!(
|
||||
"ssh-config authentication failed for {}@{}:{} - no valid default key found",
|
||||
|
||||
+4
-6
@@ -16,14 +16,12 @@ rust_i18n::i18n!("locales", fallback = "en");
|
||||
gpui::actions!(ashell_terminal, [TerminalTabKey, TerminalBacktabKey]);
|
||||
|
||||
pub(crate) use app::keybinding_recorder::{
|
||||
ClosePane, FocusPaneDown, FocusPaneLeft, FocusPaneRight, FocusPaneUp, NewSsh, OpenSearch,
|
||||
OpenSession, OpenSettings, OpenTransfers, SplitPaneDown, SplitPaneLeft, SplitPaneRight,
|
||||
SplitPaneUp, ToggleSftpZoom, ToggleSidebar, Copy, Paste,
|
||||
ClosePane, Copy, FocusPaneDown, FocusPaneLeft, FocusPaneRight, FocusPaneUp, NewSsh, OpenSearch,
|
||||
OpenSession, OpenSettings, OpenTransfers, Paste, SplitPaneDown, SplitPaneLeft, SplitPaneRight,
|
||||
SplitPaneUp, ToggleSftpZoom, ToggleSidebar,
|
||||
};
|
||||
|
||||
pub(crate) use app::{
|
||||
Ashell, PaneLayout, SelectorEntry, SftpContextMenuState, TabGroup,
|
||||
};
|
||||
pub(crate) use app::{Ashell, PaneLayout, SelectorEntry, SftpContextMenuState, TabGroup};
|
||||
|
||||
fn main() {
|
||||
app::startup::sync_macos_launch_environment();
|
||||
|
||||
+23
-15
@@ -351,8 +351,8 @@ impl ConfigStore {
|
||||
}
|
||||
|
||||
let mut cache = if path.exists() {
|
||||
let raw_bytes = fs::read(&path)
|
||||
.with_context(|| format!("failed to read {}", path.display()))?;
|
||||
let raw_bytes =
|
||||
fs::read(&path).with_context(|| format!("failed to read {}", path.display()))?;
|
||||
let hardware_uuid = get_hardware_uuid();
|
||||
match decrypt_config(&raw_bytes, &hardware_uuid) {
|
||||
Ok(cache) => cache,
|
||||
@@ -1031,7 +1031,12 @@ fn get_hardware_uuid() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("reg")
|
||||
.args(&["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"])
|
||||
.args(&[
|
||||
"query",
|
||||
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
||||
"/v",
|
||||
"MachineGuid",
|
||||
])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
@@ -1069,17 +1074,17 @@ fn encrypt_config(config: &ConfigFile, password: &str) -> Result<Vec<u8>> {
|
||||
let mut nonce = [0u8; 24];
|
||||
OsRng.fill_bytes(&mut salt);
|
||||
OsRng.fill_bytes(&mut nonce);
|
||||
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
Argon2::default()
|
||||
.hash_password_into(password.as_bytes(), &salt, &mut key)
|
||||
.map_err(|err| anyhow::anyhow!("derive encryption key: {err}"))?;
|
||||
|
||||
|
||||
let plaintext = serde_json::to_vec(config).context("serialize config")?;
|
||||
let ciphertext = XChaCha20Poly1305::new((&key).into())
|
||||
.encrypt(XNonce::from_slice(&nonce), plaintext.as_ref())
|
||||
.map_err(|_| anyhow::anyhow!("encrypt config payload"))?;
|
||||
|
||||
|
||||
serde_json::to_vec_pretty(&EncryptedConfigEnvelope {
|
||||
format_version: 1,
|
||||
kdf: "argon2id".to_string(),
|
||||
@@ -1100,7 +1105,9 @@ fn decrypt_config(raw: &[u8], password: &str) -> Result<ConfigFile> {
|
||||
{
|
||||
return Err(anyhow::anyhow!("unsupported encrypted config format"));
|
||||
}
|
||||
let salt = STANDARD.decode(envelope.salt).context("decode config salt")?;
|
||||
let salt = STANDARD
|
||||
.decode(envelope.salt)
|
||||
.context("decode config salt")?;
|
||||
let nonce = STANDARD
|
||||
.decode(envelope.nonce)
|
||||
.context("decode config nonce")?;
|
||||
@@ -1110,16 +1117,18 @@ fn decrypt_config(raw: &[u8], password: &str) -> Result<ConfigFile> {
|
||||
let ciphertext = STANDARD
|
||||
.decode(envelope.payload)
|
||||
.context("decode encrypted config payload")?;
|
||||
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
Argon2::default()
|
||||
.hash_password_into(password.as_bytes(), &salt, &mut key)
|
||||
.map_err(|err| anyhow::anyhow!("derive encryption key: {err}"))?;
|
||||
|
||||
|
||||
let plaintext = XChaCha20Poly1305::new((&key).into())
|
||||
.decrypt(XNonce::from_slice(&nonce), ciphertext.as_ref())
|
||||
.map_err(|_| anyhow::anyhow!("cannot decrypt config; hardware UUID mismatch or corrupted data"))?;
|
||||
|
||||
.map_err(|_| {
|
||||
anyhow::anyhow!("cannot decrypt config; hardware UUID mismatch or corrupted data")
|
||||
})?;
|
||||
|
||||
serde_json::from_slice(&plaintext).context("parse decrypted config")
|
||||
}
|
||||
|
||||
@@ -1138,17 +1147,16 @@ mod tests {
|
||||
let config = ConfigFile::default();
|
||||
let password = "test-password-123";
|
||||
let encrypted = encrypt_config(&config, password).unwrap();
|
||||
|
||||
|
||||
// Ensure it doesn't contain plain text fields of default config
|
||||
let encrypted_str = String::from_utf8_lossy(&encrypted);
|
||||
assert!(!encrypted_str.contains("Maple Mono NF CN"));
|
||||
assert!(encrypted_str.contains("argon2id"));
|
||||
|
||||
|
||||
let decrypted = decrypt_config(&encrypted, password).unwrap();
|
||||
assert_eq!(decrypted.terminal_font_family, config.terminal_font_family);
|
||||
|
||||
|
||||
// Decrypt with wrong password should fail
|
||||
assert!(decrypt_config(&encrypted, "wrong-password").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -102,9 +102,7 @@ impl Ashell {
|
||||
|
||||
let mut session = match self.ssh_auth_method {
|
||||
AuthMethod::Password => Session::password(host, port, user, password),
|
||||
AuthMethod::Key => {
|
||||
Session::key(host, port, user, key_path, key_inline, passphrase)
|
||||
}
|
||||
AuthMethod::Key => Session::key(host, port, user, key_path, key_inline, passphrase),
|
||||
AuthMethod::Config => {
|
||||
// Force key_inline to empty — config mode never uses inline key content.
|
||||
// The backend will try default keys from ~/.ssh/ if no explicit key path is set.
|
||||
@@ -785,7 +783,11 @@ impl Ashell {
|
||||
}
|
||||
|
||||
pub(crate) fn handle_tab_close(&mut self, id: String) {
|
||||
if self.connection_progress.as_ref().map_or(false, |p| p.tab_id == id) {
|
||||
if self
|
||||
.connection_progress
|
||||
.as_ref()
|
||||
.map_or(false, |p| p.tab_id == id)
|
||||
{
|
||||
self.connection_progress = None;
|
||||
}
|
||||
let group_ix = self
|
||||
|
||||
@@ -177,4 +177,3 @@ mod tests {
|
||||
assert_eq!(entries[1].hostname, "5.6.7.8");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -886,8 +886,7 @@ async fn connect_and_authenticate(
|
||||
let passphrase = session.passphrase.trim();
|
||||
let passphrase = (!passphrase.is_empty()).then_some(passphrase);
|
||||
let success =
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase)
|
||||
.await?;
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase).await?;
|
||||
if !success {
|
||||
return Err(anyhow!(
|
||||
"public key authentication failed for {}@{}:{} - no valid default key found in ~/.ssh/",
|
||||
@@ -940,8 +939,7 @@ async fn connect_and_authenticate(
|
||||
let passphrase = session.passphrase.trim();
|
||||
let passphrase = (!passphrase.is_empty()).then_some(passphrase);
|
||||
let success =
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase)
|
||||
.await?;
|
||||
authenticate_with_default_keys(&mut handle, &session.user, passphrase).await?;
|
||||
if !success {
|
||||
return Err(anyhow!(
|
||||
"ssh-config authentication failed for {}@{}:{} - no valid default key found",
|
||||
|
||||
+32
-17
@@ -375,7 +375,12 @@ impl TerminalElement {
|
||||
fn layout_grid(
|
||||
&self,
|
||||
cx: &App,
|
||||
) -> (Vec<LayoutRect>, Vec<BatchedTextRun>, Vec<LayoutCustomBlock>, Vec<LayoutUnderline>) {
|
||||
) -> (
|
||||
Vec<LayoutRect>,
|
||||
Vec<BatchedTextRun>,
|
||||
Vec<LayoutCustomBlock>,
|
||||
Vec<LayoutUnderline>,
|
||||
) {
|
||||
let view_read = self.view.read(cx);
|
||||
let hovered_url = view_read.hovered_url.clone();
|
||||
|
||||
@@ -439,12 +444,18 @@ impl TerminalElement {
|
||||
// Apply hover underline if mouse is hovering over this URL
|
||||
if let Some(hu) = &hovered_url {
|
||||
if hu.tab_id == self.tab_id
|
||||
&& hu.cells.contains(&(render_cell.row as usize, render_cell.col as usize))
|
||||
&& hu
|
||||
.cells
|
||||
.contains(&(render_cell.row as usize, render_cell.col as usize))
|
||||
{
|
||||
underlines.push(LayoutUnderline {
|
||||
row: render_cell.row,
|
||||
col: render_cell.col,
|
||||
cells: if cell.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 },
|
||||
cells: if cell.flags.contains(Flags::WIDE_CHAR) {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
},
|
||||
color: style.color,
|
||||
});
|
||||
}
|
||||
@@ -515,7 +526,9 @@ impl TerminalElement {
|
||||
let cursor_style = self.view.read(cx).cursor_style;
|
||||
let show_cursor = match cursor_style {
|
||||
CursorStyle::Blink | CursorStyle::BeamBlink => {
|
||||
if let Ok(duration) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
|
||||
if let Ok(duration) =
|
||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)
|
||||
{
|
||||
(duration.as_millis() / 600) % 2 == 0
|
||||
} else {
|
||||
true
|
||||
@@ -595,7 +608,7 @@ impl Element for TerminalElement {
|
||||
let tab_id = self.tab_id.clone();
|
||||
let _ = view.update(cx, |this, cx| {
|
||||
let old_bounds = this.terminal_bounds.insert(tab_id.clone(), bounds);
|
||||
|
||||
|
||||
// Sync PTY size unconditionally on every prepaint layout pass to ensure
|
||||
// absolute synchronization with GPUI layout regardless of intermediate events.
|
||||
let line_height = this.terminal_line_height();
|
||||
@@ -604,11 +617,11 @@ impl Element for TerminalElement {
|
||||
let h = bounds.size.height.as_f32();
|
||||
let cols = (w / cell_width).floor().max(1.0) as u16;
|
||||
let rows = (h / line_height).floor().max(1.0) as u16;
|
||||
|
||||
|
||||
if let Some(tab) = this.tabs.iter_mut().find(|t| t.id == tab_id) {
|
||||
tab.resize(cols, rows);
|
||||
}
|
||||
|
||||
|
||||
if old_bounds != Some(bounds) {
|
||||
cx.notify();
|
||||
}
|
||||
@@ -640,9 +653,14 @@ impl Element for TerminalElement {
|
||||
) {
|
||||
// Compute a vertical offset to center the text grid vertically,
|
||||
// distributing the leftover pixel remainder evenly to the top and bottom.
|
||||
let grid_height = prepaint.metrics.line_height * (prepaint.bounds.size.height.as_f32() / prepaint.metrics.line_height.as_f32()).floor();
|
||||
let grid_height = prepaint.metrics.line_height
|
||||
* (prepaint.bounds.size.height.as_f32() / prepaint.metrics.line_height.as_f32())
|
||||
.floor();
|
||||
let y_offset = ((prepaint.bounds.size.height - grid_height) / 2.0).max(px(0.0));
|
||||
let draw_origin = point(prepaint.bounds.origin.x, prepaint.bounds.origin.y + y_offset);
|
||||
let draw_origin = point(
|
||||
prepaint.bounds.origin.x,
|
||||
prepaint.bounds.origin.y + y_offset,
|
||||
);
|
||||
|
||||
for rect in &prepaint.rects {
|
||||
rect.paint(draw_origin, prepaint.metrics, window);
|
||||
@@ -657,10 +675,10 @@ impl Element for TerminalElement {
|
||||
}
|
||||
|
||||
for block in &prepaint.custom_blocks {
|
||||
let x = draw_origin.x.as_f32()
|
||||
+ block.col as f32 * prepaint.metrics.cell_width.as_f32();
|
||||
let y = draw_origin.y.as_f32()
|
||||
+ block.row as f32 * prepaint.metrics.line_height.as_f32();
|
||||
let x =
|
||||
draw_origin.x.as_f32() + block.col as f32 * prepaint.metrics.cell_width.as_f32();
|
||||
let y =
|
||||
draw_origin.y.as_f32() + block.row as f32 * prepaint.metrics.line_height.as_f32();
|
||||
paint_custom_block(
|
||||
window,
|
||||
block.c,
|
||||
@@ -801,10 +819,7 @@ fn merge_underlines(mut underlines: Vec<LayoutUnderline>) -> Vec<LayoutUnderline
|
||||
|
||||
for u in underlines {
|
||||
if let Some(last) = merged.last_mut() {
|
||||
if last.row == u.row
|
||||
&& last.color == u.color
|
||||
&& last.col + last.cells as i32 == u.col
|
||||
{
|
||||
if last.row == u.row && last.color == u.color && last.col + last.cells as i32 == u.col {
|
||||
last.cells += u.cells;
|
||||
continue;
|
||||
}
|
||||
|
||||
+467
-168
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use gpui::Hsla;
|
||||
use crate::terminal::RenderCell;
|
||||
use gpui::Hsla;
|
||||
use std::collections::HashMap;
|
||||
|
||||
trait HslaExt {
|
||||
fn into_rgba_like(self, r: u8, g: u8, b: u8) -> Self;
|
||||
@@ -15,10 +15,19 @@ impl HslaExt for Hsla {
|
||||
let min = rf.min(gf).min(bf);
|
||||
let l = (max + min) / 2.0;
|
||||
if max == min {
|
||||
return Hsla { h: 0.0, s: 0.0, l, a: 1.0 };
|
||||
return Hsla {
|
||||
h: 0.0,
|
||||
s: 0.0,
|
||||
l,
|
||||
a: 1.0,
|
||||
};
|
||||
}
|
||||
let d = max - min;
|
||||
let s = if l > 0.5 { d / (2.0 - max - min) } else { d / (max + min) };
|
||||
let s = if l > 0.5 {
|
||||
d / (2.0 - max - min)
|
||||
} else {
|
||||
d / (max + min)
|
||||
};
|
||||
let h = if max == rf {
|
||||
((gf - bf) / d + if gf < bf { 6.0 } else { 0.0 }) / 6.0
|
||||
} else if max == gf {
|
||||
@@ -33,58 +42,58 @@ impl HslaExt for Hsla {
|
||||
#[derive(Debug, Clone)]
|
||||
struct HighlightColors {
|
||||
// Log levels
|
||||
error: Hsla, // ERROR, ERR
|
||||
critical: Hsla, // PANIC, FATAL, EMERGENCY, CRITICAL
|
||||
warning: Hsla, // WARNING, WARN
|
||||
info: Hsla, // INFO, NOTICE
|
||||
debug: Hsla, // DEBUG, TRACE, DBG
|
||||
alert: Hsla, // ALERT
|
||||
error: Hsla, // ERROR, ERR
|
||||
critical: Hsla, // PANIC, FATAL, EMERGENCY, CRITICAL
|
||||
warning: Hsla, // WARNING, WARN
|
||||
info: Hsla, // INFO, NOTICE
|
||||
debug: Hsla, // DEBUG, TRACE, DBG
|
||||
alert: Hsla, // ALERT
|
||||
|
||||
// Status indicators
|
||||
success: Hsla, // SUCCESS, OK, PASS, DONE, COMPLETED
|
||||
failure: Hsla, // FAILED, FAIL, FAILURE
|
||||
pending: Hsla, // PENDING, WAITING, PROCESSING
|
||||
running: Hsla, // RUNNING, ACTIVE, EXECUTING
|
||||
stopped: Hsla, // STOPPED, INACTIVE, HALTED, IDLE
|
||||
skipped: Hsla, // SKIPPED, SKIP
|
||||
success: Hsla, // SUCCESS, OK, PASS, DONE, COMPLETED
|
||||
failure: Hsla, // FAILED, FAIL, FAILURE
|
||||
pending: Hsla, // PENDING, WAITING, PROCESSING
|
||||
running: Hsla, // RUNNING, ACTIVE, EXECUTING
|
||||
stopped: Hsla, // STOPPED, INACTIVE, HALTED, IDLE
|
||||
skipped: Hsla, // SKIPPED, SKIP
|
||||
|
||||
// Network
|
||||
network_up: Hsla, // UP, ONLINE, CONNECTED
|
||||
network_down: Hsla, // DOWN, OFFLINE, UNREACHABLE
|
||||
timeout: Hsla, // TIMEOUT, TIMED OUT
|
||||
refused: Hsla, // REFUSED, REJECTED, DENIED
|
||||
network_up: Hsla, // UP, ONLINE, CONNECTED
|
||||
network_down: Hsla, // DOWN, OFFLINE, UNREACHABLE
|
||||
timeout: Hsla, // TIMEOUT, TIMED OUT
|
||||
refused: Hsla, // REFUSED, REJECTED, DENIED
|
||||
|
||||
// Security & Auth
|
||||
security: Hsla, // SSH, SSL, TLS, CERTIFICATE
|
||||
auth: Hsla, // AUTHENTICATED, AUTHORIZED, LOGIN
|
||||
danger: Hsla, // ROOT, SUDO, PASSWORD, SECRET
|
||||
security: Hsla, // SSH, SSL, TLS, CERTIFICATE
|
||||
auth: Hsla, // AUTHENTICATED, AUTHORIZED, LOGIN
|
||||
danger: Hsla, // ROOT, SUDO, PASSWORD, SECRET
|
||||
|
||||
// Operations
|
||||
started: Hsla, // START, BOOT, STARTING
|
||||
stopped_op: Hsla, // STOP, SHUTDOWN, STOPPING
|
||||
restart: Hsla, // RESTART, RESTARTING
|
||||
deploy: Hsla, // DEPLOY, DEPLOYED, DEPLOYMENT
|
||||
crashed: Hsla, // CRASH, CRASHED, SIGSEGV
|
||||
started: Hsla, // START, BOOT, STARTING
|
||||
stopped_op: Hsla, // STOP, SHUTDOWN, STOPPING
|
||||
restart: Hsla, // RESTART, RESTARTING
|
||||
deploy: Hsla, // DEPLOY, DEPLOYED, DEPLOYMENT
|
||||
crashed: Hsla, // CRASH, CRASHED, SIGSEGV
|
||||
|
||||
// Resources
|
||||
memory: Hsla, // MEMORY, RAM, SWAP, HEAP
|
||||
cpu: Hsla, // CPU, PROCESSOR, CORE
|
||||
disk: Hsla, // DISK, STORAGE, PARTITION, MOUNT
|
||||
memory: Hsla, // MEMORY, RAM, SWAP, HEAP
|
||||
cpu: Hsla, // CPU, PROCESSOR, CORE
|
||||
disk: Hsla, // DISK, STORAGE, PARTITION, MOUNT
|
||||
|
||||
// HTTP codes
|
||||
http_2xx: Hsla, // 200-299 Success
|
||||
http_3xx: Hsla, // 300-399 Redirect
|
||||
http_4xx: Hsla, // 400-499 Client Error
|
||||
http_5xx: Hsla, // 500-599 Server Error
|
||||
http_2xx: Hsla, // 200-299 Success
|
||||
http_3xx: Hsla, // 300-399 Redirect
|
||||
http_4xx: Hsla, // 400-499 Client Error
|
||||
http_5xx: Hsla, // 500-599 Server Error
|
||||
|
||||
// Dev / Exceptions
|
||||
exception: Hsla, // Exception, Traceback, Error type
|
||||
deprecated: Hsla, // DEPRECATED, TODO, FIXME
|
||||
exception: Hsla, // Exception, Traceback, Error type
|
||||
deprecated: Hsla, // DEPRECATED, TODO, FIXME
|
||||
|
||||
// Existing
|
||||
network: Hsla, // IP addresses
|
||||
url: Hsla, // http://, https://
|
||||
port: Hsla, // :22, :443, etc.
|
||||
network: Hsla, // IP addresses
|
||||
url: Hsla, // http://, https://
|
||||
port: Hsla, // :22, :443, etc.
|
||||
}
|
||||
|
||||
fn hsla(r: u8, g: u8, b: u8) -> Hsla {
|
||||
@@ -100,58 +109,58 @@ fn hsla(r: u8, g: u8, b: u8) -> Hsla {
|
||||
fn highlight_colors() -> HighlightColors {
|
||||
HighlightColors {
|
||||
// Log levels
|
||||
error: hsla(224, 96, 96), // #E06060 red
|
||||
critical: hsla(255, 50, 50), // #FF3232 bright red
|
||||
warning: hsla(232, 201, 122), // #E8C97A yellow
|
||||
info: hsla(108, 180, 238), // #6CB4EE blue
|
||||
debug: hsla(130, 140, 155), // #828C9B gray
|
||||
alert: hsla(213, 126, 234), // #D57EEA bright magenta
|
||||
error: hsla(224, 96, 96), // #E06060 red
|
||||
critical: hsla(255, 50, 50), // #FF3232 bright red
|
||||
warning: hsla(232, 201, 122), // #E8C97A yellow
|
||||
info: hsla(108, 180, 238), // #6CB4EE blue
|
||||
debug: hsla(130, 140, 155), // #828C9B gray
|
||||
alert: hsla(213, 126, 234), // #D57EEA bright magenta
|
||||
|
||||
// Status
|
||||
success: hsla(126, 198, 153), // #7EC699 green
|
||||
failure: hsla(232, 168, 124), // #E8A87C orange
|
||||
pending: hsla(232, 201, 122), // #E8C97A yellow
|
||||
running: hsla( 86, 206, 234), // #56CEEA cyan
|
||||
stopped: hsla(160, 165, 175), // #A0A5AF gray
|
||||
skipped: hsla(199, 146, 234), // #C792EA purple
|
||||
success: hsla(126, 198, 153), // #7EC699 green
|
||||
failure: hsla(232, 168, 124), // #E8A87C orange
|
||||
pending: hsla(232, 201, 122), // #E8C97A yellow
|
||||
running: hsla(86, 206, 234), // #56CEEA cyan
|
||||
stopped: hsla(160, 165, 175), // #A0A5AF gray
|
||||
skipped: hsla(199, 146, 234), // #C792EA purple
|
||||
|
||||
// Network
|
||||
network_up: hsla(126, 198, 153), // #7EC699 green
|
||||
network_down: hsla(224, 96, 96), // #E06060 red
|
||||
timeout: hsla(213, 126, 234), // #D57EEA magenta
|
||||
refused: hsla(245, 160, 80), // #F5A050 orange
|
||||
network_up: hsla(126, 198, 153), // #7EC699 green
|
||||
network_down: hsla(224, 96, 96), // #E06060 red
|
||||
timeout: hsla(213, 126, 234), // #D57EEA magenta
|
||||
refused: hsla(245, 160, 80), // #F5A050 orange
|
||||
|
||||
// Security
|
||||
security: hsla( 86, 206, 234), // #56CEEA cyan
|
||||
auth: hsla(126, 198, 153), // #7EC699 green
|
||||
danger: hsla(180, 50, 50), // #B43232 dark red
|
||||
security: hsla(86, 206, 234), // #56CEEA cyan
|
||||
auth: hsla(126, 198, 153), // #7EC699 green
|
||||
danger: hsla(180, 50, 50), // #B43232 dark red
|
||||
|
||||
// Operations
|
||||
started: hsla(126, 198, 153), // #7EC699 green
|
||||
stopped_op: hsla(224, 96, 96), // #E06060 red
|
||||
restart: hsla(232, 201, 122), // #E8C97A yellow
|
||||
deploy: hsla(100, 210, 140), // #64D28C bright green
|
||||
crashed: hsla(255, 50, 50), // #FF3232 bright red
|
||||
started: hsla(126, 198, 153), // #7EC699 green
|
||||
stopped_op: hsla(224, 96, 96), // #E06060 red
|
||||
restart: hsla(232, 201, 122), // #E8C97A yellow
|
||||
deploy: hsla(100, 210, 140), // #64D28C bright green
|
||||
crashed: hsla(255, 50, 50), // #FF3232 bright red
|
||||
|
||||
// Resources
|
||||
memory: hsla(199, 146, 234), // #C792EA purple
|
||||
cpu: hsla( 86, 206, 234), // #56CEEA cyan
|
||||
disk: hsla(108, 180, 238), // #6CB4EE blue
|
||||
memory: hsla(199, 146, 234), // #C792EA purple
|
||||
cpu: hsla(86, 206, 234), // #56CEEA cyan
|
||||
disk: hsla(108, 180, 238), // #6CB4EE blue
|
||||
|
||||
// HTTP codes
|
||||
http_2xx: hsla(126, 198, 153), // #7EC699 green
|
||||
http_3xx: hsla( 86, 206, 234), // #56CEEA cyan
|
||||
http_4xx: hsla(232, 201, 122), // #E8C97A yellow
|
||||
http_5xx: hsla(224, 96, 96), // #E06060 red
|
||||
http_2xx: hsla(126, 198, 153), // #7EC699 green
|
||||
http_3xx: hsla(86, 206, 234), // #56CEEA cyan
|
||||
http_4xx: hsla(232, 201, 122), // #E8C97A yellow
|
||||
http_5xx: hsla(224, 96, 96), // #E06060 red
|
||||
|
||||
// Dev
|
||||
exception: hsla(224, 96, 96), // #E06060 red
|
||||
deprecated: hsla(245, 160, 80), // #F5A050 orange
|
||||
exception: hsla(224, 96, 96), // #E06060 red
|
||||
deprecated: hsla(245, 160, 80), // #F5A050 orange
|
||||
|
||||
// Existing
|
||||
network: hsla(199, 146, 234), // #C792EA purple
|
||||
url: hsla( 86, 212, 199), // #56D4C7 teal
|
||||
port: hsla(130, 170, 200), // #82AAC8 muted teal
|
||||
network: hsla(199, 146, 234), // #C792EA purple
|
||||
url: hsla(86, 212, 199), // #56D4C7 teal
|
||||
port: hsla(130, 170, 200), // #82AAC8 muted teal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,18 +211,40 @@ fn highlight_http_codes(
|
||||
// Specific HTTP codes to highlight
|
||||
const HTTP_CODES: &[(u16, bool)] = &[
|
||||
// 2xx
|
||||
(200, true), (201, true), (202, true), (204, true), (206, true),
|
||||
(200, true),
|
||||
(201, true),
|
||||
(202, true),
|
||||
(204, true),
|
||||
(206, true),
|
||||
// 3xx
|
||||
(301, true), (302, true), (304, true), (307, true), (308, true),
|
||||
(301, true),
|
||||
(302, true),
|
||||
(304, true),
|
||||
(307, true),
|
||||
(308, true),
|
||||
// 4xx
|
||||
(400, true), (401, true), (403, true), (404, true), (405, true),
|
||||
(408, true), (409, true), (410, true), (422, true), (429, true),
|
||||
(400, true),
|
||||
(401, true),
|
||||
(403, true),
|
||||
(404, true),
|
||||
(405, true),
|
||||
(408, true),
|
||||
(409, true),
|
||||
(410, true),
|
||||
(422, true),
|
||||
(429, true),
|
||||
// 5xx
|
||||
(500, true), (502, true), (503, true), (504, true),
|
||||
(500, true),
|
||||
(502, true),
|
||||
(503, true),
|
||||
(504, true),
|
||||
];
|
||||
|
||||
for i in 0..len.saturating_sub(2) {
|
||||
if !bytes[i].is_ascii_digit() || !bytes[i + 1].is_ascii_digit() || !bytes[i + 2].is_ascii_digit() {
|
||||
if !bytes[i].is_ascii_digit()
|
||||
|| !bytes[i + 1].is_ascii_digit()
|
||||
|| !bytes[i + 2].is_ascii_digit()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -249,10 +280,7 @@ fn highlight_http_codes(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight_cells(
|
||||
cells: &[RenderCell],
|
||||
rows: usize,
|
||||
) -> HashMap<(i32, i32), Hsla> {
|
||||
pub fn highlight_cells(cells: &[RenderCell], rows: usize) -> HashMap<(i32, i32), Hsla> {
|
||||
let colors = highlight_colors();
|
||||
|
||||
let mut row_chars: Vec<Vec<(i32, char)>> = vec![Vec::with_capacity(128); rows];
|
||||
@@ -289,178 +317,458 @@ pub fn highlight_cells(
|
||||
let text = chars_buf.as_str();
|
||||
|
||||
// ── 1. Critical errors (highest priority) ──────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["PANIC", "EMERGENCY", "FATAL", "SEGFAULT", "CRITICAL",
|
||||
"OOM", "OUT OF MEMORY", "KERNEL PANIC", "CORE DUMPED", "BUS ERROR"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"PANIC",
|
||||
"EMERGENCY",
|
||||
"FATAL",
|
||||
"SEGFAULT",
|
||||
"CRITICAL",
|
||||
"OOM",
|
||||
"OUT OF MEMORY",
|
||||
"KERNEL PANIC",
|
||||
"CORE DUMPED",
|
||||
"BUS ERROR",
|
||||
],
|
||||
colors.critical,
|
||||
);
|
||||
|
||||
// ── 2. Error keywords ──────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["ERROR", "ERR"], colors.error);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["ERROR", "ERR"],
|
||||
colors.error,
|
||||
);
|
||||
|
||||
// ── 3. Alert ───────────────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["ALERT"], colors.alert);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["ALERT"],
|
||||
colors.alert,
|
||||
);
|
||||
|
||||
// ── 4. Warning keywords ────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["WARNING", "WARN"], colors.warning);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["WARNING", "WARN"],
|
||||
colors.warning,
|
||||
);
|
||||
|
||||
// ── 5. Info keywords ───────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["INFO", "INFORMATION", "NOTICE"], colors.info);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["INFO", "INFORMATION", "NOTICE"],
|
||||
colors.info,
|
||||
);
|
||||
|
||||
// ── 6. Debug keywords ──────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["DEBUG", "DBG", "TRACE"], colors.debug);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["DEBUG", "DBG", "TRACE"],
|
||||
colors.debug,
|
||||
);
|
||||
|
||||
// ── 7. Success status ──────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["SUCCESS", "SUCCEEDED", "SUCCESSFUL", "PASSED", "PASS",
|
||||
"OK", "DONE", "COMPLETED", "FINISHED", "COMPLETE"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"SUCCESS",
|
||||
"SUCCEEDED",
|
||||
"SUCCESSFUL",
|
||||
"PASSED",
|
||||
"PASS",
|
||||
"OK",
|
||||
"DONE",
|
||||
"COMPLETED",
|
||||
"FINISHED",
|
||||
"COMPLETE",
|
||||
],
|
||||
colors.success,
|
||||
);
|
||||
|
||||
// ── 8. Failure status ──────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["FAILED", "FAILURE", "FAIL", "NOT OK"],
|
||||
colors.failure,
|
||||
);
|
||||
|
||||
// ── 9. Pending / Waiting ───────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["PENDING", "WAITING", "PROCESSING", "IN PROGRESS", "QUEUED"],
|
||||
colors.pending,
|
||||
);
|
||||
|
||||
// ── 10. Running / Active ───────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["RUNNING", "ACTIVE", "EXECUTING", "IN_PROGRESS", "LIVE"],
|
||||
colors.running,
|
||||
);
|
||||
|
||||
// ── 11. Stopped / Inactive ─────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["STOPPED", "INACTIVE", "HALTED", "IDLE", "PAUSED"],
|
||||
colors.stopped,
|
||||
);
|
||||
|
||||
// ── 12. Skipped ────────────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["SKIPPED", "SKIP", "SKIPPING"], colors.skipped);
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["SKIPPED", "SKIP", "SKIPPING"],
|
||||
colors.skipped,
|
||||
);
|
||||
|
||||
// ── 13. Network UP ─────────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["UP", "ONLINE", "CONNECTED", "REACHABLE", "LISTENING",
|
||||
"ESTABLISHED", "LINK UP"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"UP",
|
||||
"ONLINE",
|
||||
"CONNECTED",
|
||||
"REACHABLE",
|
||||
"LISTENING",
|
||||
"ESTABLISHED",
|
||||
"LINK UP",
|
||||
],
|
||||
colors.network_up,
|
||||
);
|
||||
|
||||
// ── 14. Network DOWN ───────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["DOWN", "OFFLINE", "UNREACHABLE", "DISCONNECTED",
|
||||
"NOT LISTENING", "LINK DOWN", "NO CARRIER"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"DOWN",
|
||||
"OFFLINE",
|
||||
"UNREACHABLE",
|
||||
"DISCONNECTED",
|
||||
"NOT LISTENING",
|
||||
"LINK DOWN",
|
||||
"NO CARRIER",
|
||||
],
|
||||
colors.network_down,
|
||||
);
|
||||
|
||||
// ── 15. Timeout ────────────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["TIMEOUT", "TIMED OUT", "TIMEOUTS", "ETIMEDOUT", "SLOW", "LATENCY"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"TIMEOUT",
|
||||
"TIMED OUT",
|
||||
"TIMEOUTS",
|
||||
"ETIMEDOUT",
|
||||
"SLOW",
|
||||
"LATENCY",
|
||||
],
|
||||
colors.timeout,
|
||||
);
|
||||
|
||||
// ── 16. Refused / Denied ───────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["REFUSED", "REJECTED", "DENIED", "PERMISSION DENIED",
|
||||
"ACCESS DENIED", "FORBIDDEN", "BLOCKED", "DROP"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"REFUSED",
|
||||
"REJECTED",
|
||||
"DENIED",
|
||||
"PERMISSION DENIED",
|
||||
"ACCESS DENIED",
|
||||
"FORBIDDEN",
|
||||
"BLOCKED",
|
||||
"DROP",
|
||||
],
|
||||
colors.refused,
|
||||
);
|
||||
|
||||
// ── 17. Security / Protocol ────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["SSH", "SSHD", "SSL", "TLS", "HTTPS", "CERTIFICATE", "CERT",
|
||||
"FIREWALL", "IPTABLES", "ACL", "WAF"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"SSH",
|
||||
"SSHD",
|
||||
"SSL",
|
||||
"TLS",
|
||||
"HTTPS",
|
||||
"CERTIFICATE",
|
||||
"CERT",
|
||||
"FIREWALL",
|
||||
"IPTABLES",
|
||||
"ACL",
|
||||
"WAF",
|
||||
],
|
||||
colors.security,
|
||||
);
|
||||
|
||||
// ── 18. Authentication ─────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["AUTHENTICATED", "ACCEPTED", "AUTHORIZED", "LOGIN", "LOGOUT",
|
||||
"LOGGED IN", "LOGGED OUT", "SESSION"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"AUTHENTICATED",
|
||||
"ACCEPTED",
|
||||
"AUTHORIZED",
|
||||
"LOGIN",
|
||||
"LOGOUT",
|
||||
"LOGGED IN",
|
||||
"LOGGED OUT",
|
||||
"SESSION",
|
||||
],
|
||||
colors.auth,
|
||||
);
|
||||
|
||||
// ── 19. Danger (root/sudo/secrets) ─────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["ROOT", "SUDO", "UID=0", "PASSWORD", "SECRET", "TOKEN",
|
||||
"API_KEY", "APIKEY", "PRIVATE KEY", "CREDENTIALS"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"ROOT",
|
||||
"SUDO",
|
||||
"UID=0",
|
||||
"PASSWORD",
|
||||
"SECRET",
|
||||
"TOKEN",
|
||||
"API_KEY",
|
||||
"APIKEY",
|
||||
"PRIVATE KEY",
|
||||
"CREDENTIALS",
|
||||
],
|
||||
colors.danger,
|
||||
);
|
||||
|
||||
// ── 20. Operations: Start ──────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["STARTED", "START", "STARTING", "BOOT", "BOOTING", "LAUNCHED", "LAUNCH"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"STARTED", "START", "STARTING", "BOOT", "BOOTING", "LAUNCHED", "LAUNCH",
|
||||
],
|
||||
colors.started,
|
||||
);
|
||||
|
||||
// ── 21. Operations: Stop ───────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["STOPPED", "STOP", "STOPPING", "SHUTDOWN", "SHUTTING DOWN", "TERMINATED"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"STOPPED",
|
||||
"STOP",
|
||||
"STOPPING",
|
||||
"SHUTDOWN",
|
||||
"SHUTTING DOWN",
|
||||
"TERMINATED",
|
||||
],
|
||||
colors.stopped_op,
|
||||
);
|
||||
|
||||
// ── 22. Operations: Restart ────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["RESTARTED", "RESTART", "RESTARTING", "RELOAD", "RELOADED", "RELOADING"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"RESTARTED",
|
||||
"RESTART",
|
||||
"RESTARTING",
|
||||
"RELOAD",
|
||||
"RELOADED",
|
||||
"RELOADING",
|
||||
],
|
||||
colors.restart,
|
||||
);
|
||||
|
||||
// ── 23. Operations: Deploy ─────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["DEPLOYED", "DEPLOYMENT", "DEPLOYING", "DEPLOY",
|
||||
"ROLLBACK", "ROLLED BACK", "ROLLING BACK", "UPGRADE", "UPGRADED"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"DEPLOYED",
|
||||
"DEPLOYMENT",
|
||||
"DEPLOYING",
|
||||
"DEPLOY",
|
||||
"ROLLBACK",
|
||||
"ROLLED BACK",
|
||||
"ROLLING BACK",
|
||||
"UPGRADE",
|
||||
"UPGRADED",
|
||||
],
|
||||
colors.deploy,
|
||||
);
|
||||
|
||||
// ── 24. Operations: Crash ──────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["CRASH", "CRASHED", "CRASHING", "SIGSEGV", "SIGABRT", "SIGKILL",
|
||||
"DIED", "EXITED", "EXIT CODE", "CORE DUMP"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"CRASH",
|
||||
"CRASHED",
|
||||
"CRASHING",
|
||||
"SIGSEGV",
|
||||
"SIGABRT",
|
||||
"SIGKILL",
|
||||
"DIED",
|
||||
"EXITED",
|
||||
"EXIT CODE",
|
||||
"CORE DUMP",
|
||||
],
|
||||
colors.crashed,
|
||||
);
|
||||
|
||||
// ── 25. Resources: Memory ──────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&["MEMORY", "RAM", "HEAP", "STACK", "SWAP", "MEM"],
|
||||
colors.memory,
|
||||
);
|
||||
|
||||
// ── 26. Resources: CPU ─────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["CPU", "PROCESSOR", "CORE", "CORES", "THREAD", "THREADS", "LOAD"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"CPU",
|
||||
"PROCESSOR",
|
||||
"CORE",
|
||||
"CORES",
|
||||
"THREAD",
|
||||
"THREADS",
|
||||
"LOAD",
|
||||
],
|
||||
colors.cpu,
|
||||
);
|
||||
|
||||
// ── 27. Resources: Disk ────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["DISK", "STORAGE", "PARTITION", "MOUNT", "FILESYSTEM",
|
||||
"INODE", "IOPS", "READ", "WRITE"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"DISK",
|
||||
"STORAGE",
|
||||
"PARTITION",
|
||||
"MOUNT",
|
||||
"FILESYSTEM",
|
||||
"INODE",
|
||||
"IOPS",
|
||||
"READ",
|
||||
"WRITE",
|
||||
],
|
||||
colors.disk,
|
||||
);
|
||||
|
||||
// ── 28. Dev: Exceptions ────────────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["EXCEPTION", "TRACEBACK", "THROW", "THROWN", "STACKTRACE",
|
||||
"TYPEERROR", "VALUEERROR", "KEYERROR", "ATTRIBUTEERROR",
|
||||
"INDEXERROR", "RUNTIMEERROR", "IOERROR", "OSERROR",
|
||||
"NULLPOINTER", "NPE", "SEGFAULT"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"EXCEPTION",
|
||||
"TRACEBACK",
|
||||
"THROW",
|
||||
"THROWN",
|
||||
"STACKTRACE",
|
||||
"TYPEERROR",
|
||||
"VALUEERROR",
|
||||
"KEYERROR",
|
||||
"ATTRIBUTEERROR",
|
||||
"INDEXERROR",
|
||||
"RUNTIMEERROR",
|
||||
"IOERROR",
|
||||
"OSERROR",
|
||||
"NULLPOINTER",
|
||||
"NPE",
|
||||
"SEGFAULT",
|
||||
],
|
||||
colors.exception,
|
||||
);
|
||||
|
||||
// ── 29. Dev: Deprecated / TODO ─────────────────────────
|
||||
highlight_keywords(&mut map, text, &byte_to_col, row_i32,
|
||||
&["DEPRECATED", "TODO", "FIXME", "HACK", "XXX", "WORKAROUND", "TEMPORARY"],
|
||||
highlight_keywords(
|
||||
&mut map,
|
||||
text,
|
||||
&byte_to_col,
|
||||
row_i32,
|
||||
&[
|
||||
"DEPRECATED",
|
||||
"TODO",
|
||||
"FIXME",
|
||||
"HACK",
|
||||
"XXX",
|
||||
"WORKAROUND",
|
||||
"TEMPORARY",
|
||||
],
|
||||
colors.deprecated,
|
||||
);
|
||||
|
||||
@@ -477,8 +785,6 @@ pub fn highlight_cells(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── 33. Port numbers ───────────────────────────────────
|
||||
for m in find_ports(text) {
|
||||
let port_len = find_port_len(&text[m..]);
|
||||
@@ -537,11 +843,7 @@ fn find_ip_len(text: &str) -> usize {
|
||||
len += 1;
|
||||
}
|
||||
|
||||
if dots == 3 && digits > 0 {
|
||||
len
|
||||
} else {
|
||||
0
|
||||
}
|
||||
if dots == 3 && digits > 0 { len } else { 0 }
|
||||
}
|
||||
|
||||
fn find_ip_addresses(text: &str) -> Vec<usize> {
|
||||
@@ -550,16 +852,12 @@ fn find_ip_addresses(text: &str) -> Vec<usize> {
|
||||
let len = bytes.len();
|
||||
|
||||
for i in 0..len {
|
||||
if bytes[i].is_ascii_digit()
|
||||
&& (i == 0 || is_boundary(bytes[i - 1] as char))
|
||||
{
|
||||
if bytes[i].is_ascii_digit() && (i == 0 || is_boundary(bytes[i - 1] as char)) {
|
||||
let remaining = &text[i..];
|
||||
let ip_len = find_ip_len(remaining);
|
||||
if ip_len > 0 {
|
||||
let ip_str = &remaining[..ip_len];
|
||||
let valid = ip_str
|
||||
.split('.')
|
||||
.all(|octet| octet.parse::<u8>().is_ok());
|
||||
let valid = ip_str.split('.').all(|octet| octet.parse::<u8>().is_ok());
|
||||
if valid {
|
||||
positions.push(i);
|
||||
}
|
||||
@@ -671,7 +969,9 @@ pub fn build_logical_lines<'a>(cells: &'a [RenderCell], rows: usize) -> Vec<Logi
|
||||
let wraps_from_prev = row_idx > 0 && {
|
||||
current_line.as_ref().map_or(false, |line| {
|
||||
line.row_cells.last().map_or(false, |rc| {
|
||||
rc.cell.flags.contains(alacritty_terminal::term::cell::Flags::WRAPLINE)
|
||||
rc.cell
|
||||
.flags
|
||||
.contains(alacritty_terminal::term::cell::Flags::WRAPLINE)
|
||||
})
|
||||
})
|
||||
};
|
||||
@@ -738,4 +1038,3 @@ pub fn find_url_at_cell(
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@@ -387,7 +387,12 @@ impl Ashell {
|
||||
if let Some((row, col, _side)) = self.terminal_grid_point_and_side(event.position) {
|
||||
if let Some(snapshot) = self.active_snapshot() {
|
||||
if let Some(active_id) = &self.active_tab {
|
||||
if let Some((url, url_cells)) = crate::terminal::highlight::find_url_at_cell(&snapshot.cells, snapshot.rows, row, col) {
|
||||
if let Some((url, url_cells)) = crate::terminal::highlight::find_url_at_cell(
|
||||
&snapshot.cells,
|
||||
snapshot.rows,
|
||||
row,
|
||||
col,
|
||||
) {
|
||||
hovered_url = Some(crate::app::HoveredUrl {
|
||||
url,
|
||||
tab_id: active_id.clone(),
|
||||
@@ -397,7 +402,7 @@ impl Ashell {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if self.hovered_url != hovered_url || self.cmd_ctrl_pressed != cmd_ctrl_pressed {
|
||||
self.hovered_url = hovered_url;
|
||||
self.cmd_ctrl_pressed = cmd_ctrl_pressed;
|
||||
|
||||
+18
-7
@@ -1,7 +1,7 @@
|
||||
pub mod custom_blocks;
|
||||
pub mod element;
|
||||
pub mod input;
|
||||
pub mod highlight;
|
||||
pub mod input;
|
||||
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
@@ -134,7 +134,12 @@ pub struct TerminalTab {
|
||||
pub rows: u16,
|
||||
pub backend: std::sync::Arc<std::sync::Mutex<BackendTx>>,
|
||||
pub scroll_pixel_y: f32,
|
||||
pub(crate) highlight_cache: std::cell::RefCell<Option<(Vec<RenderCell>, std::collections::HashMap<(i32, i32), gpui::Hsla>)>>,
|
||||
pub(crate) highlight_cache: std::cell::RefCell<
|
||||
Option<(
|
||||
Vec<RenderCell>,
|
||||
std::collections::HashMap<(i32, i32), gpui::Hsla>,
|
||||
)>,
|
||||
>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -348,9 +353,9 @@ impl TerminalTab {
|
||||
|
||||
let highlights = if is_enabled {
|
||||
let mut cache = self.highlight_cache.borrow_mut();
|
||||
let cache_valid = cache.as_ref().is_some_and(|(cached_cells, _)| {
|
||||
cached_cells == &cells
|
||||
});
|
||||
let cache_valid = cache
|
||||
.as_ref()
|
||||
.is_some_and(|(cached_cells, _)| cached_cells == &cells);
|
||||
if cache_valid {
|
||||
cache.as_ref().unwrap().1.clone()
|
||||
} else {
|
||||
@@ -503,7 +508,10 @@ fn viewport_selection_from_range(
|
||||
} = selection.as_ref().copied()?;
|
||||
|
||||
let top_point = viewport_to_point(display_offset, Point::new(0, Column(0)));
|
||||
let bottom_point = viewport_to_point(display_offset, Point::new(rows.saturating_sub(1), Column(0)));
|
||||
let bottom_point = viewport_to_point(
|
||||
display_offset,
|
||||
Point::new(rows.saturating_sub(1), Column(0)),
|
||||
);
|
||||
|
||||
let top_line = top_point.line;
|
||||
let bottom_line = bottom_point.line;
|
||||
@@ -521,7 +529,10 @@ fn viewport_selection_from_range(
|
||||
} else if end.line > bottom_line {
|
||||
Point::new(rows.saturating_sub(1), Column(cols.saturating_sub(1)))
|
||||
} else {
|
||||
point_to_viewport(display_offset, end).unwrap_or(Point::new(rows.saturating_sub(1), Column(cols.saturating_sub(1))))
|
||||
point_to_viewport(display_offset, end).unwrap_or(Point::new(
|
||||
rows.saturating_sub(1),
|
||||
Column(cols.saturating_sub(1)),
|
||||
))
|
||||
};
|
||||
|
||||
Some(ViewportSelection {
|
||||
|
||||
Reference in New Issue
Block a user