refactor(desktop): hide connection child state internals

This commit is contained in:
Kang
2026-07-28 11:46:17 +08:00
parent 2d18e78fae
commit 778158b87a
20 changed files with 503 additions and 215 deletions
@@ -39,24 +39,24 @@ impl NyaTermApp {
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.connection_state.import.path_prompt_active() || self.config_path_prompt.is_some() {
if self.connection_state.import_path_prompt_active() || self.config_path_prompt.is_some() {
self.terminal.view.status = "connection import picker is already open".to_string();
cx.notify();
return;
}
self.connection_state.import.open_dialog();
self.connection_state.open_import_dialog();
self.connection_state.list.close_more_menu();
self.title_menu_open = None;
self.title_menu_submenu = None;
self.terminal.view.status = "select a connection import source".to_string();
let import_focus = self.connection_state.import.focus_handle();
let import_focus = self.connection_state.import_focus_handle();
window.focus(&import_focus);
cx.notify();
}
pub(in crate::features) fn close_connection_import_dialog(&mut self, cx: &mut Context<Self>) {
self.connection_state.import.close_dialog();
self.connection_state.close_import_dialog();
cx.notify();
}
@@ -65,7 +65,7 @@ impl NyaTermApp {
source: ConnectionImportSource,
cx: &mut Context<Self>,
) {
self.connection_state.import.close_dialog();
self.connection_state.close_import_dialog();
if source == ConnectionImportSource::NyatermBackup {
self.prompt_portable_snapshot_import(cx);
return;
@@ -78,7 +78,7 @@ impl NyaTermApp {
source: ConnectionImportSource,
cx: &mut Context<Self>,
) {
if self.connection_state.import.path_prompt_active() {
if self.connection_state.import_path_prompt_active() {
self.terminal.view.status = "connection import picker is already open".to_string();
cx.notify();
return;
@@ -92,7 +92,7 @@ impl NyaTermApp {
});
let config_dir = self.runtime.config_dir().to_path_buf();
let portable_key_path = self.runtime.portable_key_path().map(ToOwned::to_owned);
self.connection_state.import.begin_path_prompt(source);
self.connection_state.begin_import_path_prompt(source);
self.terminal.view.status = source.selecting_status().to_string();
cx.spawn(async move |this, cx| {
@@ -132,7 +132,7 @@ impl NyaTermApp {
result: ConnectionImportResult,
cx: &mut Context<Self>,
) {
self.connection_state.import.finish_path_prompt();
self.connection_state.finish_import_path_prompt();
match result {
ConnectionImportResult::Imported(count) => {
self.refresh_store_from_runtime();
@@ -10,7 +10,7 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) {
self.connection_state.list.close_more_menu();
self.connection_state.confirmations.open_clear_all();
self.connection_state.open_clear_all();
self.terminal.view.status = "confirm clearing all saved connections".to_string();
cx.notify();
}
@@ -19,24 +19,24 @@ impl NyaTermApp {
&mut self,
cx: &mut Context<Self>,
) {
self.connection_state.confirmations.close_clear_all();
self.connection_state.close_clear_all();
cx.notify();
}
pub(in crate::features) fn confirm_connections_clear_all(&mut self, cx: &mut Context<Self>) {
if !self.connection_state.confirmations.clear_all_is_open() {
if !self.connection_state.clear_all_is_open() {
return;
}
match self.with_connection_store(|store| store.replace_sessions(&SessionsConfig::default()))
{
Ok(()) => {
self.connection_state.confirmations.close_clear_all();
self.connection_state.close_clear_all();
self.connection_state.list.clear_runtime_state();
self.refresh_store_from_runtime();
self.terminal.view.status = self.tr("savedConnections.clearAllSuccess").to_string();
}
Err(error) => {
self.connection_state.confirmations.close_clear_all();
self.connection_state.close_clear_all();
self.terminal.view.status = format!("clear saved connections failed: {error}");
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = false;
@@ -60,8 +60,7 @@ impl NyaTermApp {
return;
};
self.connection_state
.confirmations
.open_delete(ConnectionDeleteConfirmState {
.open_delete_confirm(ConnectionDeleteConfirmState {
connection_id,
label: connection.name.clone(),
});
@@ -70,12 +69,12 @@ impl NyaTermApp {
}
pub(in crate::features) fn close_connection_delete_confirm(&mut self, cx: &mut Context<Self>) {
self.connection_state.confirmations.close_delete();
self.connection_state.close_delete_confirm();
cx.notify();
}
pub(in crate::features) fn confirm_connection_delete(&mut self, cx: &mut Context<Self>) {
let Some(confirm) = self.connection_state.confirmations.take_delete() else {
let Some(confirm) = self.connection_state.take_delete_confirm() else {
return;
};
match self.with_connection_store(|store| store.delete_connection(&confirm.connection_id)) {
@@ -118,8 +117,7 @@ impl NyaTermApp {
.filter(|child| child.parent_id.as_deref() == Some(group_id.as_str()))
.count();
self.connection_state
.confirmations
.open_group_delete(ConnectionGroupDeleteConfirmState {
.open_group_delete_confirm(ConnectionGroupDeleteConfirmState {
group_id,
label: group.name.clone(),
connection_count,
@@ -133,12 +131,12 @@ impl NyaTermApp {
&mut self,
cx: &mut Context<Self>,
) {
self.connection_state.confirmations.close_group_delete();
self.connection_state.close_group_delete_confirm();
cx.notify();
}
pub(in crate::features) fn confirm_connection_group_delete(&mut self, cx: &mut Context<Self>) {
let Some(confirm) = self.connection_state.confirmations.take_group_delete() else {
let Some(confirm) = self.connection_state.take_group_delete_confirm() else {
return;
};
match self.with_connection_store(|store| store.delete_group(&confirm.group_id)) {
@@ -36,8 +36,7 @@ impl NyaTermApp {
.or(parent_id);
self.connection_state
.group_editor
.begin_edit(ConnectionGroupEditorState {
.begin_group_editor(ConnectionGroupEditorState {
id: group_id,
name,
parent_id,
@@ -52,7 +51,7 @@ impl NyaTermApp {
}
pub(in crate::features) fn close_connection_group_editor(&mut self, cx: &mut Context<Self>) {
self.connection_state.group_editor.close();
self.connection_state.close_group_editor();
self.connection_state.clear_group_editor_field();
self.terminal.view.status = "connection group editor closed".to_string();
cx.notify();
@@ -74,8 +73,7 @@ impl NyaTermApp {
"backspace" if !keystroke.modifiers.platform && !keystroke.modifiers.control => {
if self
.connection_state
.group_editor
.apply_name_key("backspace", None)
.apply_group_editor_name_key("backspace", None)
{
cx.notify();
}
@@ -88,8 +86,7 @@ impl NyaTermApp {
{
if self
.connection_state
.group_editor
.apply_name_key(keystroke.key.as_str(), Some(input))
.apply_group_editor_name_key(keystroke.key.as_str(), Some(input))
{
cx.notify();
}
@@ -100,13 +97,13 @@ impl NyaTermApp {
}
pub(in crate::features) fn save_connection_group_editor(&mut self, cx: &mut Context<Self>) {
let Some(editor) = self.connection_state.group_editor.active_draft() else {
let Some(editor) = self.connection_state.active_group_editor_draft() else {
return;
};
let name = editor.name.trim().to_string();
if name.is_empty() {
let message = self.tr("savedConnections.folderNameRequired").to_string();
self.connection_state.group_editor.set_error(message);
self.connection_state.set_group_editor_error(message);
cx.notify();
return;
}
@@ -132,12 +129,12 @@ impl NyaTermApp {
match self.with_connection_store(|store| store.save_group(&group)) {
Ok(()) => {
self.connection_state.list.expand_group(group.id.clone());
self.connection_state.group_editor.close();
self.connection_state.close_group_editor();
self.refresh_store_from_runtime();
self.terminal.view.status = format!("saved connection group {}", group.name);
}
Err(error) => {
self.connection_state.group_editor.set_error(error);
self.connection_state.set_group_editor_error(error);
}
}
cx.notify();
@@ -128,16 +128,12 @@ impl NyaTermApp {
return;
}
self.connection_state
.confirmations
.open_group_open(ConnectionGroupOpenConfirmState {
.open_group_open_confirm(ConnectionGroupOpenConfirmState {
group_id,
label: group.name.clone(),
connection_count,
});
let group_open_focus = self
.connection_state
.confirmations
.group_open_focus_handle();
let group_open_focus = self.connection_state.group_open_focus_handle();
window.focus(&group_open_focus);
cx.notify();
}
@@ -146,7 +142,7 @@ impl NyaTermApp {
&mut self,
cx: &mut Context<Self>,
) {
self.connection_state.confirmations.close_group_open();
self.connection_state.close_group_open_confirm();
cx.notify();
}
@@ -155,7 +151,7 @@ impl NyaTermApp {
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(confirm) = self.connection_state.confirmations.take_group_open() else {
let Some(confirm) = self.connection_state.take_group_open_confirm() else {
return;
};
self.start_group_connections(confirm.group_id, window, cx);
@@ -61,11 +61,11 @@ use self::network_logic::{
pub(in crate::features) struct ConnectionFeatureState {
pub list: ConnectionListState,
pub import: ConnectionImportState,
import: ConnectionImportState,
pub editor: ConnectionEditorFeatureState,
pub group_editor: ConnectionGroupEditorFeatureState,
pub confirmations: ConnectionConfirmationState,
pub network: NetworkFeatureState,
group_editor: ConnectionGroupEditorFeatureState,
confirmations: ConnectionConfirmationState,
network: NetworkFeatureState,
}
pub(in crate::features) struct ConnectionFeatureFocus {
@@ -109,7 +109,7 @@ pub(in crate::features) struct ConnectionListState {
last_selected_id: Option<String>,
}
pub(in crate::features) struct ConnectionImportState {
struct ConnectionImportState {
import_dialog_open: bool,
import_path_prompt: Option<ConnectionImportSource>,
import_focus: FocusHandle,
@@ -139,7 +139,7 @@ pub(in crate::features) struct ConnectionEditorFeatureState {
menu_scroll: ScrollHandle,
}
pub(in crate::features) struct ConnectionGroupEditorFeatureState {
struct ConnectionGroupEditorFeatureState {
draft: Option<ConnectionGroupEditorState>,
/// The folder-name input, built with the draft it mirrors.
field: Option<Entity<TextField>>,
@@ -147,7 +147,7 @@ pub(in crate::features) struct ConnectionGroupEditorFeatureState {
focus: FocusHandle,
}
pub(in crate::features) struct ConnectionConfirmationState {
struct ConnectionConfirmationState {
clear_all_open: bool,
delete: Option<ConnectionDeleteConfirmState>,
group_delete: Option<ConnectionGroupDeleteConfirmState>,
@@ -155,7 +155,7 @@ pub(in crate::features) struct ConnectionConfirmationState {
group_open_focus: FocusHandle,
}
pub(in crate::features) struct NetworkFeatureState {
struct NetworkFeatureState {
tab: NetworkTab,
delete_confirm: Option<NetworkDeleteConfirmState>,
group_editor: Option<NetworkGroupEditorState>,
@@ -357,6 +357,285 @@ impl ConnectionFeatureState {
}
}
pub fn active_group_editor_draft(&self) -> Option<ConnectionGroupEditorState> {
self.group_editor.active_draft()
}
pub fn group_editor_focus_handle(&self) -> FocusHandle {
self.group_editor.focus_handle()
}
pub fn begin_group_editor(&mut self, draft: ConnectionGroupEditorState) {
self.group_editor.begin_edit(draft);
}
pub fn apply_group_editor_name_key(&mut self, key: &str, input: Option<&str>) -> bool {
self.group_editor.apply_name_key(key, input)
}
pub fn set_group_editor_error(&mut self, error: String) -> bool {
self.group_editor.set_error(error)
}
pub fn close_group_editor(&mut self) {
self.group_editor.close();
}
pub fn open_clear_all(&mut self) {
self.confirmations.open_clear_all();
}
pub fn close_clear_all(&mut self) {
self.confirmations.close_clear_all();
}
pub fn clear_all_is_open(&self) -> bool {
self.confirmations.clear_all_is_open()
}
pub fn active_delete_confirm(&self) -> Option<ConnectionDeleteConfirmState> {
self.confirmations.active_delete()
}
pub fn open_delete_confirm(&mut self, confirm: ConnectionDeleteConfirmState) {
self.confirmations.open_delete(confirm);
}
pub fn close_delete_confirm(&mut self) {
self.confirmations.close_delete();
}
pub fn take_delete_confirm(&mut self) -> Option<ConnectionDeleteConfirmState> {
self.confirmations.take_delete()
}
pub fn active_group_delete_confirm(&self) -> Option<ConnectionGroupDeleteConfirmState> {
self.confirmations.active_group_delete()
}
pub fn open_group_delete_confirm(&mut self, confirm: ConnectionGroupDeleteConfirmState) {
self.confirmations.open_group_delete(confirm);
}
pub fn close_group_delete_confirm(&mut self) {
self.confirmations.close_group_delete();
}
pub fn take_group_delete_confirm(&mut self) -> Option<ConnectionGroupDeleteConfirmState> {
self.confirmations.take_group_delete()
}
pub fn active_group_open_confirm(&self) -> Option<ConnectionGroupOpenConfirmState> {
self.confirmations.active_group_open()
}
pub fn open_group_open_confirm(&mut self, confirm: ConnectionGroupOpenConfirmState) {
self.confirmations.open_group_open(confirm);
}
pub fn close_group_open_confirm(&mut self) {
self.confirmations.close_group_open();
}
pub fn take_group_open_confirm(&mut self) -> Option<ConnectionGroupOpenConfirmState> {
self.confirmations.take_group_open()
}
pub fn group_open_focus_handle(&self) -> FocusHandle {
self.confirmations.group_open_focus_handle()
}
pub fn network_active_tab(&self) -> NetworkTab {
self.network.active_tab()
}
pub fn network_tab_is(&self, tab: NetworkTab) -> bool {
self.network.tab_is(tab)
}
pub fn network_section_is_expanded(&self, section_key: &str) -> bool {
self.network.section_is_expanded(section_key)
}
pub fn network_item_menu_is_open(&self, tab: NetworkTab, id: &str) -> bool {
self.network.item_menu_is_open(tab, id)
}
pub fn network_move_picker_is_open(&self, tab: NetworkTab, id: &str) -> bool {
self.network.move_picker_is_open(tab, id)
}
pub fn active_network_delete_confirm(&self) -> Option<NetworkDeleteConfirmState> {
self.network.active_delete_confirm()
}
pub fn active_network_group_editor(&self) -> Option<NetworkGroupEditorState> {
self.network.active_group_editor()
}
pub fn active_network_group_delete_confirm(&self) -> Option<NetworkGroupDeleteConfirmState> {
self.network.active_group_delete_confirm()
}
pub fn active_network_tunnel_editor(&self) -> Option<NetworkTunnelEditorState> {
self.network.active_tunnel_editor()
}
pub fn active_network_proxy_editor(&self) -> Option<NetworkProxyEditorState> {
self.network.active_proxy_editor()
}
pub fn network_tunnel_editor_focus_handle(&self) -> FocusHandle {
self.network.tunnel_editor_focus_handle()
}
pub fn network_proxy_editor_focus_handle(&self) -> FocusHandle {
self.network.proxy_editor_focus_handle()
}
pub fn set_network_tab(&mut self, tab: NetworkTab) {
self.network.set_tab(tab);
}
pub fn toggle_network_section(&mut self, section_key: String) -> bool {
self.network.toggle_section(section_key)
}
pub fn toggle_network_item_menu(&mut self, tab: NetworkTab, id: String) -> bool {
self.network.toggle_item_menu(tab, id)
}
pub fn toggle_network_move_picker(&mut self, tab: NetworkTab, id: String) -> bool {
self.network.toggle_move_picker(tab, id)
}
pub fn close_network_move_picker(&mut self) {
self.network.close_move_picker();
}
pub fn open_network_delete_confirm(&mut self, confirm: NetworkDeleteConfirmState) {
self.network.open_delete_confirm(confirm);
}
pub fn close_network_delete_confirm(&mut self) {
self.network.close_delete_confirm();
}
pub fn begin_network_group_edit(&mut self, draft: NetworkGroupEditorState) {
self.network.begin_group_edit(draft);
}
pub fn set_network_group_editor_name(&mut self, text: String) -> bool {
self.network.set_group_editor_name(text)
}
pub fn set_network_group_editor_error(&mut self, error: String) -> bool {
self.network.set_group_editor_error(error)
}
pub fn close_network_group_editor(&mut self) {
self.network.close_group_editor();
}
pub fn open_network_group_delete_confirm(&mut self, confirm: NetworkGroupDeleteConfirmState) {
self.network.open_group_delete_confirm(confirm);
}
pub fn close_network_group_delete_confirm(&mut self) {
self.network.close_group_delete_confirm();
}
pub fn begin_network_tunnel_edit(&mut self, draft: NetworkTunnelEditorState) {
self.network.begin_tunnel_edit(draft);
}
pub fn close_network_tunnel_editor(&mut self) {
self.network.close_tunnel_editor();
}
pub fn set_network_tunnel_editor_field(
&mut self,
field: NetworkTunnelEditorField,
text: String,
) -> bool {
self.network.set_tunnel_editor_field(field, text)
}
pub fn cycle_network_tunnel_type(&mut self) -> Option<String> {
self.network.cycle_tunnel_type()
}
pub fn cycle_network_tunnel_connection<'a>(
&mut self,
connection_ids: impl IntoIterator<Item = &'a str>,
) -> bool {
self.network.cycle_tunnel_connection(connection_ids)
}
pub fn cycle_network_tunnel_group<'a>(
&mut self,
group_ids: impl IntoIterator<Item = &'a str>,
) -> bool {
self.network.cycle_tunnel_group(group_ids)
}
pub fn set_network_tunnel_bind_localhost(&mut self, bind_localhost: bool) -> bool {
self.network.set_tunnel_bind_localhost(bind_localhost)
}
pub fn toggle_network_tunnel_auto_open(&mut self) -> Option<bool> {
self.network.toggle_tunnel_auto_open()
}
pub fn set_network_tunnel_editor_error(&mut self, error: String) -> bool {
self.network.set_tunnel_editor_error(error)
}
pub fn begin_network_proxy_edit(&mut self, draft: NetworkProxyEditorState) {
self.network.begin_proxy_edit(draft);
}
pub fn close_network_proxy_editor(&mut self) {
self.network.close_proxy_editor();
}
pub fn set_network_proxy_editor_field(
&mut self,
field: NetworkProxyEditorField,
text: String,
) -> bool {
self.network.set_proxy_editor_field(field, text)
}
pub fn cycle_network_proxy_protocol(&mut self) -> Option<String> {
self.network.cycle_proxy_protocol()
}
pub fn cycle_network_proxy_group<'a>(
&mut self,
group_ids: impl IntoIterator<Item = &'a str>,
) -> bool {
self.network.cycle_proxy_group(group_ids)
}
pub fn set_network_proxy_editor_error(&mut self, error: String) -> bool {
self.network.set_proxy_editor_error(error)
}
pub fn remove_network_item_references(&mut self, tab: NetworkTab, id: &str) {
self.network.remove_item_references(tab, id);
}
pub fn remove_network_group_references(
&mut self,
tab: NetworkTab,
group_id: &str,
deleted_item_ids: &[String],
) {
self.network
.remove_group_references(tab, group_id, deleted_item_ids);
}
pub fn clear_editor_fields(&mut self) {
self.editor.fields.clear();
self.editor.field_subscriptions.clear();
@@ -376,6 +655,34 @@ impl ConnectionFeatureState {
group_id,
);
}
pub fn import_dialog_is_open(&self) -> bool {
self.import.is_dialog_open()
}
pub fn import_path_prompt_active(&self) -> bool {
self.import.path_prompt_active()
}
pub fn import_focus_handle(&self) -> FocusHandle {
self.import.focus_handle()
}
pub fn open_import_dialog(&mut self) {
self.import.open_dialog();
}
pub fn close_import_dialog(&mut self) {
self.import.close_dialog();
}
pub fn begin_import_path_prompt(&mut self, source: ConnectionImportSource) {
self.import.begin_path_prompt(source);
}
pub fn finish_import_path_prompt(&mut self) {
self.import.finish_path_prompt();
}
}
impl ConnectionListState {
@@ -1309,18 +1616,18 @@ mod tests {
clear_network_tunnel_editor, clear_selected_connection_ids, close_connection_more_menu,
commit_connection_editor_new_group, connection_drop_position_for_target,
connection_editor_inline_panel_draft, connection_editor_window_open_or_pending,
cycle_connection_sort_mode, cycle_network_proxy_group, cycle_network_proxy_protocol,
cycle_network_tunnel_connection, cycle_network_tunnel_group, cycle_network_tunnel_type,
finish_connection_editor_save_state, insert_connection_editor_description_newline,
remove_connection_list_references, remove_group_list_references,
remove_network_group_and_item_references, remove_network_group_references,
remove_network_item_references, retain_loaded_connection_list_references,
select_connection_ids, set_connection_drop_target_if_changed,
set_connection_editor_advanced_tab, set_connection_editor_error,
set_connection_editor_field_text, set_connection_editor_icon, set_connection_editor_kind,
set_connection_editor_menu_value, set_connection_editor_password_source,
set_connection_editor_telnet_tab, set_connection_group_editor_error,
set_connection_group_hover, set_network_group_editor_error, set_network_group_editor_name,
cycle_connection_sort_mode, cycle_network_proxy_protocol, cycle_network_tunnel_connection,
cycle_network_tunnel_group, cycle_network_tunnel_type, finish_connection_editor_save_state,
insert_connection_editor_description_newline, remove_connection_list_references,
remove_group_list_references, remove_network_group_and_item_references,
remove_network_group_references, remove_network_item_references,
retain_loaded_connection_list_references, select_connection_ids,
set_connection_drop_target_if_changed, set_connection_editor_advanced_tab,
set_connection_editor_error, set_connection_editor_field_text, set_connection_editor_icon,
set_connection_editor_kind, set_connection_editor_menu_value,
set_connection_editor_password_source, set_connection_editor_telnet_tab,
set_connection_group_editor_error, set_connection_group_hover,
set_network_group_editor_error, set_network_group_editor_name,
set_network_proxy_editor_error, set_network_proxy_editor_field,
set_network_tunnel_bind_localhost, set_network_tunnel_editor_error,
set_network_tunnel_editor_field, stepped_menu_highlight, sync_connection_search_expansion,
@@ -33,9 +33,9 @@ impl NyaTermApp {
.flex()
.flex_col()
.gap_3()
.track_focus(&self.connection_state.group_editor.focus_handle())
.track_focus(&self.connection_state.group_editor_focus_handle())
.on_click(cx.listener(|this, _, window, cx| {
let group_editor_focus = this.connection_state.group_editor.focus_handle();
let group_editor_focus = this.connection_state.group_editor_focus_handle();
window.focus(&group_editor_focus);
cx.notify();
}))
@@ -259,17 +259,9 @@ impl NyaTermApp {
.flex()
.flex_col()
.gap_3()
.track_focus(
&self
.connection_state
.confirmations
.group_open_focus_handle(),
)
.track_focus(&self.connection_state.group_open_focus_handle())
.on_click(cx.listener(|this, _, window, cx| {
let group_open_focus = this
.connection_state
.confirmations
.group_open_focus_handle();
let group_open_focus = this.connection_state.group_open_focus_handle();
window.focus(&group_open_focus);
cx.notify();
}))
@@ -221,25 +221,24 @@ impl NyaTermApp {
.child(self.connections_search_bar(window, cx))
.child(list)
.when_some(
self.connection_state.group_editor.active_draft(),
self.connection_state.active_group_editor_draft(),
|this, editor| this.child(self.connection_group_editor_panel(editor, cx)),
)
.when_some(
self.connection_state.confirmations.active_delete(),
self.connection_state.active_delete_confirm(),
|this, confirm| this.child(self.connection_delete_confirm_panel(confirm, cx)),
)
.when_some(
self.connection_state.confirmations.active_group_delete(),
self.connection_state.active_group_delete_confirm(),
|this, confirm| this.child(self.connection_group_delete_confirm_panel(confirm, cx)),
)
.when_some(
self.connection_state.confirmations.active_group_open(),
self.connection_state.active_group_open_confirm(),
|this, confirm| this.child(self.connection_group_open_confirm_panel(confirm, cx)),
)
.when(
self.connection_state.confirmations.clear_all_is_open(),
|this| this.child(self.connections_clear_all_confirm_panel(cx)),
)
.when(self.connection_state.clear_all_is_open(), |this| {
this.child(self.connections_clear_all_confirm_panel(cx))
})
.when(self.connection_state.list.context_menu_is_open(), |this| {
this.child(self.connection_context_menu_overlay(cx))
})
@@ -79,7 +79,7 @@ impl NyaTermApp {
proxy_list = proxy_list.child(proxy_section(palette, section, self, cx));
}
}
let active_tab = self.connection_state.network.active_tab();
let active_tab = self.connection_state.network_active_tab();
// Tauri NetworkPanel body (PanelHeader is shared):
// scroll(p-3) > Tabs(grid-cols-2) > config row (label + New Group/New item) > grouped list.
// Network create/edit/delete use modal dialogs (Tauri Dialog) over the panel.
@@ -119,7 +119,7 @@ impl NyaTermApp {
.child(network_tab_button(
"network-tab-tunnels",
self.tr("network.tunnels").to_string(),
self.connection_state.network.tab_is(NetworkTab::Tunnels),
self.connection_state.network_tab_is(NetworkTab::Tunnels),
self.theme_palette(),
cx.listener(|this, _, _, cx| {
this.set_network_tab(NetworkTab::Tunnels, cx);
@@ -128,7 +128,7 @@ impl NyaTermApp {
.child(network_tab_button(
"network-tab-proxies",
self.tr("network.proxy").to_string(),
self.connection_state.network.tab_is(NetworkTab::Proxies),
self.connection_state.network_tab_is(NetworkTab::Proxies),
self.theme_palette(),
cx.listener(|this, _, _, cx| {
this.set_network_tab(NetworkTab::Proxies, cx);
@@ -161,7 +161,7 @@ impl NyaTermApp {
"icons/fe/new-folder.svg",
cx.listener(|this, _, _, cx| {
this.open_network_group_editor(
this.connection_state.network.active_tab(),
this.connection_state.network_active_tab(),
None,
cx,
);
@@ -169,8 +169,7 @@ impl NyaTermApp {
))
.when(
self.connection_state
.network
.tab_is(NetworkTab::Tunnels),
.network_tab_is(NetworkTab::Tunnels),
|this| {
this.child(network_create_button(
palette,
@@ -187,8 +186,7 @@ impl NyaTermApp {
)
.when(
self.connection_state
.network
.tab_is(NetworkTab::Proxies),
.network_tab_is(NetworkTab::Proxies),
|this| {
this.child(network_create_button(
palette,
@@ -223,19 +221,19 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) -> Option<AnyElement> {
let palette = self.theme_palette();
if let Some(confirm) = self.connection_state.network.active_delete_confirm() {
if let Some(confirm) = self.connection_state.active_network_delete_confirm() {
return Some(network_delete_confirm_panel(self, confirm, cx).into_any_element());
}
if let Some(editor) = self.connection_state.network.active_group_editor() {
if let Some(editor) = self.connection_state.active_network_group_editor() {
return Some(network_group_editor_panel(self, editor, cx).into_any_element());
}
if let Some(confirm) = self.connection_state.network.active_group_delete_confirm() {
if let Some(confirm) = self.connection_state.active_network_group_delete_confirm() {
return Some(network_group_delete_confirm_panel(self, confirm, cx).into_any_element());
}
if let Some(editor) = self.connection_state.network.active_tunnel_editor() {
if let Some(editor) = self.connection_state.active_network_tunnel_editor() {
return Some(network_tunnel_editor_panel(palette, editor, self, cx).into_any_element());
}
if let Some(editor) = self.connection_state.network.active_proxy_editor() {
if let Some(editor) = self.connection_state.active_network_proxy_editor() {
return Some(network_proxy_editor_panel(palette, editor, self, cx).into_any_element());
}
None
@@ -33,8 +33,7 @@ pub(in crate::features::pages::tunnels) fn proxy_network_row(
let proxy_label_for_delete = proxy.name.clone();
let menu_open = app
.connection_state
.network
.item_menu_is_open(NetworkTab::Proxies, &proxy.id);
.network_item_menu_is_open(NetworkTab::Proxies, &proxy.id);
// Tauri ProxyRow: name, protocol, address; overflow actions on the right.
div()
@@ -74,8 +74,7 @@ pub(in crate::features::pages::tunnels) fn proxy_section(
let section_key = format!("proxy:{}", section.id);
let collapsed = !app
.connection_state
.network
.section_is_expanded(&section_key);
.network_section_is_expanded(&section_key);
let section_id_for_toggle = section.id.clone();
let mut rows = div().flex().flex_col();
if section.proxies.is_empty() {
@@ -93,8 +92,7 @@ pub(in crate::features::pages::tunnels) fn proxy_section(
for (index, proxy) in section.proxies.into_iter().enumerate() {
let move_picker_open = app
.connection_state
.network
.move_picker_is_open(NetworkTab::Proxies, &proxy.id);
.network_move_picker_is_open(NetworkTab::Proxies, &proxy.id);
rows = rows.child(
div()
.flex()
@@ -191,8 +189,7 @@ pub(in crate::features::pages::tunnels) fn proxy_section(
let menu_id = format!("group:{}", group.id);
let menu_open = app
.connection_state
.network
.item_menu_is_open(NetworkTab::Proxies, &menu_id);
.network_item_menu_is_open(NetworkTab::Proxies, &menu_id);
this.child(network_item_overflow_menu(
palette,
app.shell_surface_color(palette.surface),
@@ -77,8 +77,7 @@ pub(in crate::features::pages::tunnels) fn tunnel_section(
let section_key = format!("tunnel:{}", section.id);
let collapsed = !app
.connection_state
.network
.section_is_expanded(&section_key);
.network_section_is_expanded(&section_key);
let section_id_for_toggle = section.id.clone();
let mut rows = div().flex().flex_col();
if section.tunnels.is_empty() {
@@ -119,12 +118,10 @@ pub(in crate::features::pages::tunnels) fn tunnel_section(
let tunnel_label_for_delete = tunnel_name(&tunnel);
let move_picker_open = app
.connection_state
.network
.move_picker_is_open(NetworkTab::Tunnels, &tunnel.id);
.network_move_picker_is_open(NetworkTab::Tunnels, &tunnel.id);
let menu_open = app
.connection_state
.network
.item_menu_is_open(NetworkTab::Tunnels, &tunnel.id);
.network_item_menu_is_open(NetworkTab::Tunnels, &tunnel.id);
let current_group_id = tunnel.group_id.clone();
rows = rows.child(
div()
@@ -272,8 +269,7 @@ pub(in crate::features::pages::tunnels) fn tunnel_section(
let menu_id = format!("group:{}", group.id);
let menu_open = app
.connection_state
.network
.item_menu_is_open(NetworkTab::Tunnels, &menu_id);
.network_item_menu_is_open(NetworkTab::Tunnels, &menu_id);
this.child(network_item_overflow_menu(
palette,
app.shell_surface_color(palette.surface),
@@ -35,7 +35,7 @@ impl NyaTermApp {
.items_center()
.justify_center()
.p_3()
.track_focus(&self.connection_state.import.focus_handle())
.track_focus(&self.connection_state.import_focus_handle())
.on_click(cx.listener(|this, _, _, cx| {
this.close_connection_import_dialog(cx);
}))
+1 -1
View File
@@ -619,7 +619,7 @@ impl NyaTermApp {
.when(self.quick_command_state.import.dialog_open, |this| {
this.child(self.quick_command_import_overlay(cx))
})
.when(self.connection_state.import.is_dialog_open(), |this| {
.when(self.connection_state.import_dialog_is_open(), |this| {
this.child(self.connection_import_overlay(cx))
})
.when(quick_switch_open, |this| {
@@ -554,7 +554,7 @@ impl NyaTermApp {
NavItem::ActiveSessions => SharedString::from(self.active_sessions_header_count()),
// Tauri NetworkPanel header shows active tab profile count.
NavItem::Tunnels => {
let count = match self.connection_state.network.active_tab() {
let count = match self.connection_state.network_active_tab() {
NetworkTab::Tunnels => self.tunnels.len(),
NetworkTab::Proxies => self.proxies.len(),
};
@@ -15,7 +15,7 @@ impl NyaTermApp {
id: String,
cx: &mut Context<Self>,
) {
self.connection_state.network.toggle_item_menu(tab, id);
self.connection_state.toggle_network_item_menu(tab, id);
cx.notify();
}
@@ -25,7 +25,7 @@ impl NyaTermApp {
id: String,
cx: &mut Context<Self>,
) {
if self.connection_state.network.toggle_move_picker(tab, id) {
if self.connection_state.toggle_network_move_picker(tab, id) {
self.terminal.view.status = format!("choose {} group", tab.label());
} else {
self.terminal.view.status = "network move menu closed".to_string();
@@ -74,7 +74,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.tunnels = next_tunnels;
self.connection_state.network.close_move_picker();
self.connection_state.close_network_move_picker();
self.terminal.view.status = format!("tunnel moved to {label}");
self.store_status.message = "tunnel group saved".to_string();
self.store_status.ready = true;
@@ -126,7 +126,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.proxies = next_proxies;
self.connection_state.network.close_move_picker();
self.connection_state.close_network_move_picker();
self.terminal.view.status = format!("proxy moved to {label}");
self.store_status.message = "proxy group saved".to_string();
self.store_status.ready = true;
@@ -148,20 +148,19 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) {
self.connection_state
.network
.open_delete_confirm(NetworkDeleteConfirmState { tab, id, label });
.open_network_delete_confirm(NetworkDeleteConfirmState { tab, id, label });
self.terminal.view.status = "network delete confirmation opened".to_string();
cx.notify();
}
pub(in crate::features) fn cancel_network_delete(&mut self, cx: &mut Context<Self>) {
self.connection_state.network.close_delete_confirm();
self.connection_state.close_network_delete_confirm();
self.terminal.view.status = "network delete cancelled".to_string();
cx.notify();
}
pub(in crate::features) fn confirm_network_delete(&mut self, cx: &mut Context<Self>) {
let Some(delete) = self.connection_state.network.active_delete_confirm() else {
let Some(delete) = self.connection_state.active_network_delete_confirm() else {
self.terminal.view.status = "no network delete is pending".to_string();
cx.notify();
return;
@@ -202,8 +201,7 @@ impl NyaTermApp {
self.tunnels = next_tunnels;
self.pending_tunnels.retain(|id| id != &tunnel_id);
self.connection_state
.network
.remove_item_references(NetworkTab::Tunnels, &tunnel_id);
.remove_network_item_references(NetworkTab::Tunnels, &tunnel_id);
self.terminal.view.status = if deleted {
format!("tunnel '{label}' deleted")
} else {
@@ -240,8 +238,7 @@ impl NyaTermApp {
let deleted = next_proxies.len() != before;
self.proxies = next_proxies;
self.connection_state
.network
.remove_item_references(NetworkTab::Proxies, &proxy_id);
.remove_network_item_references(NetworkTab::Proxies, &proxy_id);
self.terminal.view.status = if deleted {
format!("proxy '{label}' deleted")
} else {
@@ -31,8 +31,7 @@ impl NyaTermApp {
};
self.connection_state
.network
.begin_group_edit(NetworkGroupEditorState {
.begin_network_group_edit(NetworkGroupEditorState {
tab,
id: group_id,
name,
@@ -46,7 +45,7 @@ impl NyaTermApp {
}
pub(in crate::features) fn close_network_group_editor(&mut self, cx: &mut Context<Self>) {
self.connection_state.network.close_group_editor();
self.connection_state.close_network_group_editor();
self.forget_text_inputs("network.group-editor.");
self.terminal.view.status = "network group editor closed".to_string();
cx.notify();
@@ -58,13 +57,13 @@ impl NyaTermApp {
text: String,
cx: &mut Context<Self>,
) {
if self.connection_state.network.set_group_editor_name(text) {
if self.connection_state.set_network_group_editor_name(text) {
cx.notify();
}
}
pub(in crate::features) fn save_network_group_editor(&mut self, cx: &mut Context<Self>) {
let Some(editor) = self.connection_state.network.active_group_editor() else {
let Some(editor) = self.connection_state.active_network_group_editor() else {
self.terminal.view.status = "no network group editor is active".to_string();
cx.notify();
return;
@@ -72,8 +71,7 @@ impl NyaTermApp {
let name = editor.name.trim().to_string();
if name.is_empty() {
self.connection_state
.network
.set_group_editor_error("Group name is required".to_string());
.set_network_group_editor_error("Group name is required".to_string());
cx.notify();
return;
}
@@ -114,7 +112,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.tunnel_groups = groups;
self.connection_state.network.close_group_editor();
self.connection_state.close_network_group_editor();
self.terminal.view.status = format!("tunnel group '{name}' saved");
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = true;
@@ -158,7 +156,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.proxy_groups = groups;
self.connection_state.network.close_group_editor();
self.connection_state.close_network_group_editor();
self.terminal.view.status = format!("proxy group '{name}' saved");
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = true;
@@ -181,8 +179,7 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) {
self.connection_state
.network
.open_group_delete_confirm(NetworkGroupDeleteConfirmState {
.open_network_group_delete_confirm(NetworkGroupDeleteConfirmState {
tab,
id,
label,
@@ -193,13 +190,13 @@ impl NyaTermApp {
}
pub(in crate::features) fn cancel_network_group_delete(&mut self, cx: &mut Context<Self>) {
self.connection_state.network.close_group_delete_confirm();
self.connection_state.close_network_group_delete_confirm();
self.terminal.view.status = "network group delete cancelled".to_string();
cx.notify();
}
pub(in crate::features) fn confirm_network_group_delete(&mut self, cx: &mut Context<Self>) {
let Some(delete) = self.connection_state.network.active_group_delete_confirm() else {
let Some(delete) = self.connection_state.active_network_group_delete_confirm() else {
self.terminal.view.status = "no network group delete is pending".to_string();
cx.notify();
return;
@@ -247,7 +244,7 @@ impl NyaTermApp {
Ok(()) => {
self.tunnel_groups = groups;
self.tunnels = tunnels;
self.connection_state.network.remove_group_references(
self.connection_state.remove_network_group_references(
NetworkTab::Tunnels,
&group_id,
&deleted_tunnel_ids,
@@ -301,7 +298,7 @@ impl NyaTermApp {
Ok(()) => {
self.proxy_groups = groups;
self.proxies = proxies;
self.connection_state.network.remove_group_references(
self.connection_state.remove_network_group_references(
NetworkTab::Proxies,
&group_id,
&deleted_proxy_ids,
@@ -23,8 +23,7 @@ impl NyaTermApp {
};
self.connection_state
.network
.begin_proxy_edit(NetworkProxyEditorState {
.begin_network_proxy_edit(NetworkProxyEditorState {
id: proxy_id,
name: proxy.name,
protocol: match proxy.protocol.as_str() {
@@ -54,13 +53,13 @@ impl NyaTermApp {
// the next proxy to seed from its own values.
self.forget_text_inputs("network.proxy-editor.");
self.terminal.view.status = "proxy editor opened".to_string();
let proxy_editor_focus = self.connection_state.network.proxy_editor_focus_handle();
let proxy_editor_focus = self.connection_state.network_proxy_editor_focus_handle();
window.focus(&proxy_editor_focus);
cx.notify();
}
pub(in crate::features) fn close_network_proxy_editor(&mut self, cx: &mut Context<Self>) {
self.connection_state.network.close_proxy_editor();
self.connection_state.close_network_proxy_editor();
self.forget_text_inputs("network.proxy-editor.");
self.terminal.view.status = "proxy editor closed".to_string();
cx.notify();
@@ -84,15 +83,14 @@ impl NyaTermApp {
};
if self
.connection_state
.network
.set_proxy_editor_field(field, text)
.set_network_proxy_editor_field(field, text)
{
cx.notify();
}
}
pub(in crate::features) fn cycle_network_proxy_protocol(&mut self, cx: &mut Context<Self>) {
if let Some(protocol) = self.connection_state.network.cycle_proxy_protocol() {
if let Some(protocol) = self.connection_state.cycle_network_proxy_protocol() {
self.terminal.view.status = format!("proxy protocol set to {protocol}");
}
cx.notify();
@@ -101,8 +99,7 @@ impl NyaTermApp {
pub(in crate::features) fn cycle_network_proxy_group(&mut self, cx: &mut Context<Self>) {
if self
.connection_state
.network
.cycle_proxy_group(self.proxy_groups.iter().map(|group| group.id.as_str()))
.cycle_network_proxy_group(self.proxy_groups.iter().map(|group| group.id.as_str()))
{
self.terminal.view.status = "proxy group changed".to_string();
}
@@ -110,7 +107,7 @@ impl NyaTermApp {
}
pub(in crate::features) fn save_network_proxy_editor(&mut self, cx: &mut Context<Self>) {
let Some(editor) = self.connection_state.network.active_proxy_editor() else {
let Some(editor) = self.connection_state.active_network_proxy_editor() else {
self.terminal.view.status = "no proxy editor is active".to_string();
cx.notify();
return;
@@ -200,7 +197,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.proxies = next_proxies;
self.connection_state.network.close_proxy_editor();
self.connection_state.close_network_proxy_editor();
self.terminal.view.status = format!("proxy '{name}' saved");
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = true;
@@ -210,8 +207,7 @@ impl NyaTermApp {
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = false;
self.connection_state
.network
.set_proxy_editor_error(self.terminal.view.status.clone());
.set_network_proxy_editor_error(self.terminal.view.status.clone());
}
}
cx.notify();
@@ -224,8 +220,7 @@ impl NyaTermApp {
) {
let error = error.into();
self.connection_state
.network
.set_proxy_editor_error(error.clone());
.set_network_proxy_editor_error(error.clone());
self.terminal.view.status = error;
cx.notify();
}
@@ -7,7 +7,7 @@ use crate::models::{NetworkTab, NetworkTunnelEditorField, NetworkTunnelEditorSta
impl NyaTermApp {
pub(in crate::features) fn set_network_tab(&mut self, tab: NetworkTab, cx: &mut Context<Self>) {
self.connection_state.network.set_tab(tab);
self.connection_state.set_network_tab(tab);
self.terminal.view.status = format!("network tab set to {}", tab.label());
cx.notify();
}
@@ -19,7 +19,7 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) {
let key = network_section_key(tab, &section_id);
if self.connection_state.network.toggle_section(key) {
if self.connection_state.toggle_network_section(key) {
self.terminal.view.status = format!("expanded {} group", tab.label());
} else {
self.terminal.view.status = format!("collapsed {} group", tab.label());
@@ -44,8 +44,7 @@ impl NyaTermApp {
};
self.connection_state
.network
.begin_tunnel_edit(NetworkTunnelEditorState {
.begin_network_tunnel_edit(NetworkTunnelEditorState {
id: tunnel_id,
is_open: tunnel.is_open,
name: tunnel.name,
@@ -79,13 +78,13 @@ impl NyaTermApp {
// next tunnel to seed from its own values.
self.forget_text_inputs("network.tunnel-editor.");
self.terminal.view.status = "tunnel editor opened".to_string();
let tunnel_editor_focus = self.connection_state.network.tunnel_editor_focus_handle();
let tunnel_editor_focus = self.connection_state.network_tunnel_editor_focus_handle();
window.focus(&tunnel_editor_focus);
cx.notify();
}
pub(in crate::features) fn close_network_tunnel_editor(&mut self, cx: &mut Context<Self>) {
self.connection_state.network.close_tunnel_editor();
self.connection_state.close_network_tunnel_editor();
self.forget_text_inputs("network.tunnel-editor.");
self.terminal.view.status = "tunnel editor closed".to_string();
cx.notify();
@@ -107,15 +106,14 @@ impl NyaTermApp {
};
if self
.connection_state
.network
.set_tunnel_editor_field(field, text)
.set_network_tunnel_editor_field(field, text)
{
cx.notify();
}
}
pub(in crate::features) fn cycle_network_tunnel_type(&mut self, cx: &mut Context<Self>) {
if let Some(tunnel_type) = self.connection_state.network.cycle_tunnel_type() {
if let Some(tunnel_type) = self.connection_state.cycle_network_tunnel_type() {
self.terminal.view.status = format!("tunnel type set to {tunnel_type}");
}
cx.notify();
@@ -130,8 +128,7 @@ impl NyaTermApp {
.collect::<Vec<_>>();
if self
.connection_state
.network
.cycle_tunnel_connection(connection_ids)
.cycle_network_tunnel_connection(connection_ids)
{
self.terminal.view.status = "tunnel SSH connection changed".to_string();
cx.notify();
@@ -141,8 +138,7 @@ impl NyaTermApp {
pub(in crate::features) fn cycle_network_tunnel_group(&mut self, cx: &mut Context<Self>) {
if self
.connection_state
.network
.cycle_tunnel_group(self.tunnel_groups.iter().map(|group| group.id.as_str()))
.cycle_network_tunnel_group(self.tunnel_groups.iter().map(|group| group.id.as_str()))
{
self.terminal.view.status = "tunnel group changed".to_string();
}
@@ -155,13 +151,12 @@ impl NyaTermApp {
cx: &mut Context<Self>,
) {
self.connection_state
.network
.set_tunnel_bind_localhost(bind_localhost);
.set_network_tunnel_bind_localhost(bind_localhost);
cx.notify();
}
pub(in crate::features) fn toggle_network_tunnel_auto_open(&mut self, cx: &mut Context<Self>) {
if let Some(auto_open) = self.connection_state.network.toggle_tunnel_auto_open() {
if let Some(auto_open) = self.connection_state.toggle_network_tunnel_auto_open() {
self.terminal.view.status = if auto_open {
"tunnel auto-open enabled"
} else {
@@ -173,7 +168,7 @@ impl NyaTermApp {
}
pub(in crate::features) fn save_network_tunnel_editor(&mut self, cx: &mut Context<Self>) {
let Some(editor) = self.connection_state.network.active_tunnel_editor() else {
let Some(editor) = self.connection_state.active_network_tunnel_editor() else {
self.terminal.view.status = "no tunnel editor is active".to_string();
cx.notify();
return;
@@ -255,7 +250,7 @@ impl NyaTermApp {
{
Ok(()) => {
self.tunnels = next_tunnels;
self.connection_state.network.close_tunnel_editor();
self.connection_state.close_network_tunnel_editor();
self.terminal.view.status = format!("tunnel '{name}' saved");
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = true;
@@ -265,8 +260,7 @@ impl NyaTermApp {
self.store_status.message = self.terminal.view.status.clone();
self.store_status.ready = false;
self.connection_state
.network
.set_tunnel_editor_error(self.terminal.view.status.clone());
.set_network_tunnel_editor_error(self.terminal.view.status.clone());
}
}
cx.notify();
@@ -279,8 +273,7 @@ impl NyaTermApp {
) {
let error = error.into();
self.connection_state
.network
.set_tunnel_editor_error(error.clone());
.set_network_tunnel_editor_error(error.clone());
self.terminal.view.status = error;
cx.notify();
}
+31 -22
View File
@@ -3,13 +3,13 @@
This document records the current GPUI migration boundaries and debt in
`nyaterm-desktop`. Keep dynamic counts here instead of in `AGENTS.md`.
Last updated from the working tree on 2026-07-27.
Last updated from the working tree on 2026-07-28.
## Current Metrics
| Metric | Current value | Notes |
| --- | ---: | --- |
| `NyaTermApp` fields | 262 | Counted from `features/app_state/mod.rs`; down from 585, still transitional. |
| `NyaTermApp` fields | 261 | Counted from `features/app_state/mod.rs`; down from 585, still transitional. |
| `impl NyaTermApp` blocks | 240 | Spread across 235 files under `crates/nyaterm-desktop/src`. |
| `#[path = "..."]` declarations in desktop | 0 | Cleared. Every directory is a real module; the boundary script fails on any new occurrence. |
| `use super::*` imports in desktop | 372 | Includes indented test-module imports; historical migration debt, do not add new occurrences. |
@@ -318,17 +318,21 @@ these as staged extraction candidates, not as formatting-only refactor targets.
guards governed editor runtime/view/projection paths against direct `focus`
field reads.
- Connection import runtime, overlay, and root rendering now use
`ConnectionImportState` façade methods for dialog visibility, active path
prompts, and focus handle access. The architecture script guards those files
against reintroducing direct `import_dialog_open`/`import_path_prompt`/
`import_focus` field reads.
`ConnectionFeatureState` façade methods for dialog visibility, active path
prompts, and focus handle access. The `import` child state is private, and
the architecture script guards governed features against reintroducing direct
`connection_state.import.*` access.
- Connection group editor name input and validation errors now route through
`ConnectionGroupEditorFeatureState` methods instead of direct runtime draft
mutation.
`ConnectionFeatureState` methods instead of direct runtime draft mutation.
- Connection group editor runtime, modal view, page rendering, and snapshot
publication now use `ConnectionGroupEditorFeatureState` façade methods for
active draft, open state, and focus handle access. The architecture script
guards those files against reintroducing direct `draft`/`focus` field reads.
publication now use `ConnectionFeatureState` façade methods for active draft,
open state, and focus handle access. The `group_editor` child state is
private, and the architecture script guards governed features against
reintroducing direct `connection_state.group_editor.*` access.
- Connection delete, group-delete, group-open and clear-all confirmations now
route through `ConnectionFeatureState` façade methods. The `confirmations`
child state is private, so rendering and runtime actions no longer reach into
`connection_state.confirmations.*` directly.
- Connection list refresh cleanup now runs after successful
`refresh_store_from_runtime()` session reloads. Selection, range anchor,
hover, pending hover, context menus, expanded groups, and drop target state
@@ -340,12 +344,13 @@ these as staged extraction candidates, not as formatting-only refactor targets.
- Saved-group expansion now routes through `ConnectionListState::expand_group`;
the architecture script guards against reintroducing direct expanded-group
insertion in governed connections code.
- Network page UI state now exposes semantic methods for tab/menu/move-picker
state, expanded-section reads, delete and group confirmations, tunnel/proxy
editor lifetime, group/tunnel/proxy editor input, focus/error/cycle/toggle
transitions, and deleted item reference cleanup. Tunnel runtime actions and
Network page rendering now use these methods instead of directly accessing
`connection_state.network` fields for those transitions and projections.
- Network page UI state now sits behind `ConnectionFeatureState` façade methods
for tab/menu/move-picker state, expanded-section reads, delete and group
confirmations, tunnel/proxy editor lifetime, group/tunnel/proxy editor input,
focus/error/cycle/toggle transitions, and deleted item reference cleanup. The
`network` child state and `NetworkFeatureState` type are private to the state
module; tunnel runtime actions, Network page rendering, and panel-count
projection no longer access `connection_state.network.*` directly.
- `NetworkFeatureState` pure helper logic now lives in
`features/connections/state/network_logic.rs`. The public semantic methods
remain in `state.rs`, while 485 lines of menu, move-picker, group editor,
@@ -814,6 +819,10 @@ these as staged extraction candidates, not as formatting-only refactor targets.
import JSON parsing, rule normalization, and merge accounting. `storage.rs`
still owns settings load/save persistence, so redb table names, settings
field paths, encryption, backup, and legacy fallback behavior are unchanged.
- The architecture script's local legacy-source-path allowlist now includes
only the static icon vendoring script and manifest in addition to the existing
migration inventory paths. These references are provenance for committed
assets; default builds still must not depend on `./temp/nyaterm-tauri`.
## Migrating
@@ -831,11 +840,11 @@ Current ownership map:
| Serial ports | `NyaTermApp.connection_serial_ports` | Runtime/discovered state | Not persisted by this state grouping. |
| Tunnel/proxy configs | `NyaTermApp.tunnels`, `tunnel_groups`, `proxies`, `proxy_groups` | Persisted network config | UI overlay state moved under `connection_state.network`; config collections remain persisted domain state. |
| List search/sort/hover/selection/DnD | `NyaTermApp.connection_state.list` | Temporary UI state | State is not persisted except sort setting remains synced to settings as before. |
| Connection import dialog | `NyaTermApp.connection_state.import` | Temporary UI/runtime prompt state | File import still runs through existing runtime paths. |
| Connection import dialog | `NyaTermApp.connection_state` private import child | Temporary UI/runtime prompt state | File import still runs through existing runtime paths; runtime and rendering enter through `ConnectionFeatureState` methods. |
| Connection editor | `NyaTermApp.connection_state.editor` | Editing draft/window UI state | Runtime key handling, window lifecycle, rendering popovers, and sideband projection use state methods; draft remains separate from saved connection data. |
| Group editor | `NyaTermApp.connection_state.group_editor` | Editing draft UI state | Draft remains separate from saved groups. |
| Delete/open confirmations | `NyaTermApp.connection_state.confirmations` | Temporary UI state | Rendering and sideband projection now use state methods; persisted data changes only after existing confirm actions run. |
| Network page UI | `NyaTermApp.connection_state.network` | Temporary UI/editor state | Page rendering, editor focus, confirm/editor draft reads, and panel-count projection use state methods. Tunnel/proxy configs remain in top-level persisted collections. |
| Group editor | `NyaTermApp.connection_state` private group-editor child | Editing draft UI state | Draft remains separate from saved groups; runtime and rendering enter through `ConnectionFeatureState` methods. |
| Delete/open confirmations | `NyaTermApp.connection_state` private confirmations child | Temporary UI state | Rendering and runtime actions enter through `ConnectionFeatureState` methods; persisted data changes only after existing confirm actions run. |
| Network page UI | `NyaTermApp.connection_state` private network child | Temporary UI/editor state | Page rendering, editor focus, confirm/editor draft reads, and panel-count projection use `ConnectionFeatureState` methods. Tunnel/proxy configs remain in top-level persisted collections. |
This round changed desktop-side state ownership, UI state plumbing, and module
boundaries. Final reports should avoid broad statements that sound like no
@@ -858,7 +867,7 @@ desktop state ownership/UI adapter change, not a terminal parser, terminal
protocol, SSH/SFTP protocol, transfer protocol, or persistence-format change.
The tunnel/proxy runtime action files were touched only to route Network page
menu, editor, confirmation, and stale-reference UI state through
`NetworkFeatureState` after the existing persistence operations succeed.
`ConnectionFeatureState` after the existing persistence operations succeed.
Tunnel/proxy config storage formats and transport execution paths continue to
use the existing behavior.
+35 -17
View File
@@ -453,6 +453,8 @@ while IFS=: read -r file _line text; do
case "$file" in
crates/nyaterm-desktop/src/features/mod.rs) ;;
crates/nyaterm-desktop/src/features/app_state/construct.rs) ;;
scripts/sync-icons.sh) ;;
scripts/icons.manifest) ;;
docs/architecture/gpui-migration-status.md) ;;
scripts/check-architecture-boundaries.sh) ;;
*)
@@ -577,27 +579,31 @@ check_no_matches \
'^[[:space:]]*pub(\([^)]*\))?[[:space:]]+(search_draft|search_focus|sort_mode|more_menu_open|context_menu|group_context_menu|hovered_connection_id|hover_pending|drop_target|hovered_group_id|expanded_group_ids|selected_ids|last_selected_id|import_dialog_open|import_path_prompt|import_focus|draft|window|window_open_pending|focus|icon_picker_open|menu|clear_all_open|delete|group_delete|group_open|group_open_focus|tab|delete_confirm|group_delete_confirm|item_menu|move_picker|expanded_sections|tunnel_editor|proxy_editor|group_editor_focus|tunnel_editor_focus|proxy_editor_focus)[[:space:]]*:' \
crates/nyaterm-desktop/src/features/connections/state.rs
check_no_matches \
"connection confirmations must be mutated through ConnectionConfirmationState methods" \
"connection confirmation child state must stay behind ConnectionFeatureState methods" \
'connection_state\.confirmations\.' \
crates/nyaterm-desktop/src/features
check_no_matches \
"connection confirmations must be mutated through ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open)\s*=' \
crates/nyaterm-desktop/src/features/connections
check_no_matches \
"connection page confirmations must be mutated through ConnectionConfirmationState methods" \
"connection page confirmations must be mutated through ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open)\s*=' \
crates/nyaterm-desktop/src/features/pages/connections
check_no_matches \
"connection menu confirmation reads must use ConnectionConfirmationState methods" \
"connection menu confirmation reads must use ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open|group_open_focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/connections/connections/menus.rs
check_no_matches \
"connection confirmation panel reads must use ConnectionConfirmationState methods" \
"connection confirmation panel reads must use ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open|group_open_focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/pages/connections/editor/group_delete.rs
check_no_matches \
"connections page confirmation reads must use ConnectionConfirmationState methods" \
"connections page confirmation reads must use ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open|group_open_focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/pages/connections/view/page.rs
check_no_matches \
"event pump confirmation projection must use ConnectionConfirmationState methods" \
"event pump confirmation projection must use ConnectionFeatureState methods" \
'connection_state\.confirmations\.(clear_all_open|delete|group_delete|group_open|group_open_focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/shell/event_pump/publish.rs
@@ -634,15 +640,19 @@ check_no_matches \
crates/nyaterm-desktop/src/features/root.rs
check_no_matches \
"connection import runtime must use ConnectionImportState methods" \
"connection import child state must stay behind ConnectionFeatureState methods" \
'connection_state\.import\.' \
crates/nyaterm-desktop/src/features
check_no_matches \
"connection import runtime must use ConnectionFeatureState methods" \
'connection_state\.import\.(import_dialog_open|import_path_prompt|import_focus)' \
crates/nyaterm-desktop/src/features/connections/connection_import_runtime.rs
check_no_matches \
"connection import overlay must use ConnectionImportState methods" \
"connection import overlay must use ConnectionFeatureState methods" \
'connection_state\.import\.(import_dialog_open|import_path_prompt|import_focus)' \
crates/nyaterm-desktop/src/features/panels/connection_import_overlay.rs
check_no_matches \
"root connection import overlay state must use ConnectionImportState methods" \
"root connection import overlay state must use ConnectionFeatureState methods" \
'connection_state\.import\.(import_dialog_open|import_path_prompt|import_focus)' \
crates/nyaterm-desktop/src/features/root.rs
@@ -650,21 +660,25 @@ check_no_matches \
"connection group editor draft mutations must go through ConnectionGroupEditorFeatureState methods" \
'connection_state\.group_editor\.draft\.as_mut\(' \
crates/nyaterm-desktop/src/features/connections
check_no_matches \
"connection group editor child state must stay behind ConnectionFeatureState methods" \
'connection_state\.group_editor\.' \
crates/nyaterm-desktop/src/features
check_no_matches \
"connection group runtime must use ConnectionGroupEditorFeatureState methods" \
"connection group runtime must use ConnectionFeatureState methods" \
'connection_state\.group_editor\.(draft|focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/connections/connection_runtime/groups.rs
check_no_matches \
"connection group editor panel must use ConnectionGroupEditorFeatureState methods" \
"connection group editor panel must use ConnectionFeatureState methods" \
'connection_state\.group_editor\.(draft|focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/pages/connections/editor/group_delete.rs
check_no_matches \
"connections page group editor state must use ConnectionGroupEditorFeatureState methods" \
"connections page group editor state must use ConnectionFeatureState methods" \
'connection_state\.group_editor\.(draft|focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/pages/connections/view/page.rs
check_no_matches \
"event pump group editor state must use ConnectionGroupEditorFeatureState methods" \
"event pump group editor state must use ConnectionFeatureState methods" \
'connection_state\.group_editor\.(draft|focus)(\.|[[:space:]]|$)' \
crates/nyaterm-desktop/src/features/shell/event_pump/publish.rs
@@ -708,19 +722,23 @@ check_no_matches \
crates/nyaterm-desktop/src/features/shell/panel_resize_runtime.rs
check_no_matches \
"network editor drafts must be mutated through NetworkFeatureState methods" \
"network child state must stay behind ConnectionFeatureState methods" \
'connection_state\.network\.' \
crates/nyaterm-desktop/src/features
check_no_matches \
"network editor drafts must be mutated through ConnectionFeatureState methods" \
'connection_state\.network\.(group_editor|tunnel_editor|proxy_editor)\.as_mut\(' \
crates/nyaterm-desktop/src/features/tunnels
check_no_matches \
"network page state reads must use NetworkFeatureState methods" \
"network page state reads must use ConnectionFeatureState methods" \
'connection_state\.network\.(tab|delete_confirm|group_editor|group_delete_confirm|item_menu|move_picker|expanded_sections|tunnel_editor|proxy_editor|group_editor_focus|tunnel_editor_focus|proxy_editor_focus)(\.|[[:space:]]|==|,|\)|$)' \
crates/nyaterm-desktop/src/features/pages/tunnels
check_no_matches \
"network runtime state reads must use NetworkFeatureState methods" \
"network runtime state reads must use ConnectionFeatureState methods" \
'connection_state\.network\.(tab|delete_confirm|group_editor|group_delete_confirm|item_menu|move_picker|expanded_sections|tunnel_editor|proxy_editor|group_editor_focus|tunnel_editor_focus|proxy_editor_focus)(\.|[[:space:]]|==|,|\)|$)' \
crates/nyaterm-desktop/src/features/tunnels
check_no_matches \
"panel stack network projection must use NetworkFeatureState methods" \
"panel stack network projection must use ConnectionFeatureState methods" \
'connection_state\.network\.(tab|delete_confirm|group_editor|group_delete_confirm|item_menu|move_picker|expanded_sections|tunnel_editor|proxy_editor|group_editor_focus|tunnel_editor_focus|proxy_editor_focus)(\.|[[:space:]]|==|,|\)|$)' \
crates/nyaterm-desktop/src/features/shell/panel_stack_runtime.rs