snapstep 0.2.0__tar.gz → 0.2.2__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.
- {snapstep-0.2.0/src/snapstep.egg-info → snapstep-0.2.2}/PKG-INFO +1 -1
- {snapstep-0.2.0 → snapstep-0.2.2}/pyproject.toml +1 -1
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/__init__.py +1 -1
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/cli.py +34 -3
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/config.py +1 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/settings.py +22 -0
- {snapstep-0.2.0 → snapstep-0.2.2/src/snapstep.egg-info}/PKG-INFO +1 -1
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/SOURCES.txt +1 -0
- snapstep-0.2.2/tests/test_export_dir.py +63 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/LICENSE +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/README.md +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/setup.cfg +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/__main__.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/capture.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/__init__.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/docx_export.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/html_export.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/markdown_export.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/log.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/models.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/privacy.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/recorder.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon.ico +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon.png +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon_rec.png +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/templates/report.html.j2 +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/__init__.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/tray.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/writer.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/dependency_links.txt +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/entry_points.txt +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/requires.txt +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/top_level.txt +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_config_recorder.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_export.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_finalize.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_imports.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_models.py +0 -0
- {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_writer.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "snapstep"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "录屏自动生成图文教程 / Record your screen, get a step-by-step guide with screenshots — automatically."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -21,12 +21,35 @@ from .models import Session
|
|
|
21
21
|
from .writer import AIWriter, TemplateWriter, apply_copy
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def resolve_export_dir(
|
|
25
|
+
cfg: Config, session_dir: Path, session: Session, explicit: Path | None = None
|
|
26
|
+
) -> Path:
|
|
27
|
+
"""决定导出目录:显式指定 > 配置项 > 默认 <会话目录>/export。
|
|
28
|
+
|
|
29
|
+
配置的自定义目录按会话建子文件夹(SnapStep-<时间戳>),避免多次导出互相覆盖。
|
|
30
|
+
"""
|
|
31
|
+
if explicit is not None:
|
|
32
|
+
return explicit
|
|
33
|
+
raw = (cfg.export.dir or "").strip()
|
|
34
|
+
if not raw:
|
|
35
|
+
return session_dir / "export"
|
|
36
|
+
base = Path(raw).expanduser()
|
|
37
|
+
if not base.is_absolute():
|
|
38
|
+
base = Path.home() / base
|
|
39
|
+
ts = session.created_at or ""
|
|
40
|
+
slug = ts[:10].replace("-", "") + "-" + ts[11:19].replace(":", "")
|
|
41
|
+
if not slug.strip("-"):
|
|
42
|
+
slug = session.id
|
|
43
|
+
return base / f"SnapStep-{slug}"
|
|
44
|
+
|
|
45
|
+
|
|
24
46
|
def generate_and_export(
|
|
25
47
|
session: Session,
|
|
26
48
|
session_dir: Path,
|
|
27
49
|
cfg: Config,
|
|
28
50
|
fmt: str | None = None,
|
|
29
51
|
use_ai: bool | None = None,
|
|
52
|
+
out_dir: Path | None = None,
|
|
30
53
|
) -> tuple[str, list[Path]]:
|
|
31
54
|
"""生成文案并导出。CLI 与 GUI 共用。返回 (writer 名称, 导出文件列表)。"""
|
|
32
55
|
from .export import export_session
|
|
@@ -39,8 +62,13 @@ def generate_and_export(
|
|
|
39
62
|
)
|
|
40
63
|
used = apply_copy(session, TemplateWriter(cfg.api.language), ai)
|
|
41
64
|
session.save(session_dir)
|
|
65
|
+
target = resolve_export_dir(cfg, session_dir, session, out_dir)
|
|
42
66
|
paths = export_session(
|
|
43
|
-
session,
|
|
67
|
+
session,
|
|
68
|
+
session_dir,
|
|
69
|
+
fmt or cfg.export.format,
|
|
70
|
+
target,
|
|
71
|
+
cfg.export.embed_images,
|
|
44
72
|
)
|
|
45
73
|
return used, paths
|
|
46
74
|
|
|
@@ -158,7 +186,10 @@ def _valid_keys(cfg: Config) -> list[str]:
|
|
|
158
186
|
|
|
159
187
|
def cmd_demo(args, cfg: Config) -> int:
|
|
160
188
|
session = _build_demo_session()
|
|
161
|
-
|
|
189
|
+
# 默认输出到用户主目录:相对路径会在 system32 等只读目录里炸掉
|
|
190
|
+
session_dir = (
|
|
191
|
+
Path(args.out).expanduser() if args.out else Path.home() / "snapstep-demo"
|
|
192
|
+
)
|
|
162
193
|
_render_demo_screenshots(session, session_dir)
|
|
163
194
|
used, paths = generate_and_export(
|
|
164
195
|
session, session_dir, cfg, fmt=args.format, use_ai=not args.no_ai
|
|
@@ -272,7 +303,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
272
303
|
p_config.add_argument("value", nargs="?", help="set 时的新值")
|
|
273
304
|
|
|
274
305
|
p_demo = sub.add_parser("demo", help="生成示例教程,验证安装")
|
|
275
|
-
p_demo.add_argument("--out",
|
|
306
|
+
p_demo.add_argument("--out", help="输出目录(默认 ~/snapstep-demo)")
|
|
276
307
|
p_demo.add_argument("--format", choices=["md", "html", "docx", "all"])
|
|
277
308
|
p_demo.add_argument("--no-ai", action="store_true")
|
|
278
309
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
5
7
|
from ..config import API_PRESETS, Config, load_config, save_config
|
|
6
8
|
|
|
7
9
|
|
|
@@ -92,6 +94,16 @@ class SettingsDialog:
|
|
|
92
94
|
self.combo_format.addItems(["html", "md", "docx"])
|
|
93
95
|
self.combo_format.setCurrentText(self.cfg.export.format)
|
|
94
96
|
form_export.addRow("默认格式", self.combo_format)
|
|
97
|
+
self.edit_export_dir = QLineEdit(self.cfg.export.dir)
|
|
98
|
+
self.edit_export_dir.setPlaceholderText("默认:<会话目录>\\export")
|
|
99
|
+
from PySide6.QtWidgets import QPushButton
|
|
100
|
+
|
|
101
|
+
row_dir = QHBoxLayout()
|
|
102
|
+
row_dir.addWidget(self.edit_export_dir)
|
|
103
|
+
btn_browse = QPushButton("浏览…")
|
|
104
|
+
btn_browse.clicked.connect(self._browse_export_dir)
|
|
105
|
+
row_dir.addWidget(btn_browse)
|
|
106
|
+
form_export.addRow("导出目录", row_dir)
|
|
95
107
|
self.check_embed = QCheckBox("HTML 内嵌截图(单文件,方便分享)")
|
|
96
108
|
self.check_embed.setChecked(self.cfg.export.embed_images)
|
|
97
109
|
form_export.addRow(self.check_embed)
|
|
@@ -117,6 +129,15 @@ class SettingsDialog:
|
|
|
117
129
|
|
|
118
130
|
self._dialog = dialog
|
|
119
131
|
|
|
132
|
+
def _browse_export_dir(self) -> None:
|
|
133
|
+
from PySide6.QtWidgets import QFileDialog
|
|
134
|
+
|
|
135
|
+
chosen = QFileDialog.getExistingDirectory(
|
|
136
|
+
self._dialog, "选择导出目录", self.edit_export_dir.text() or str(Path.home())
|
|
137
|
+
)
|
|
138
|
+
if chosen:
|
|
139
|
+
self.edit_export_dir.setText(chosen)
|
|
140
|
+
|
|
120
141
|
def _apply_preset(self, name: str) -> None:
|
|
121
142
|
preset = API_PRESETS.get(name)
|
|
122
143
|
if not preset:
|
|
@@ -145,5 +166,6 @@ class SettingsDialog:
|
|
|
145
166
|
self.cfg.api.language = self.combo_lang.currentText()
|
|
146
167
|
self.cfg.export.format = self.combo_format.currentText()
|
|
147
168
|
self.cfg.export.embed_images = self.check_embed.isChecked()
|
|
169
|
+
self.cfg.export.dir = self.edit_export_dir.text().strip()
|
|
148
170
|
save_config(self.cfg)
|
|
149
171
|
return True
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""自定义导出目录:解析规则与端到端导出落点。"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from snapstep.cli import (
|
|
6
|
+
_build_demo_session,
|
|
7
|
+
_render_demo_screenshots,
|
|
8
|
+
generate_and_export,
|
|
9
|
+
resolve_export_dir,
|
|
10
|
+
)
|
|
11
|
+
from snapstep.config import Config
|
|
12
|
+
from snapstep.models import Session
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_resolve_default(tmp_path):
|
|
16
|
+
session = Session(created_at="2026-09-12T10:34:20")
|
|
17
|
+
cfg = Config() # dir 为空
|
|
18
|
+
assert (
|
|
19
|
+
resolve_export_dir(cfg, tmp_path, session) == tmp_path / "export"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_resolve_custom_absolute(tmp_path):
|
|
24
|
+
session = Session(created_at="2026-09-12T10:34:20")
|
|
25
|
+
cfg = Config()
|
|
26
|
+
cfg.export.dir = str(tmp_path / "outbox")
|
|
27
|
+
target = resolve_export_dir(cfg, tmp_path, session)
|
|
28
|
+
assert target == tmp_path / "outbox" / "SnapStep-20260912-103420"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_resolve_relative_based_on_home(tmp_path):
|
|
32
|
+
session = Session(created_at="2026-09-12T10:34:20")
|
|
33
|
+
cfg = Config()
|
|
34
|
+
cfg.export.dir = "docs/tutorials"
|
|
35
|
+
target = resolve_export_dir(cfg, tmp_path, session)
|
|
36
|
+
assert target == Path.home() / "docs" / "tutorials" / "SnapStep-20260912-103420"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_explicit_wins_over_config(tmp_path):
|
|
40
|
+
session = Session(created_at="2026-09-12T10:34:20")
|
|
41
|
+
cfg = Config()
|
|
42
|
+
cfg.export.dir = str(tmp_path / "outbox")
|
|
43
|
+
assert (
|
|
44
|
+
resolve_export_dir(cfg, tmp_path, session, explicit=tmp_path / "x")
|
|
45
|
+
== tmp_path / "x"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_generate_and_export_uses_custom_dir(tmp_path):
|
|
50
|
+
session = _build_demo_session()
|
|
51
|
+
session.created_at = "2026-09-12T10:34:20"
|
|
52
|
+
session_dir = tmp_path / "sess"
|
|
53
|
+
_render_demo_screenshots(session, session_dir)
|
|
54
|
+
cfg = Config()
|
|
55
|
+
cfg.export.dir = str(tmp_path / "outbox")
|
|
56
|
+
|
|
57
|
+
used, paths = generate_and_export(session, session_dir, cfg, fmt="md")
|
|
58
|
+
|
|
59
|
+
assert used in ("ai", "template")
|
|
60
|
+
target = tmp_path / "outbox" / "SnapStep-20260912-103420"
|
|
61
|
+
assert (target / "guide.md").exists()
|
|
62
|
+
for p in paths:
|
|
63
|
+
assert str(p).startswith(str(target))
|
|
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
|