pyquipu-cli 0.2.0__tar.gz → 0.2.1__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.
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/PKG-INFO +5 -5
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/pyproject.toml +5 -5
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/workspace.py +1 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/tui.py +14 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_tui_reachability.py +7 -7
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_view_model.py +3 -3
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/.gitignore +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/__init__.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/__init__.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/axon.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/cache.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/cache.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/export.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/export.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/helpers.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/helpers.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/navigation.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/query.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/query.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/remote.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/run.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/show.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/show.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/commands/ui.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/config.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/config.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/logger_config.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/logger_config.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/main.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/rendering.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/rendering.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/tui.css +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/tui.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/ui_utils.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/ui_utils.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/view_model.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/src/quipu/cli/view_model.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/stitcher.lock +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/conftest.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_cache_commands.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_cache_commands.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_cli_interaction.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_cli_interaction.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_export_command.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_export_command.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_navigation_commands.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_query_commands.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_query_commands.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_unfriendly_paths.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_unfriendly_paths.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_workspace_commands.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_tui_logic.py +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_tui_logic.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_tui_reachability.stitcher.yaml +0 -0
- {pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/unit/test_view_model.stitcher.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyquipu-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: The command-line interface for Quipu.
|
|
5
5
|
Project-URL: Homepage, https://github.com/quipu-dev/Quipu
|
|
6
6
|
Project-URL: Repository, https://github.com/quipu-dev/Quipu
|
|
@@ -20,11 +20,11 @@ Classifier: Topic :: Software Development :: Version Control
|
|
|
20
20
|
Classifier: Topic :: Text Processing
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
|
-
Requires-Dist: pyquipu-application~=0.2.
|
|
23
|
+
Requires-Dist: pyquipu-application~=0.2.1
|
|
24
24
|
Requires-Dist: pyquipu-bus~=0.2.0
|
|
25
25
|
Requires-Dist: pyquipu-common~=0.1.1
|
|
26
|
-
Requires-Dist: pyquipu-engine~=0.2.
|
|
27
|
-
Requires-Dist: pyquipu-runtime~=0.2.
|
|
28
|
-
Requires-Dist: pyquipu-spec~=0.1.
|
|
26
|
+
Requires-Dist: pyquipu-engine~=0.2.1
|
|
27
|
+
Requires-Dist: pyquipu-runtime~=0.2.1
|
|
28
|
+
Requires-Dist: pyquipu-spec~=0.1.1
|
|
29
29
|
Requires-Dist: textual>=0.58.0
|
|
30
30
|
Requires-Dist: typer>=0.9.0
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyquipu-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
authors = [{ name="doucx", email="doucxldh@gmail.com" }]
|
|
9
9
|
description = "The command-line interface for Quipu."
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -25,11 +25,11 @@ classifiers = [
|
|
|
25
25
|
keywords = ["quipu", "devops", "cli", "tui", "git", "history"]
|
|
26
26
|
|
|
27
27
|
dependencies = [
|
|
28
|
-
"pyquipu-spec ~= 0.1.
|
|
29
|
-
"pyquipu-engine ~= 0.2.
|
|
30
|
-
"pyquipu-runtime ~= 0.2.
|
|
28
|
+
"pyquipu-spec ~= 0.1.1",
|
|
29
|
+
"pyquipu-engine ~= 0.2.1",
|
|
30
|
+
"pyquipu-runtime ~= 0.2.1",
|
|
31
31
|
"pyquipu-common ~= 0.1.1",
|
|
32
|
-
"pyquipu-application ~= 0.2.
|
|
32
|
+
"pyquipu-application ~= 0.2.1",
|
|
33
33
|
"pyquipu-bus ~= 0.2.0",
|
|
34
34
|
"typer >= 0.9.0",
|
|
35
35
|
"textual >= 0.58.0",
|
|
@@ -74,6 +74,7 @@ def register(app: typer.Typer):
|
|
|
74
74
|
bus.success("workspace.discard.noChanges", short_hash=latest_node.short_hash)
|
|
75
75
|
ctx.exit(0)
|
|
76
76
|
|
|
77
|
+
assert target_tree_hash is not None
|
|
77
78
|
diff_stat_str = engine.git_db.get_diff_stat(target_tree_hash, current_hash)
|
|
78
79
|
|
|
79
80
|
if not force:
|
|
@@ -102,10 +102,12 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
102
102
|
self.engine.close()
|
|
103
103
|
|
|
104
104
|
def _update_header(self):
|
|
105
|
+
assert self.view_model is not None
|
|
105
106
|
mode = "Markdown" if self.markdown_enabled else "Raw Text"
|
|
106
107
|
self.sub_title = f"Page {self.view_model.current_page} / {self.view_model.total_pages} | View: {mode} (m)"
|
|
107
108
|
|
|
108
109
|
def _load_page(self, page_number: int) -> None:
|
|
110
|
+
assert self.view_model is not None
|
|
109
111
|
logger.debug(f"TUI: Loading page {page_number}")
|
|
110
112
|
self.view_model.load_page(page_number)
|
|
111
113
|
logger.debug(f"TUI: Page {page_number} loaded with {len(self.view_model.current_page_nodes)} nodes.")
|
|
@@ -124,6 +126,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
124
126
|
self.query_one(DataTable).action_cursor_down()
|
|
125
127
|
|
|
126
128
|
def action_toggle_hidden(self) -> None:
|
|
129
|
+
assert self.view_model is not None
|
|
127
130
|
self.view_model.toggle_unreachable()
|
|
128
131
|
self._refresh_table()
|
|
129
132
|
|
|
@@ -138,29 +141,34 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
138
141
|
pass
|
|
139
142
|
|
|
140
143
|
def action_checkout_node(self) -> None:
|
|
144
|
+
assert self.view_model is not None
|
|
141
145
|
selected_node = self.view_model.get_selected_node()
|
|
142
146
|
if selected_node:
|
|
143
147
|
self.exit(result=("checkout", selected_node.output_tree))
|
|
144
148
|
|
|
145
149
|
def action_dump_content(self) -> None:
|
|
150
|
+
assert self.view_model is not None
|
|
146
151
|
selected_node = self.view_model.get_selected_node()
|
|
147
152
|
if selected_node:
|
|
148
153
|
content = self.view_model.get_public_content(selected_node)
|
|
149
154
|
self.exit(result=("dump", content))
|
|
150
155
|
|
|
151
156
|
def action_previous_page(self) -> None:
|
|
157
|
+
assert self.view_model is not None
|
|
152
158
|
if self.view_model.current_page > 1:
|
|
153
159
|
self._load_page(self.view_model.current_page - 1)
|
|
154
160
|
else:
|
|
155
161
|
self.bell()
|
|
156
162
|
|
|
157
163
|
def action_next_page(self) -> None:
|
|
164
|
+
assert self.view_model is not None
|
|
158
165
|
if self.view_model.current_page < self.view_model.total_pages:
|
|
159
166
|
self._load_page(self.view_model.current_page + 1)
|
|
160
167
|
else:
|
|
161
168
|
self.bell()
|
|
162
169
|
|
|
163
170
|
def _refresh_table(self):
|
|
171
|
+
assert self.view_model is not None
|
|
164
172
|
table = self.query_one(DataTable)
|
|
165
173
|
table.clear()
|
|
166
174
|
# 从 ViewModel 获取要渲染的节点
|
|
@@ -170,6 +178,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
170
178
|
self._update_header()
|
|
171
179
|
|
|
172
180
|
def _populate_table(self, table: DataTable, nodes: List[QuipuNode]):
|
|
181
|
+
assert self.view_model is not None
|
|
173
182
|
# 移除了过滤逻辑,因为 ViewModel 已经处理
|
|
174
183
|
tracks: list[Optional[str]] = []
|
|
175
184
|
|
|
@@ -228,6 +237,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
228
237
|
return node.summary or "No description"
|
|
229
238
|
|
|
230
239
|
def _focus_current_node(self, table: DataTable):
|
|
240
|
+
assert self.view_model is not None
|
|
231
241
|
current_output_tree_hash = self.view_model.current_output_tree_hash
|
|
232
242
|
logger.debug(f"DEBUG: Attempting focus. HEAD={current_output_tree_hash}")
|
|
233
243
|
|
|
@@ -273,6 +283,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
273
283
|
logger.error(f"DEBUG: Failed to focus current node: {e}", exc_info=True)
|
|
274
284
|
|
|
275
285
|
def _update_loading_preview(self):
|
|
286
|
+
assert self.view_model is not None
|
|
276
287
|
node = self.view_model.get_selected_node()
|
|
277
288
|
if not node:
|
|
278
289
|
return
|
|
@@ -321,6 +332,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
321
332
|
|
|
322
333
|
case ContentViewSate.SHOWING_CONTENT:
|
|
323
334
|
container.set_class(True, "split-mode")
|
|
335
|
+
assert self.view_model is not None
|
|
324
336
|
node = self.view_model.get_selected_node()
|
|
325
337
|
|
|
326
338
|
if node:
|
|
@@ -341,6 +353,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
341
353
|
|
|
342
354
|
@on(DataTable.RowHighlighted)
|
|
343
355
|
def on_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
|
|
356
|
+
assert self.view_model is not None
|
|
344
357
|
# 1. Update data model
|
|
345
358
|
if event.row_key.value:
|
|
346
359
|
self.view_model.select_node_by_key(event.row_key.value)
|
|
@@ -367,6 +380,7 @@ class QuipuUiApp(App[Optional[UiResult]]):
|
|
|
367
380
|
|
|
368
381
|
def action_toggle_view(self) -> None:
|
|
369
382
|
if self.content_view_state == ContentViewSate.HIDDEN:
|
|
383
|
+
assert self.view_model is not None
|
|
370
384
|
# If a node is selected, transition to loading, otherwise do nothing
|
|
371
385
|
if self.view_model.get_selected_node():
|
|
372
386
|
self._set_state(ContentViewSate.LOADING)
|
|
@@ -55,14 +55,14 @@ class TestUiReachability:
|
|
|
55
55
|
|
|
56
56
|
ancestors = {"a", "root"}
|
|
57
57
|
view_model = view_model_factory([node_root, node_a, node_b], current_hash="a", ancestors=ancestors)
|
|
58
|
-
app = QuipuUiApp(work_dir=Path("."))
|
|
59
|
-
app.view_model = view_model
|
|
60
|
-
app.show_unreachable = False
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
# 模拟 TUI 切换视图以隐藏不可达节点
|
|
60
|
+
# ViewModel 默认 show_unreachable=True
|
|
61
|
+
view_model.toggle_unreachable()
|
|
62
|
+
assert view_model.show_unreachable is False
|
|
63
|
+
|
|
64
|
+
view_model.load_page(1)
|
|
65
|
+
rendered_nodes = view_model.get_nodes_to_render()
|
|
66
66
|
|
|
67
67
|
assert node_b not in rendered_nodes
|
|
68
68
|
assert node_a in rendered_nodes
|
|
@@ -12,9 +12,9 @@ class MockHistoryReader(HistoryReader):
|
|
|
12
12
|
def __init__(
|
|
13
13
|
self,
|
|
14
14
|
nodes: List[QuipuNode],
|
|
15
|
-
ancestors: Set[str] = None,
|
|
16
|
-
descendants: Set[str] = None,
|
|
17
|
-
private_data: dict[str, str] = None,
|
|
15
|
+
ancestors: Optional[Set[str]] = None,
|
|
16
|
+
descendants: Optional[Set[str]] = None,
|
|
17
|
+
private_data: Optional[dict[str, str]] = None,
|
|
18
18
|
):
|
|
19
19
|
self._nodes = sorted(nodes, key=lambda n: n.timestamp, reverse=True)
|
|
20
20
|
self._ancestors = ancestors or set()
|
|
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
|
|
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
|
{pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_cli_interaction.stitcher.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyquipu_cli-0.2.0 → pyquipu_cli-0.2.1}/tests/integration/test_unfriendly_paths.stitcher.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|