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.
Files changed (39) hide show
  1. {snapstep-0.2.0/src/snapstep.egg-info → snapstep-0.2.2}/PKG-INFO +1 -1
  2. {snapstep-0.2.0 → snapstep-0.2.2}/pyproject.toml +1 -1
  3. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/__init__.py +1 -1
  4. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/cli.py +34 -3
  5. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/config.py +1 -0
  6. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/settings.py +22 -0
  7. {snapstep-0.2.0 → snapstep-0.2.2/src/snapstep.egg-info}/PKG-INFO +1 -1
  8. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/SOURCES.txt +1 -0
  9. snapstep-0.2.2/tests/test_export_dir.py +63 -0
  10. {snapstep-0.2.0 → snapstep-0.2.2}/LICENSE +0 -0
  11. {snapstep-0.2.0 → snapstep-0.2.2}/README.md +0 -0
  12. {snapstep-0.2.0 → snapstep-0.2.2}/setup.cfg +0 -0
  13. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/__main__.py +0 -0
  14. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/capture.py +0 -0
  15. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/__init__.py +0 -0
  16. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/docx_export.py +0 -0
  17. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/html_export.py +0 -0
  18. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/export/markdown_export.py +0 -0
  19. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/log.py +0 -0
  20. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/models.py +0 -0
  21. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/privacy.py +0 -0
  22. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/recorder.py +0 -0
  23. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon.ico +0 -0
  24. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon.png +0 -0
  25. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/icon_rec.png +0 -0
  26. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/resources/templates/report.html.j2 +0 -0
  27. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/__init__.py +0 -0
  28. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/ui/tray.py +0 -0
  29. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep/writer.py +0 -0
  30. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/dependency_links.txt +0 -0
  31. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/entry_points.txt +0 -0
  32. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/requires.txt +0 -0
  33. {snapstep-0.2.0 → snapstep-0.2.2}/src/snapstep.egg-info/top_level.txt +0 -0
  34. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_config_recorder.py +0 -0
  35. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_export.py +0 -0
  36. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_finalize.py +0 -0
  37. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_imports.py +0 -0
  38. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_models.py +0 -0
  39. {snapstep-0.2.0 → snapstep-0.2.2}/tests/test_writer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snapstep
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: 录屏自动生成图文教程 / Record your screen, get a step-by-step guide with screenshots — automatically.
5
5
  Author: Felixssss-106
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "snapstep"
7
- version = "0.2.0"
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"
@@ -1,3 +1,3 @@
1
1
  """SnapStep — 录屏自动生成图文教程。"""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -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, session_dir, fmt or cfg.export.format, None, cfg.export.embed_images
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
- session_dir = Path(args.out)
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", default="snapstep-demo")
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
 
@@ -49,6 +49,7 @@ class ApiConfig:
49
49
  class ExportConfig:
50
50
  format: str = "html" # md / html / docx
51
51
  embed_images: bool = True # HTML 单文件内嵌 base64,方便直接分享
52
+ dir: str = "" # 自定义导出目录(空 = 默认 <会话目录>/export);支持 ~,相对路径基于用户主目录
52
53
 
53
54
 
54
55
  @dataclass
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snapstep
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: 录屏自动生成图文教程 / Record your screen, get a step-by-step guide with screenshots — automatically.
5
5
  Author: Felixssss-106
6
6
  License-Expression: MIT
@@ -30,6 +30,7 @@ src/snapstep/ui/settings.py
30
30
  src/snapstep/ui/tray.py
31
31
  tests/test_config_recorder.py
32
32
  tests/test_export.py
33
+ tests/test_export_dir.py
33
34
  tests/test_finalize.py
34
35
  tests/test_imports.py
35
36
  tests/test_models.py
@@ -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