Skip to content

Commit 1768217

Browse files
committed
Add visual mode to explorer and improve UX
1 parent a9d7f03 commit 1768217

File tree

19 files changed

+972
-14
lines changed

19 files changed

+972
-14
lines changed

sqlit/core/binding_contexts.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ def get_binding_contexts(ctx: InputContext) -> set[str]:
1212

1313
if ctx.focus == "explorer":
1414
contexts.add("tree")
15+
if ctx.tree_visual_mode_active:
16+
contexts.add("tree_visual")
1517
if ctx.tree_filter_active:
1618
contexts.add("tree_filter")
1719

sqlit/core/input_context.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class InputContext:
1616
leader_pending: bool
1717
leader_menu: str
1818
tree_filter_active: bool
19+
tree_multi_select_active: bool
20+
tree_visual_mode_active: bool
1921
autocomplete_visible: bool
2022
results_filter_active: bool
2123
value_view_active: bool
@@ -25,6 +27,7 @@ class InputContext:
2527
current_connection_name: str | None
2628
tree_node_kind: str | None
2729
tree_node_connection_name: str | None
30+
tree_node_connection_selected: bool
2831
last_result_is_error: bool
2932
has_results: bool
3033
stacked_result_count: int = 0

sqlit/core/keymap.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ def _build_action_keys(self) -> list[ActionKeyDef]:
292292
return [
293293
# Tree actions
294294
ActionKeyDef("n", "new_connection", "tree"),
295+
ActionKeyDef("v", "enter_tree_visual_mode", "tree"),
296+
ActionKeyDef("escape", "exit_tree_visual_mode", "tree_visual"),
297+
ActionKeyDef("v", "exit_tree_visual_mode", "tree_visual", primary=False),
298+
ActionKeyDef("escape", "clear_connection_selection", "tree"),
295299
ActionKeyDef("s", "select_table", "tree"),
296300
ActionKeyDef("f", "refresh_tree", "tree"),
297301
ActionKeyDef("R", "refresh_tree", "tree", primary=False),
@@ -307,7 +311,9 @@ def _build_action_keys(self) -> list[ActionKeyDef]:
307311
ActionKeyDef("x", "disconnect", "tree"),
308312
ActionKeyDef("z", "collapse_tree", "tree"),
309313
ActionKeyDef("j", "tree_cursor_down", "tree"),
314+
ActionKeyDef("down", "tree_cursor_down", "tree", primary=False),
310315
ActionKeyDef("k", "tree_cursor_up", "tree"),
316+
ActionKeyDef("up", "tree_cursor_up", "tree", primary=False),
311317
ActionKeyDef("slash", "tree_filter", "tree"),
312318
ActionKeyDef("escape", "tree_filter_close", "tree_filter"),
313319
ActionKeyDef("enter", "tree_filter_accept", "tree_filter"),
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Helpers for safely persisting connections without dropping stored passwords."""
2+
3+
from __future__ import annotations
4+
5+
import copy
6+
from typing import TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
from sqlit.domains.connections.app.credentials import CredentialsService
10+
from sqlit.domains.connections.domain.config import ConnectionConfig
11+
12+
13+
def build_persist_connections(
14+
connections: list[ConnectionConfig],
15+
credentials_service: CredentialsService,
16+
) -> list[ConnectionConfig]:
17+
"""Return a copy of connections with missing passwords filled from storage.
18+
19+
This prevents saves from clearing stored passwords when the in-memory
20+
connection list was loaded without credentials.
21+
"""
22+
persist_connections = copy.deepcopy(connections)
23+
for conn in persist_connections:
24+
endpoint = conn.tcp_endpoint
25+
if endpoint and endpoint.password is None:
26+
stored = credentials_service.get_password(conn.name)
27+
if stored is not None:
28+
endpoint.password = stored
29+
30+
if conn.tunnel and conn.tunnel.password is None:
31+
stored_ssh = credentials_service.get_ssh_password(conn.name)
32+
if stored_ssh is not None:
33+
conn.tunnel.password = stored_ssh
34+
35+
return persist_connections

sqlit/domains/connections/store/connections.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,14 @@ def save_all(self, connections: list[ConnectionConfig]) -> None:
189189
Args:
190190
connections: List of ConnectionConfig objects to save.
191191
"""
192+
from sqlit.domains.connections.app.persist_utils import build_persist_connections
193+
192194
errors: list[CredentialsStoreError] = []
193-
for config in connections:
195+
persist_connections = build_persist_connections(connections, self.credentials_service)
196+
for config in persist_connections:
194197
errors.extend(self._save_credentials(config))
195198

196-
payload = [self._config_to_dict_without_passwords(c) for c in connections]
199+
payload = [self._config_to_dict_without_passwords(c) for c in persist_connections]
197200
self._write_json(self._wrap_connections_payload(payload))
198201
if errors:
199202
raise CredentialsPersistError(errors)

0 commit comments

Comments
 (0)