cc-session-control 0.6.2__tar.gz → 0.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cc_session_control-0.6.2/src/cc_session_control.egg-info → cc_session_control-0.6.3}/PKG-INFO +1 -1
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/__init__.py +1 -1
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/app.py +8 -5
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_base.py +15 -1
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_keytable.py +2 -2
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/agents.py +11 -15
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/rc.py +13 -17
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/sessions.py +19 -18
- {cc_session_control-0.6.2 → cc_session_control-0.6.3/src/cc_session_control.egg-info}/PKG-INFO +1 -1
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/LICENSE +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/README.md +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/pyproject.toml +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/setup.cfg +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/__main__.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/__init__.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/agent_ops.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/resume_list.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/session_ops.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/skill_ops.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/cli.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/clipboard.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/config.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/__init__.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/cleanup.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/environments.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/liveness.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/proc.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/rc.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/registry.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/sessions.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/snapshot.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/models.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/skill/SKILL.md +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/__init__.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_colspec.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_confirm.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_rows.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_session_row.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_sessions_cleanup.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/SOURCES.txt +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/dependency_links.txt +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/entry_points.txt +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/requires.txt +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/top_level.txt +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_agent_ops.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_agents.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_app.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_cleanup.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_cli.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_data.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_environments.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_liveness.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_proc.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_rc.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_registry.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_resume_list.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_sessions.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_skill_ops.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_smoke.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_snapshot.py +0 -0
- {cc_session_control-0.6.2 → cc_session_control-0.6.3}/tests/test_views.py +0 -0
|
@@ -6,7 +6,7 @@ import curses
|
|
|
6
6
|
import os
|
|
7
7
|
import threading
|
|
8
8
|
from collections.abc import Callable
|
|
9
|
-
from typing import Protocol, runtime_checkable
|
|
9
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
10
10
|
|
|
11
11
|
import urwid
|
|
12
12
|
|
|
@@ -16,6 +16,9 @@ from .views.agents import AgentsView
|
|
|
16
16
|
from .views.rc import RCView
|
|
17
17
|
from .views.sessions import SessionsView
|
|
18
18
|
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from .actions.session_ops import ExitIntent
|
|
21
|
+
|
|
19
22
|
# D7/R10: shown across all tabs when `/proc` is unavailable (e.g. macOS), where
|
|
20
23
|
# the "current" session can't be determined and destructive ops are refused.
|
|
21
24
|
_DEGRADED_BANNER = "⚠ liveness 降级(无 /proc):terminate/delete/cleanup 已受限"
|
|
@@ -89,7 +92,7 @@ def _make_screen() -> urwid.raw_display.Screen:
|
|
|
89
92
|
|
|
90
93
|
class App:
|
|
91
94
|
def __init__(self) -> None:
|
|
92
|
-
self.result:
|
|
95
|
+
self.result: ExitIntent | None = None
|
|
93
96
|
self._exiting = False
|
|
94
97
|
self._alarm_handle: object | None = None
|
|
95
98
|
self._notify_alarm: object | None = None
|
|
@@ -211,14 +214,14 @@ class App:
|
|
|
211
214
|
self._confirm_yes = None
|
|
212
215
|
self.set_hints(self.views[self._active].keyhints())
|
|
213
216
|
|
|
214
|
-
def _exit(self, result:
|
|
217
|
+
def _exit(self, result: ExitIntent | None = None) -> None:
|
|
215
218
|
self._exiting = True
|
|
216
219
|
if self._alarm_handle:
|
|
217
220
|
self.loop.remove_alarm(self._alarm_handle)
|
|
218
221
|
self.result = result
|
|
219
222
|
raise urwid.ExitMainLoop()
|
|
220
223
|
|
|
221
|
-
def exit_with(self, intent:
|
|
224
|
+
def exit_with(self, intent: ExitIntent) -> None:
|
|
222
225
|
"""Exit the MainLoop carrying a `session_ops.ExitIntent`.
|
|
223
226
|
|
|
224
227
|
Views construct the intent (they know the verb); `cli._cmd_tui` calls
|
|
@@ -312,7 +315,7 @@ class App:
|
|
|
312
315
|
view.apply_data()
|
|
313
316
|
return True
|
|
314
317
|
|
|
315
|
-
def run(self) ->
|
|
318
|
+
def run(self) -> ExitIntent | None:
|
|
316
319
|
self._pipe_fd = self.loop.watch_pipe(self._on_pipe)
|
|
317
320
|
self.views[self._active].load()
|
|
318
321
|
self.set_hints(self.views[self._active].keyhints())
|
|
@@ -16,6 +16,7 @@ import urwid
|
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from ..app import App
|
|
19
|
+
from ._keytable import Key
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class ListTabView:
|
|
@@ -69,7 +70,20 @@ class ListTabView:
|
|
|
69
70
|
# --- key-table dispatch (see views/_keytable.py) ---
|
|
70
71
|
|
|
71
72
|
#: the view's single-source key table; subclasses override.
|
|
72
|
-
KEY_TABLE: tuple = ()
|
|
73
|
+
KEY_TABLE: tuple[Key, ...] = ()
|
|
74
|
+
|
|
75
|
+
def handle_key(self, key: str) -> None:
|
|
76
|
+
"""Default TabView key handling: overlay mode intercepts, list mode
|
|
77
|
+
dispatches from KEY_TABLE. A view with extra modes (e.g. the Sessions
|
|
78
|
+
filter/cleanup/preview) overrides, handles those, then calls super()."""
|
|
79
|
+
if self._overlay_active():
|
|
80
|
+
self._handle_overlay_key(key)
|
|
81
|
+
return
|
|
82
|
+
self._dispatch_key(key)
|
|
83
|
+
|
|
84
|
+
def _overlay_active(self) -> bool:
|
|
85
|
+
"""Whether the view is in its overlay (help/watch) mode right now."""
|
|
86
|
+
raise NotImplementedError
|
|
73
87
|
|
|
74
88
|
def _dispatch_key(self, key: str) -> None:
|
|
75
89
|
"""List-mode dispatch driven by `KEY_TABLE` — the same declaration that
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_keytable.py
RENAMED
|
@@ -19,7 +19,7 @@ class Key:
|
|
|
19
19
|
keys: tuple[str, ...] # urwid key names, e.g. ("enter", "o")
|
|
20
20
|
hint: str | None # footer label; None = shown only in help
|
|
21
21
|
handler: str # view method name (resolved via getattr)
|
|
22
|
-
|
|
22
|
+
help_lines: tuple[str, ...] = () # pre-indented help-overlay display lines
|
|
23
23
|
section: str | None = None # help section this entry lists under
|
|
24
24
|
needs_selection: bool = True # call handler(_selected()) vs handler()
|
|
25
25
|
|
|
@@ -45,7 +45,7 @@ def help_lines(table: tuple[Key, ...], layout: HelpLayout) -> list[str]:
|
|
|
45
45
|
lines.append(title)
|
|
46
46
|
for e in table:
|
|
47
47
|
if e.section == title:
|
|
48
|
-
lines.extend(e.
|
|
48
|
+
lines.extend(e.help_lines)
|
|
49
49
|
lines.append("")
|
|
50
50
|
lines.extend(layout.suffix)
|
|
51
51
|
return lines
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/agents.py
RENAMED
|
@@ -79,23 +79,23 @@ class AgentsView(ListTabView):
|
|
|
79
79
|
# App-level FOOTER_PREFIX, so its entry is hint-less.
|
|
80
80
|
KEY_TABLE = (
|
|
81
81
|
Key(("enter", "o"), "Enter/o 接回", "_takeover",
|
|
82
|
-
section="后台 agent 生命周期:",
|
|
82
|
+
section="后台 agent 生命周期:", help_lines=(
|
|
83
83
|
" Enter/o 接回(拉回前台,复用 resume;接运行中的 agent 会先确认接管)",
|
|
84
84
|
)),
|
|
85
|
-
Key(("s",), "s 停止", "_stop", section="后台 agent 生命周期:",
|
|
85
|
+
Key(("s",), "s 停止", "_stop", section="后台 agent 生命周期:", help_lines=(
|
|
86
86
|
" s 停止(仅运行中,需确认)",
|
|
87
87
|
)),
|
|
88
|
-
Key(("d",), "d 删除", "_remove", section="后台 agent 生命周期:",
|
|
88
|
+
Key(("d",), "d 删除", "_remove", section="后台 agent 生命周期:", help_lines=(
|
|
89
89
|
" d 删除(仅已结束)",
|
|
90
90
|
)),
|
|
91
|
-
Key(("w",), "w 查看", "_watch", section="后台 agent 生命周期:",
|
|
91
|
+
Key(("w",), "w 查看", "_watch", section="后台 agent 生命周期:", help_lines=(
|
|
92
92
|
" w 查看 timeline(只读)",
|
|
93
93
|
)),
|
|
94
|
-
Key(("R",), "R 重启", "_key_respawn", section="后台 agent 生命周期:",
|
|
94
|
+
Key(("R",), "R 重启", "_key_respawn", section="后台 agent 生命周期:", help_lines=(
|
|
95
95
|
" R 重启(respawn)",
|
|
96
96
|
)),
|
|
97
97
|
Key(("r",), None, "_key_refresh", needs_selection=False,
|
|
98
|
-
section="后台 agent 生命周期:",
|
|
98
|
+
section="后台 agent 生命周期:", help_lines=(
|
|
99
99
|
" r 刷新",
|
|
100
100
|
)),
|
|
101
101
|
Key(("?",), "? 详细说明", "_show_help", needs_selection=False),
|
|
@@ -126,12 +126,15 @@ class AgentsView(ListTabView):
|
|
|
126
126
|
# --- TabView contract ---
|
|
127
127
|
|
|
128
128
|
def keyhints(self) -> str:
|
|
129
|
-
if self.
|
|
129
|
+
if self._overlay_active():
|
|
130
130
|
# "其余" is honest: the prefix's Tab/q stay global (Tab switches
|
|
131
131
|
# tabs, q QUITS — neither returns to the list).
|
|
132
132
|
return "其余任意键返回"
|
|
133
133
|
return footer_hints(self.KEY_TABLE)
|
|
134
134
|
|
|
135
|
+
def _overlay_active(self) -> bool:
|
|
136
|
+
return self._mode in ("help", "watch")
|
|
137
|
+
|
|
135
138
|
def _enrich(self, jobs: list[AgentJob]) -> list[AgentJob]:
|
|
136
139
|
"""Fill host liveness for the self-fetch path (snapshot already enriched).
|
|
137
140
|
|
|
@@ -185,14 +188,7 @@ class AgentsView(ListTabView):
|
|
|
185
188
|
return widget.job
|
|
186
189
|
return None
|
|
187
190
|
|
|
188
|
-
# --- key dispatch ---
|
|
189
|
-
|
|
190
|
-
def handle_key(self, key: str) -> None:
|
|
191
|
-
if self._mode in ("help", "watch"):
|
|
192
|
-
self._handle_overlay_key(key)
|
|
193
|
-
return
|
|
194
|
-
# Normal list mode — dispatch straight from KEY_TABLE.
|
|
195
|
-
self._dispatch_key(key)
|
|
191
|
+
# --- key handlers (bound by name in KEY_TABLE; dispatch lives in the base) ---
|
|
196
192
|
|
|
197
193
|
def _key_respawn(self, job: AgentJob) -> None:
|
|
198
194
|
cmd = agent_ops.respawn(job)
|
|
@@ -138,35 +138,35 @@ class RCView(ListTabView):
|
|
|
138
138
|
# App-level FOOTER_PREFIX, so its entry is hint-less.
|
|
139
139
|
KEY_TABLE = (
|
|
140
140
|
Key(("t",), "t 新建会话", "_key_tmux_new",
|
|
141
|
-
section="项目操作(仅对「项目」行生效):",
|
|
141
|
+
section="项目操作(仅对「项目」行生效):", help_lines=(
|
|
142
142
|
" t 在项目目录新建 tmux claude 会话并直接进入(离开 csctl)",
|
|
143
143
|
)),
|
|
144
144
|
Key(("enter",), "Enter 启动远控", "_key_start",
|
|
145
|
-
section="项目操作(仅对「项目」行生效):",
|
|
145
|
+
section="项目操作(仅对「项目」行生效):", help_lines=(
|
|
146
146
|
" Enter 启动选中项目的远程控制服务",
|
|
147
147
|
)),
|
|
148
148
|
Key(("s",), "s 停止", "_key_stop",
|
|
149
|
-
section="项目操作(仅对「项目」行生效):",
|
|
149
|
+
section="项目操作(仅对「项目」行生效):", help_lines=(
|
|
150
150
|
" s 停止选中项目的远程控制服务(需确认)",
|
|
151
151
|
)),
|
|
152
152
|
Key(("a",), "a 开机自启", "_key_autostart",
|
|
153
|
-
section="项目操作(仅对「项目」行生效):",
|
|
153
|
+
section="项目操作(仅对「项目」行生效):", help_lines=(
|
|
154
154
|
" a 切换「开机自启」:A 键一键启动时是否带上本项目",
|
|
155
155
|
)),
|
|
156
156
|
Key(("c",), "c 自动远控", "_key_rc_toggle",
|
|
157
|
-
section="项目操作(仅对「项目」行生效):",
|
|
157
|
+
section="项目操作(仅对「项目」行生效):", help_lines=(
|
|
158
158
|
" c 切换「自动远控」:claude 启动时自动开远程控制,手机即可接管",
|
|
159
159
|
)),
|
|
160
160
|
Key(("A",), "A 全部启动", "_key_start_all", needs_selection=False,
|
|
161
|
-
section="批量操作:",
|
|
161
|
+
section="批量操作:", help_lines=(
|
|
162
162
|
" A 启动所有「开机自启」项目",
|
|
163
163
|
)),
|
|
164
164
|
Key(("S",), "S 全部停止", "_key_stop_all", needs_selection=False,
|
|
165
|
-
section="批量操作:",
|
|
165
|
+
section="批量操作:", help_lines=(
|
|
166
166
|
" S 停止全部远程控制服务(需确认)",
|
|
167
167
|
)),
|
|
168
168
|
Key(("r",), None, "_key_refresh", needs_selection=False,
|
|
169
|
-
section="批量操作:",
|
|
169
|
+
section="批量操作:", help_lines=(
|
|
170
170
|
" r 重新扫描刷新",
|
|
171
171
|
)),
|
|
172
172
|
Key(("?",), "? 详细说明", "_show_help", needs_selection=False),
|
|
@@ -198,7 +198,7 @@ class RCView(ListTabView):
|
|
|
198
198
|
self._help = False
|
|
199
199
|
|
|
200
200
|
def keyhints(self) -> str:
|
|
201
|
-
if self.
|
|
201
|
+
if self._overlay_active():
|
|
202
202
|
# "其余" is honest: the prefix's Tab/q stay global (Tab switches
|
|
203
203
|
# tabs, q QUITS — neither returns to the list).
|
|
204
204
|
return "其余任意键返回"
|
|
@@ -206,6 +206,9 @@ class RCView(ListTabView):
|
|
|
206
206
|
# footer Text wraps on narrow terminals (vertical for width).
|
|
207
207
|
return footer_hints(self.KEY_TABLE)
|
|
208
208
|
|
|
209
|
+
def _overlay_active(self) -> bool:
|
|
210
|
+
return self._help
|
|
211
|
+
|
|
209
212
|
def load(self) -> None:
|
|
210
213
|
self._projects = rc.scan()
|
|
211
214
|
self._servers = rc.scan_servers()
|
|
@@ -268,14 +271,7 @@ class RCView(ListTabView):
|
|
|
268
271
|
return widget.project
|
|
269
272
|
return None
|
|
270
273
|
|
|
271
|
-
|
|
272
|
-
if self._help:
|
|
273
|
-
self._handle_overlay_key(key)
|
|
274
|
-
return
|
|
275
|
-
# Normal list mode — dispatch straight from KEY_TABLE.
|
|
276
|
-
self._dispatch_key(key)
|
|
277
|
-
|
|
278
|
-
# --- key handlers (bound by name in KEY_TABLE) ---
|
|
274
|
+
# --- key handlers (bound by name in KEY_TABLE; dispatch lives in the base) ---
|
|
279
275
|
|
|
280
276
|
def _key_tmux_new(self, p: RCProject) -> None:
|
|
281
277
|
if not p.dir_exists:
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/sessions.py
RENAMED
|
@@ -48,44 +48,44 @@ class SessionsView(CleanupMixin, ListTabView):
|
|
|
48
48
|
# table in the footer is a user preference (2026-07-05); `r 刷新` stays in
|
|
49
49
|
# the App-level FOOTER_PREFIX, so its entry is hint-less here.
|
|
50
50
|
KEY_TABLE = (
|
|
51
|
-
Key(("enter",), "Enter 接回", "_key_resume", section="会话操作:",
|
|
51
|
+
Key(("enter",), "Enter 接回", "_key_resume", section="会话操作:", help_lines=(
|
|
52
52
|
" Enter 接回选中的会话(在当前终端恢复;接运行中的会话会先确认接管)",
|
|
53
53
|
)),
|
|
54
|
-
Key(("t",), "t tmux接回", "_key_tmux", section="会话操作:",
|
|
54
|
+
Key(("t",), "t tmux接回", "_key_tmux", section="会话操作:", help_lines=(
|
|
55
55
|
" t tmux 接回(会话恢复进 tmux 窗口并接入前台——终端断线会话不死,",
|
|
56
56
|
" 重连后 tmux attach 可捡回;已在 tmux 中的会话直接接入不重启;",
|
|
57
57
|
" 接运行中的裸终端会话会先确认接管)",
|
|
58
58
|
)),
|
|
59
|
-
Key(("f",), "f 分叉", "_key_fork", section="会话操作:",
|
|
59
|
+
Key(("f",), "f 分叉", "_key_fork", section="会话操作:", help_lines=(
|
|
60
60
|
" f 分叉会话(创建副本后接回,不影响原会话)",
|
|
61
61
|
)),
|
|
62
|
-
Key(("s",), "s 停止", "_key_stop", section="会话操作:",
|
|
62
|
+
Key(("s",), "s 停止", "_key_stop", section="会话操作:", help_lines=(
|
|
63
63
|
" s 停止运行中的会话(发送 SIGTERM,需二次确认)",
|
|
64
64
|
)),
|
|
65
|
-
Key(("R",), "R 转后台", "_key_relaunch", section="会话操作:",
|
|
65
|
+
Key(("R",), "R 转后台", "_key_relaunch", section="会话操作:", help_lines=(
|
|
66
66
|
" R 转入 tmux 后台并开启远控(脱离终端,手机/网页可接管;",
|
|
67
67
|
" 接运行中的会话会先确认接管)",
|
|
68
68
|
)),
|
|
69
|
-
Key(("d",), "d 删除", "_key_delete", section="会话操作:",
|
|
69
|
+
Key(("d",), "d 删除", "_key_delete", section="会话操作:", help_lines=(
|
|
70
70
|
" d 删除已结束的会话记录",
|
|
71
71
|
)),
|
|
72
|
-
Key(("y",), "y 复制命令", "_key_yank", section="会话操作:",
|
|
72
|
+
Key(("y",), "y 复制命令", "_key_yank", section="会话操作:", help_lines=(
|
|
73
73
|
" y 复制接回命令到剪贴板",
|
|
74
74
|
)),
|
|
75
75
|
Key(("h",), "h 桥接显隐", "_key_toggle_hidden", needs_selection=False,
|
|
76
|
-
section="会话操作:",
|
|
76
|
+
section="会话操作:", help_lines=(
|
|
77
77
|
" h 显示/隐藏桥接、SDK 会话",
|
|
78
78
|
)),
|
|
79
79
|
Key(("c",), "c 清理", "_enter_cleanup", needs_selection=False,
|
|
80
|
-
section="清理与过滤:",
|
|
80
|
+
section="清理与过滤:", help_lines=(
|
|
81
81
|
" c 打开清理子菜单",
|
|
82
82
|
)),
|
|
83
83
|
Key(("/",), "/ 过滤", "_enter_filter", needs_selection=False,
|
|
84
|
-
section="清理与过滤:",
|
|
84
|
+
section="清理与过滤:", help_lines=(
|
|
85
85
|
" / 按关键词过滤会话列表",
|
|
86
86
|
)),
|
|
87
87
|
Key(("r",), None, "_key_refresh", needs_selection=False,
|
|
88
|
-
section="清理与过滤:",
|
|
88
|
+
section="清理与过滤:", help_lines=(
|
|
89
89
|
" r 刷新",
|
|
90
90
|
)),
|
|
91
91
|
Key(("?",), "? 详细说明", "_show_help", needs_selection=False),
|
|
@@ -127,7 +127,7 @@ class SessionsView(CleanupMixin, ListTabView):
|
|
|
127
127
|
self._cleanup_walker = urwid.SimpleFocusListWalker([])
|
|
128
128
|
|
|
129
129
|
def keyhints(self) -> str:
|
|
130
|
-
if self.
|
|
130
|
+
if self._overlay_active():
|
|
131
131
|
# "其余" is honest: the prefix's Tab/q stay global (Tab switches
|
|
132
132
|
# tabs, q QUITS — neither returns to the list).
|
|
133
133
|
return "其余任意键返回"
|
|
@@ -362,11 +362,12 @@ class SessionsView(CleanupMixin, ListTabView):
|
|
|
362
362
|
|
|
363
363
|
# --- Key dispatch ---
|
|
364
364
|
|
|
365
|
-
def
|
|
366
|
-
|
|
367
|
-
self._handle_overlay_key(key)
|
|
368
|
-
return
|
|
365
|
+
def _overlay_active(self) -> bool:
|
|
366
|
+
return self._mode == "help"
|
|
369
367
|
|
|
368
|
+
def handle_key(self, key: str) -> None:
|
|
369
|
+
"""Extra modes first (filter/preview/cleanup are Sessions-only); the
|
|
370
|
+
help overlay + list dispatch fall through to the base handle_key."""
|
|
370
371
|
if self._mode == "filter":
|
|
371
372
|
if key == "enter":
|
|
372
373
|
self._exit_filter()
|
|
@@ -398,8 +399,8 @@ class SessionsView(CleanupMixin, ListTabView):
|
|
|
398
399
|
self.app.refresh_with_notice()
|
|
399
400
|
return
|
|
400
401
|
|
|
401
|
-
#
|
|
402
|
-
|
|
402
|
+
# Help overlay + normal list mode: the base handle_key.
|
|
403
|
+
super().handle_key(key)
|
|
403
404
|
|
|
404
405
|
# --- key handlers (bound by name in KEY_TABLE) ---
|
|
405
406
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/__init__.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/agent_ops.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/resume_list.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/session_ops.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/actions/skill_ops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/__init__.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/cleanup.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/environments.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/liveness.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/registry.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/sessions.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/data/snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/__init__.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_colspec.py
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_confirm.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control/views/_session_row.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/requires.txt
RENAMED
|
File without changes
|
{cc_session_control-0.6.2 → cc_session_control-0.6.3}/src/cc_session_control.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|