Add task manager internal page
This commit is contained in:
@@ -18,6 +18,7 @@ actions!(
|
||||
OpenHistory,
|
||||
OpenNewTab,
|
||||
OpenSettings,
|
||||
OpenTaskManager,
|
||||
Quit,
|
||||
RestoreClosedTab,
|
||||
SelectNextTab,
|
||||
@@ -38,6 +39,8 @@ fn main() {
|
||||
KeyBinding::new("ctrl-shift-j", OpenDownloads, None),
|
||||
KeyBinding::new("cmd-y", OpenHistory, None),
|
||||
KeyBinding::new("ctrl-h", OpenHistory, None),
|
||||
KeyBinding::new("cmd-escape", OpenTaskManager, None),
|
||||
KeyBinding::new("shift-escape", OpenTaskManager, None),
|
||||
KeyBinding::new("cmd-,", OpenSettings, None),
|
||||
KeyBinding::new("ctrl-,", OpenSettings, None),
|
||||
KeyBinding::new("cmd-l", FocusAddressBar, None),
|
||||
@@ -78,6 +81,7 @@ fn main() {
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Open Downloads", OpenDownloads),
|
||||
MenuItem::action("Open History", OpenHistory),
|
||||
MenuItem::action("Open Task Manager", OpenTaskManager),
|
||||
MenuItem::action("Open Settings", OpenSettings),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Toggle Pin", TogglePinnedTab),
|
||||
|
||||
@@ -7,6 +7,7 @@ mod plugins;
|
||||
mod profiles;
|
||||
mod reading_list;
|
||||
mod sync;
|
||||
mod task_manager;
|
||||
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::{colors, spacing};
|
||||
@@ -32,6 +33,7 @@ impl ElyShell {
|
||||
"ely://downloads" => self.render_downloads_page(snapshot, cx),
|
||||
"ely://history" => self.render_history_page(snapshot, cx),
|
||||
"ely://archive" => self.render_archive_page(snapshot, cx),
|
||||
"ely://task-manager" => self.render_task_manager_page(snapshot),
|
||||
"ely://about" => self.render_about_page(snapshot),
|
||||
"ely://settings/plugins" => self.render_plugins_page(snapshot, cx),
|
||||
"ely://settings/profiles" => self.render_profiles_page(snapshot, cx),
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
use ely_browser_core::{BrowserSnapshot, InstalledPlugin};
|
||||
use ely_design_system::colors;
|
||||
use ely_domain::{BrowserTab, DownloadEntry, DownloadState, TabId, TabState};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
AnyElement, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div, px, rgb,
|
||||
};
|
||||
use gpui_component::{IconName, StyledExt, scroll::ScrollableElement};
|
||||
|
||||
use super::{
|
||||
ElyShell,
|
||||
download_labels::{download_size_label, download_state_label},
|
||||
render_canvas_surface,
|
||||
};
|
||||
|
||||
impl ElyShell {
|
||||
pub(super) fn render_task_manager_page(&mut self, snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
render_canvas_surface(
|
||||
div()
|
||||
.size_full()
|
||||
.p_8()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_5()
|
||||
.child(render_task_manager_header(snapshot))
|
||||
.child(render_task_summary(snapshot))
|
||||
.child(render_task_table(snapshot)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_task_manager_header(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.items_end()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("Task Manager"))
|
||||
.child(div().text_sm().text_color(rgb(colors::MUTED)).child(format!(
|
||||
"{} / {}",
|
||||
snapshot.active_profile_name, snapshot.active_space_name
|
||||
))),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(IconName::LayoutDashboard)
|
||||
.child(format!("{} local tasks", task_count(snapshot))),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_task_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.border_t_1()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.py_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.children([
|
||||
task_metric("Tabs", snapshot.tabs.len()),
|
||||
task_metric("Downloads", snapshot.download_entries.len()),
|
||||
task_metric("Plugins", snapshot.installed_plugins.len()),
|
||||
task_metric("Spaces", snapshot.spaces.len()),
|
||||
])
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn task_metric(label: &'static str, value: usize) -> AnyElement {
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(div().text_xs().text_color(rgb(colors::MUTED)).child(label))
|
||||
.child(
|
||||
div().text_sm().font_semibold().text_color(rgb(colors::INK)).child(value.to_string()),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_task_table(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_y_scrollbar()
|
||||
.child(render_section_header("Tabs"))
|
||||
.children(
|
||||
snapshot
|
||||
.tabs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, tab)| render_tab_task_row(index, tab, &snapshot.active_tab_id)),
|
||||
)
|
||||
.when(!snapshot.download_entries.is_empty(), |this| {
|
||||
this.child(render_section_header("Downloads")).children(
|
||||
snapshot
|
||||
.download_entries
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.map(|(index, entry)| render_download_task_row(index, entry)),
|
||||
)
|
||||
})
|
||||
.when(!snapshot.installed_plugins.is_empty(), |this| {
|
||||
this.child(render_section_header("Plugins")).children(
|
||||
snapshot
|
||||
.installed_plugins
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, plugin)| render_plugin_task_row(index, plugin)),
|
||||
)
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_section_header(label: &'static str) -> AnyElement {
|
||||
div()
|
||||
.pt_4()
|
||||
.pb_2()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(label)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_tab_task_row(index: usize, tab: &BrowserTab, active_tab_id: &TabId) -> AnyElement {
|
||||
let status = tab_state_label(tab.state());
|
||||
let status_color = tab_state_color(tab.state());
|
||||
let scope = if tab.id() == active_tab_id { "Active tab" } else { "Tab" };
|
||||
let icon = if tab.id() == active_tab_id { IconName::CircleCheck } else { IconName::Globe };
|
||||
|
||||
render_task_row(
|
||||
format!("tab-task-{index}"),
|
||||
icon,
|
||||
tab.title().to_string(),
|
||||
tab.display_url(),
|
||||
scope,
|
||||
status,
|
||||
status_color,
|
||||
)
|
||||
}
|
||||
|
||||
fn render_download_task_row(index: usize, entry: &DownloadEntry) -> AnyElement {
|
||||
render_task_row(
|
||||
format!("download-task-{index}"),
|
||||
IconName::File,
|
||||
entry.file_name().to_string(),
|
||||
download_size_label(entry),
|
||||
"Download",
|
||||
download_state_label(entry.state()),
|
||||
download_state_color(entry.state()),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_plugin_task_row(index: usize, plugin: &InstalledPlugin) -> AnyElement {
|
||||
let status = if plugin.enabled() { "Enabled" } else { "Disabled" };
|
||||
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
|
||||
let detail = format!(
|
||||
"{} permissions - {} contributions",
|
||||
plugin.manifest().permissions().len(),
|
||||
plugin.manifest().contributes().len()
|
||||
);
|
||||
|
||||
render_task_row(
|
||||
format!("plugin-task-{index}"),
|
||||
IconName::Asterisk,
|
||||
plugin.manifest().name().to_string(),
|
||||
detail,
|
||||
"Plugin",
|
||||
status,
|
||||
status_color,
|
||||
)
|
||||
}
|
||||
|
||||
fn render_task_row(
|
||||
id: String,
|
||||
icon: IconName,
|
||||
title: String,
|
||||
detail: String,
|
||||
scope: &'static str,
|
||||
status: &'static str,
|
||||
status_color: u32,
|
||||
) -> AnyElement {
|
||||
div()
|
||||
.id(SharedString::from(id))
|
||||
.py_3()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(div().text_color(rgb(colors::MUTED_SOFT)).child(icon))
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(title),
|
||||
)
|
||||
.child(
|
||||
div().text_xs().truncate().text_color(rgb(colors::MUTED)).child(detail),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_end()
|
||||
.gap_3()
|
||||
.text_xs()
|
||||
.child(div().min_w(px(86.0)).text_color(rgb(colors::MUTED)).child(scope))
|
||||
.child(
|
||||
div()
|
||||
.min_w(px(76.0))
|
||||
.font_semibold()
|
||||
.text_color(rgb(status_color))
|
||||
.child(status),
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn task_count(snapshot: &BrowserSnapshot) -> usize {
|
||||
snapshot.tabs.len() + snapshot.download_entries.len() + snapshot.installed_plugins.len()
|
||||
}
|
||||
|
||||
fn tab_state_label(state: &TabState) -> &'static str {
|
||||
match state {
|
||||
TabState::Loading => "Loading",
|
||||
TabState::Ready => "Ready",
|
||||
TabState::Crashed => "Crashed",
|
||||
TabState::Discarded => "Discarded",
|
||||
TabState::Archived => "Archived",
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_state_color(state: &TabState) -> u32 {
|
||||
match state {
|
||||
TabState::Loading => colors::PRIMARY,
|
||||
TabState::Ready => colors::SUCCESS,
|
||||
TabState::Crashed => colors::ERROR,
|
||||
TabState::Discarded | TabState::Archived => colors::MUTED,
|
||||
}
|
||||
}
|
||||
|
||||
fn download_state_color(state: &DownloadState) -> u32 {
|
||||
match state {
|
||||
DownloadState::InProgress => colors::PRIMARY,
|
||||
DownloadState::Completed => colors::SUCCESS,
|
||||
DownloadState::Paused => colors::MUTED,
|
||||
DownloadState::Cancelled | DownloadState::Failed => colors::ERROR,
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ use plugins::{PendingPluginInstall, PendingPluginUninstall};
|
||||
|
||||
use crate::{
|
||||
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
||||
OpenSettings, RestoreClosedTab, SelectNextTab, SelectPreviousTab, ToggleFavoriteTab,
|
||||
TogglePinnedTab,
|
||||
OpenSettings, OpenTaskManager, RestoreClosedTab, SelectNextTab, SelectPreviousTab,
|
||||
ToggleFavoriteTab, TogglePinnedTab,
|
||||
};
|
||||
|
||||
enum ShellState {
|
||||
@@ -114,6 +114,10 @@ impl ElyShell {
|
||||
self.open_internal_tab("ely://settings", window, cx);
|
||||
}
|
||||
|
||||
fn open_task_manager(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.open_internal_tab("ely://task-manager", window, cx);
|
||||
}
|
||||
|
||||
fn open_internal_tab(&mut self, url_text: &str, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Ok(url) = UrlText::parse(url_text) {
|
||||
self.open_url(url, window, cx);
|
||||
@@ -294,6 +298,15 @@ impl ElyShell {
|
||||
self.open_settings(window, cx);
|
||||
}
|
||||
|
||||
fn on_open_task_manager(
|
||||
&mut self,
|
||||
_: &OpenTaskManager,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.open_task_manager(window, cx);
|
||||
}
|
||||
|
||||
fn on_restore_closed_tab(
|
||||
&mut self,
|
||||
_: &RestoreClosedTab,
|
||||
|
||||
@@ -42,6 +42,7 @@ impl ElyShell {
|
||||
.on_action(cx.listener(Self::on_open_history))
|
||||
.on_action(cx.listener(Self::on_open_new_tab))
|
||||
.on_action(cx.listener(Self::on_open_settings))
|
||||
.on_action(cx.listener(Self::on_open_task_manager))
|
||||
.on_action(cx.listener(Self::on_restore_closed_tab))
|
||||
.on_action(cx.listener(Self::on_select_next_tab))
|
||||
.on_action(cx.listener(Self::on_select_previous_tab))
|
||||
|
||||
Reference in New Issue
Block a user