clip2table 0.1.0__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 (153) hide show
  1. clip2table-0.1.0/CHANGELOG.md +45 -0
  2. clip2table-0.1.0/LICENSE +21 -0
  3. clip2table-0.1.0/MANIFEST.in +22 -0
  4. clip2table-0.1.0/NOTICE.md +114 -0
  5. clip2table-0.1.0/PKG-INFO +220 -0
  6. clip2table-0.1.0/README.md +191 -0
  7. clip2table-0.1.0/SKILL.md +202 -0
  8. clip2table-0.1.0/_dev/c2t.py +13 -0
  9. clip2table-0.1.0/docs/architecture.md +252 -0
  10. clip2table-0.1.0/docs/design-notes.md +210 -0
  11. clip2table-0.1.0/docs/history/PROBE-RESULTS.md +45 -0
  12. clip2table-0.1.0/docs/history/VERIFY.md +107 -0
  13. clip2table-0.1.0/docs/history/clip2table-plan-v2.md +612 -0
  14. clip2table-0.1.0/docs/publishing.md +81 -0
  15. clip2table-0.1.0/docs/releases/v0.1.0.md +62 -0
  16. clip2table-0.1.0/docs/usage.md +372 -0
  17. clip2table-0.1.0/docs/verification.md +281 -0
  18. clip2table-0.1.0/fixtures/sample-desktop-notepad.txt +44 -0
  19. clip2table-0.1.0/fixtures/sample-ecom.txt +17 -0
  20. clip2table-0.1.0/fixtures/sample-excel-range.txt +11 -0
  21. clip2table-0.1.0/fixtures/sample-gov.txt +20 -0
  22. clip2table-0.1.0/fixtures/sample-hr.txt +23 -0
  23. clip2table-0.1.0/fixtures/sample-numbered.txt +4 -0
  24. clip2table-0.1.0/fixtures/sample-prose.txt +4 -0
  25. clip2table-0.1.0/fixtures/sample-table.html +6 -0
  26. clip2table-0.1.0/pyproject.toml +53 -0
  27. clip2table-0.1.0/setup.cfg +4 -0
  28. clip2table-0.1.0/src/clip2table/__init__.py +11 -0
  29. clip2table-0.1.0/src/clip2table/__main__.py +11 -0
  30. clip2table-0.1.0/src/clip2table/capabilities.py +86 -0
  31. clip2table-0.1.0/src/clip2table/cards/__init__.py +1 -0
  32. clip2table-0.1.0/src/clip2table/cards/builtin/admin-order-list.toml +55 -0
  33. clip2table-0.1.0/src/clip2table/cards/builtin/generic-item-block.toml +57 -0
  34. clip2table-0.1.0/src/clip2table/cards/builtin/job-list.toml +54 -0
  35. clip2table-0.1.0/src/clip2table/cards/builtin/notice-list.toml +51 -0
  36. clip2table-0.1.0/src/clip2table/cards/builtin/numbered-list.toml +47 -0
  37. clip2table-0.1.0/src/clip2table/cards/builtin/samples/admin-order-list/orders.tsv.expect.tsv +6 -0
  38. clip2table-0.1.0/src/clip2table/cards/builtin/samples/admin-order-list/orders.tsv.txt +6 -0
  39. clip2table-0.1.0/src/clip2table/cards/builtin/samples/generic-item-block/gov-list.expect.tsv +4 -0
  40. clip2table-0.1.0/src/clip2table/cards/builtin/samples/generic-item-block/gov-list.txt +20 -0
  41. clip2table-0.1.0/src/clip2table/cards/builtin/samples/job-list/hr-roles.expect.tsv +5 -0
  42. clip2table-0.1.0/src/clip2table/cards/builtin/samples/job-list/hr-roles.txt +23 -0
  43. clip2table-0.1.0/src/clip2table/cards/builtin/samples/notice-list/list-page.expect.tsv +4 -0
  44. clip2table-0.1.0/src/clip2table/cards/builtin/samples/notice-list/list-page.txt +17 -0
  45. clip2table-0.1.0/src/clip2table/cards/builtin/samples/numbered-list/project-list.expect.tsv +5 -0
  46. clip2table-0.1.0/src/clip2table/cards/builtin/samples/numbered-list/project-list.txt +4 -0
  47. clip2table-0.1.0/src/clip2table/cards/loader.py +50 -0
  48. clip2table-0.1.0/src/clip2table/cards/regress.py +184 -0
  49. clip2table-0.1.0/src/clip2table/cards/schema.py +299 -0
  50. clip2table-0.1.0/src/clip2table/channels/__init__.py +1 -0
  51. clip2table-0.1.0/src/clip2table/channels/cdp.py +299 -0
  52. clip2table-0.1.0/src/clip2table/channels/cfhtml.py +144 -0
  53. clip2table-0.1.0/src/clip2table/channels/clipboard.py +137 -0
  54. clip2table-0.1.0/src/clip2table/channels/http.py +169 -0
  55. clip2table-0.1.0/src/clip2table/channels/keyboard.py +492 -0
  56. clip2table-0.1.0/src/clip2table/channels/ws.py +147 -0
  57. clip2table-0.1.0/src/clip2table/cli.py +1106 -0
  58. clip2table-0.1.0/src/clip2table/core.py +135 -0
  59. clip2table-0.1.0/src/clip2table/dataset.py +1358 -0
  60. clip2table-0.1.0/src/clip2table/errors.py +35 -0
  61. clip2table-0.1.0/src/clip2table/flow.py +224 -0
  62. clip2table-0.1.0/src/clip2table/harvest.py +982 -0
  63. clip2table-0.1.0/src/clip2table/ladder.py +229 -0
  64. clip2table-0.1.0/src/clip2table/models.py +205 -0
  65. clip2table-0.1.0/src/clip2table/pipeline/__init__.py +1 -0
  66. clip2table-0.1.0/src/clip2table/pipeline/assemble.py +116 -0
  67. clip2table-0.1.0/src/clip2table/pipeline/extract.py +524 -0
  68. clip2table-0.1.0/src/clip2table/pipeline/htmltable.py +243 -0
  69. clip2table-0.1.0/src/clip2table/pipeline/infer.py +461 -0
  70. clip2table-0.1.0/src/clip2table/pipeline/normalize.py +61 -0
  71. clip2table-0.1.0/src/clip2table/pipeline/segment.py +539 -0
  72. clip2table-0.1.0/src/clip2table/pipeline/sniff.py +80 -0
  73. clip2table-0.1.0/src/clip2table/render/__init__.py +1 -0
  74. clip2table-0.1.0/src/clip2table/render/base.py +65 -0
  75. clip2table-0.1.0/src/clip2table/render/text.py +77 -0
  76. clip2table-0.1.0/src/clip2table/render/xlsx.py +213 -0
  77. clip2table-0.1.0/src/clip2table/report.py +267 -0
  78. clip2table-0.1.0/src/clip2table.egg-info/PKG-INFO +220 -0
  79. clip2table-0.1.0/src/clip2table.egg-info/SOURCES.txt +151 -0
  80. clip2table-0.1.0/src/clip2table.egg-info/dependency_links.txt +1 -0
  81. clip2table-0.1.0/src/clip2table.egg-info/entry_points.txt +2 -0
  82. clip2table-0.1.0/src/clip2table.egg-info/requires.txt +3 -0
  83. clip2table-0.1.0/src/clip2table.egg-info/top_level.txt +1 -0
  84. clip2table-0.1.0/tests/__init__.py +11 -0
  85. clip2table-0.1.0/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  86. clip2table-0.1.0/tests/__pycache__/test_assemble.cpython-311.pyc +0 -0
  87. clip2table-0.1.0/tests/__pycache__/test_card_binding.cpython-311.pyc +0 -0
  88. clip2table-0.1.0/tests/__pycache__/test_cards.cpython-311.pyc +0 -0
  89. clip2table-0.1.0/tests/__pycache__/test_cdp_hint.cpython-311.pyc +0 -0
  90. clip2table-0.1.0/tests/__pycache__/test_cfhtml.cpython-311.pyc +0 -0
  91. clip2table-0.1.0/tests/__pycache__/test_cli.cpython-311.pyc +0 -0
  92. clip2table-0.1.0/tests/__pycache__/test_clipboard.cpython-311.pyc +0 -0
  93. clip2table-0.1.0/tests/__pycache__/test_conflicts.cpython-311.pyc +0 -0
  94. clip2table-0.1.0/tests/__pycache__/test_dataset.cpython-311.pyc +0 -0
  95. clip2table-0.1.0/tests/__pycache__/test_extract.cpython-311.pyc +0 -0
  96. clip2table-0.1.0/tests/__pycache__/test_extract_line.cpython-311.pyc +0 -0
  97. clip2table-0.1.0/tests/__pycache__/test_field_trace.cpython-311.pyc +0 -0
  98. clip2table-0.1.0/tests/__pycache__/test_flow.cpython-311.pyc +0 -0
  99. clip2table-0.1.0/tests/__pycache__/test_harvest_activation.cpython-311.pyc +0 -0
  100. clip2table-0.1.0/tests/__pycache__/test_harvest_challenge.cpython-311.pyc +0 -0
  101. clip2table-0.1.0/tests/__pycache__/test_harvest_chrome_copy.cpython-311.pyc +0 -0
  102. clip2table-0.1.0/tests/__pycache__/test_harvest_error_page.cpython-311.pyc +0 -0
  103. clip2table-0.1.0/tests/__pycache__/test_htmltable.cpython-311.pyc +0 -0
  104. clip2table-0.1.0/tests/__pycache__/test_infer.cpython-311.pyc +0 -0
  105. clip2table-0.1.0/tests/__pycache__/test_keyboard_focus.cpython-311.pyc +0 -0
  106. clip2table-0.1.0/tests/__pycache__/test_legacy_baseline.cpython-311.pyc +0 -0
  107. clip2table-0.1.0/tests/__pycache__/test_lines_mode.cpython-311.pyc +0 -0
  108. clip2table-0.1.0/tests/__pycache__/test_normalize.cpython-311.pyc +0 -0
  109. clip2table-0.1.0/tests/__pycache__/test_page_dedupe_cache.cpython-311.pyc +0 -0
  110. clip2table-0.1.0/tests/__pycache__/test_paging.cpython-311.pyc +0 -0
  111. clip2table-0.1.0/tests/__pycache__/test_regress.cpython-311.pyc +0 -0
  112. clip2table-0.1.0/tests/__pycache__/test_render_lossless.cpython-311.pyc +0 -0
  113. clip2table-0.1.0/tests/__pycache__/test_resume_restore.cpython-311.pyc +0 -0
  114. clip2table-0.1.0/tests/__pycache__/test_segment.cpython-311.pyc +0 -0
  115. clip2table-0.1.0/tests/__pycache__/test_selftest.cpython-311.pyc +0 -0
  116. clip2table-0.1.0/tests/__pycache__/test_state_recovery.cpython-311.pyc +0 -0
  117. clip2table-0.1.0/tests/__pycache__/test_structure.cpython-311.pyc +0 -0
  118. clip2table-0.1.0/tests/__pycache__/test_unparsed_sidecar.cpython-311.pyc +0 -0
  119. clip2table-0.1.0/tests/__pycache__/test_ws.cpython-311.pyc +0 -0
  120. clip2table-0.1.0/tests/test_assemble.py +160 -0
  121. clip2table-0.1.0/tests/test_card_binding.py +204 -0
  122. clip2table-0.1.0/tests/test_cards.py +249 -0
  123. clip2table-0.1.0/tests/test_cdp_hint.py +80 -0
  124. clip2table-0.1.0/tests/test_cfhtml.py +150 -0
  125. clip2table-0.1.0/tests/test_cli.py +272 -0
  126. clip2table-0.1.0/tests/test_clipboard.py +284 -0
  127. clip2table-0.1.0/tests/test_conflicts.py +262 -0
  128. clip2table-0.1.0/tests/test_dataset.py +211 -0
  129. clip2table-0.1.0/tests/test_extract.py +258 -0
  130. clip2table-0.1.0/tests/test_extract_line.py +59 -0
  131. clip2table-0.1.0/tests/test_field_trace.py +254 -0
  132. clip2table-0.1.0/tests/test_flow.py +140 -0
  133. clip2table-0.1.0/tests/test_harvest_activation.py +197 -0
  134. clip2table-0.1.0/tests/test_harvest_challenge.py +82 -0
  135. clip2table-0.1.0/tests/test_harvest_chrome_copy.py +63 -0
  136. clip2table-0.1.0/tests/test_harvest_error_page.py +61 -0
  137. clip2table-0.1.0/tests/test_htmltable.py +90 -0
  138. clip2table-0.1.0/tests/test_infer.py +257 -0
  139. clip2table-0.1.0/tests/test_keyboard_focus.py +107 -0
  140. clip2table-0.1.0/tests/test_legacy_baseline.py +106 -0
  141. clip2table-0.1.0/tests/test_lines_mode.py +137 -0
  142. clip2table-0.1.0/tests/test_normalize.py +72 -0
  143. clip2table-0.1.0/tests/test_page_dedupe_cache.py +131 -0
  144. clip2table-0.1.0/tests/test_paging.py +125 -0
  145. clip2table-0.1.0/tests/test_regress.py +177 -0
  146. clip2table-0.1.0/tests/test_render_lossless.py +109 -0
  147. clip2table-0.1.0/tests/test_resume_restore.py +176 -0
  148. clip2table-0.1.0/tests/test_segment.py +246 -0
  149. clip2table-0.1.0/tests/test_selftest.py +163 -0
  150. clip2table-0.1.0/tests/test_state_recovery.py +99 -0
  151. clip2table-0.1.0/tests/test_structure.py +157 -0
  152. clip2table-0.1.0/tests/test_unparsed_sidecar.py +117 -0
  153. clip2table-0.1.0/tests/test_ws.py +120 -0
@@ -0,0 +1,45 @@
1
+ # 变更记录
2
+
3
+ 格式参照 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
4
+ 版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
5
+
6
+ ## [0.1.0] — 2026-10-23
7
+
8
+ 首个可用版本。四条里程碑加两轮共 16 项整改,每条都有可复核的验收记录,
9
+ 不在这里自述(见 [docs/verification.md](docs/verification.md))。
10
+
11
+ ### 新增
12
+
13
+ - **三条取数通道**:`http` 直连 / `browser` CDP 发真按键 / `desktop` Win32 `SendInput`。
14
+ 不指定时自动降级,报告首行永远写清楚这次走了哪条、判定依据是什么。
15
+ - **零配置结构反推** `infer`:S0 表格 / S1 周期 / S2 键值 / S3 编号 / S4 原文兜底。
16
+ 有歧义时说出来,不硬猜。
17
+ - **站点卡(TOML)**:通道、翻页、切分规则、字段定义、去重键全部声明在卡里。
18
+ 加一个站点 = 加一个文件,代码一行不动。
19
+ - **数据集**:以「页」为提交单位(先写临时页目录再发布为不可变页)、断点续采、
20
+ 页级去重、冲突四分类审计、卡指纹绑定(同一份数据不允许被两套规则混着写)。
21
+ - **导出**:CSV / Markdown / JSONL / XLSX。无损模式——超出单元格上限的值旁挂
22
+ `.full.jsonl`,字段名里的 `|` 自动转义,被改动的单元格逐条记账。
23
+ - **字段溯源**:每个值都记着类型、来源(行号 / 正则捕获组 / 单元格)和失败原因。
24
+ - **5 张内置卡**,各带逐字节回归样本,`clip2table test <卡名>` 可自证。
25
+ - `clip2table doctor` 自检(剪贴板 / 卡目录 / 浏览器调试端口),
26
+ `clip2table selftest` 把文档里出现的命令和真实参数表对账;找不到任何文档时
27
+ 它报错退出,而不是给一个「跳过」的绿。
28
+
29
+ ### 保证
30
+
31
+ 这三条是这个项目的定义,不是宣传语——它们各有一组测试盯着:
32
+
33
+ - **行数守恒**:记录数 + 未识别行数 = 源行数。切不动的行进 `_unparsed`,不丢。
34
+ - **不补值**:表里没有的就是空。空缺率本身是信息,填平了就再也看不出来。
35
+ - **导出不改内容**:任何被截断或转义的单元格都写进旁挂文件,不静默动手。
36
+
37
+ ### 已知限制
38
+
39
+ - **仅 Windows**。通道 C 依赖 Win32 API,通道 B 的焦点归还依赖窗口句柄。
40
+ - **通道 B 在浏览器被最小化时要短暂借用前台**,采完把焦点还给原来的窗口。
41
+ 窗口只是被盖住时不动你的前台。三种情形各有实测记录。
42
+ - **纯文本切分是启发式的**。页面结构变了就会切错——所以有行形状校验、独立答案键
43
+ 回归、逐页偏离标记,而不是靠「看起来对」。
44
+
45
+ [0.1.0]: https://github.com/yjq123-zyh/clip2table/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 clip2table contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ # sdist 里要有足够跑测试的东西,否则「下载源码包」这条路是死的。
2
+ # 之前只有 src/ 和 tests/ 进了 sdist,fixtures/ 没进——单测一跑就红在找不到夹具,
3
+ # 下载 sdist 的人会以为代码坏了。wheel 是给用户的,sdist 是给改代码的人的,
4
+ # 两者要的内容不一样,得分开说。
5
+
6
+ include LICENSE
7
+ include NOTICE.md
8
+ include README.md
9
+ include SKILL.md
10
+ include CHANGELOG.md
11
+ include pyproject.toml
12
+
13
+ # 单测直接读 fixtures/,缺了它 472 项里有一批跑不起来
14
+ recursive-include fixtures *
15
+ recursive-include tests *
16
+
17
+ # selftest 要拿这几份文档跟真实参数表对账;装好的 wheel 没有它们(那时它会报错),
18
+ # sdist 里有,所以从 sdist 跑 selftest 是绿的、从 wheel 跑是非 0 的,两者都对
19
+ recursive-include docs *.md
20
+
21
+ # 不装包也能跑的子命令启动器,CI 和文档都用它
22
+ include _dev/c2t.py
@@ -0,0 +1,114 @@
1
+ # 使用须知与权利说明
2
+
3
+ 本文件说明本项目的著作权归属、授权范围、禁止性规定、免责声明、协议终止与争议解决。
4
+
5
+ **软件许可的正式条款以 [LICENSE](LICENSE)(MIT)为准。** 本文件与其在授权范围或
6
+ 条件上不一致时,以 MIT 许可证为准;涉及强制性法律规定的,依法律规定处理。
7
+
8
+ ---
9
+
10
+ ## 一、著作权归属
11
+
12
+ 1. 本项目中受著作权保护的代码、文档及其他原创内容,其著作权由相应作者或其他合法权利人
13
+ 依法享有。具体归属以文件声明、有效协议及相关权利证明为依据。
14
+ 2. 除另有有效约定外,贡献者提交代码或文档不构成著作权转让;项目维护者不因接收贡献而
15
+ 取得全部贡献内容的著作权。
16
+ 3. 用户对本项目进行修改、扩展或二次开发,可以依法享有其新增原创内容的相应权利,但不得
17
+ 据此否认原有内容的权利归属。
18
+ 4. 用户使用本项目处理的数据,其权利归属不因经过本项目采集、解析或导出而改变。项目作者
19
+ 及贡献者不因提供工具而取得用户数据的所有权或使用许可。
20
+
21
+ 上述权利归属原则依据著作权法律规则确定,参见《中华人民共和国著作权法》。
22
+
23
+ > 与工具行为对应的一句实话:本项目在你自己的机器上运行,不含遥测、不上传数据、不建立
24
+ > 任何回传通道。走 `http` 通道时它会按卡里声明的地址发起请求,走 `browser` / `desktop`
25
+ > 通道时它驱动的是你自己的浏览器和你自己的窗口——没有第三方能从我们这里拿到你的数据,
26
+ > 因为我们本来就拿不到。
27
+
28
+ ---
29
+
30
+ ## 二、授权范围
31
+
32
+ 1. 在遵守 MIT 许可证条件的前提下,任何获得本项目副本的人,均可免费使用、复制、修改、
33
+ 合并、发布、分发、再许可及销售软件副本。
34
+ 2. 授权包括个人使用、学习研究、企业内部使用、商业使用和二次开发。MIT 许可证不要求用户
35
+ 公开其自行开发的全部源代码。
36
+ 3. 分发软件副本或包含本项目实质性内容的作品时,应按照 MIT 许可证保留相应著作权声明及
37
+ 许可声明。
38
+ 4. 本项目的软件许可**不包含**对第三方网站、账户、数据、商标及其他独立权利的授权。能够
39
+ 浏览或复制某项内容,不等于已经取得任意批量采集、公开传播或商业利用该内容的权利。
40
+
41
+ 软件授权的具体条件以项目所附 [MIT 许可证](LICENSE) 为准。
42
+
43
+ ---
44
+
45
+ ## 三、禁止性规定
46
+
47
+ 使用者不得实施以下行为:
48
+
49
+ 1. 在依法应当保留声明的再分发情形下,删除必要的著作权声明和许可声明。
50
+ 2. 伪造作者身份、冒认他人作品,或以欺骗方式宣称修改版、衍生产品获得项目作者的官方认可
51
+ 或背书。
52
+ 3. 利用本项目实施非法访问、窃取账户凭据、侵犯个人信息权益、侵犯商业秘密或其他违法行为。
53
+ 4. 在缺乏合法依据的情况下,获取、使用、披露或传播受法律保护的数据和内容。
54
+ 5. 以本项目名义虚构授权、服务承诺或保证,并将相关责任归于未作出该承诺的作者、贡献者或
55
+ 维护者。
56
+
57
+ 上述规定涉及许可证履行及独立的法定义务,**不构成对 MIT 许可证新增的商业用途或行业限制**。
58
+
59
+ ---
60
+
61
+ ## 四、免责声明
62
+
63
+ 1. 本项目按照 MIT 许可证以现状提供。在适用法律允许的范围内,作者及贡献者不提供关于
64
+ 适销性、特定用途适用性或不侵权的明示或默示保证。
65
+ 2. 本项目的结构推断、字段提取、分页采集和格式转换可能受到源内容变化、浏览器行为、窗口
66
+ 状态及用户配置的影响。输出结果、质量提示和溯源信息不构成对数据准确性、完整性或业务
67
+ 适用性的保证。
68
+ 3. 用户应根据实际用途决定是否复核结果、保留原始数据和进行备份,并对其采集范围、数据
69
+ 使用方式及据此作出的业务决定负责。
70
+ 4. 在适用法律允许的范围内,作者及贡献者依 MIT 许可证享有相应责任限制;本说明不排除依法
71
+ 不得免除的责任。
72
+ 5. 任何免责约定均不得排除依法应承担的人身损害责任,或因故意、重大过失造成对方财产损失
73
+ 的责任。
74
+
75
+ 免责及责任限制须受强制性法律规定约束,参见《中华人民共和国民法典》第五百零六条。
76
+
77
+ ---
78
+
79
+ ## 五、协议终止与违约处理
80
+
81
+ 1. 用户可以随时停止使用本项目。停止使用不影响此前已发生行为的法律责任。
82
+ 2. 对不符合 MIT 许可证条件、且不存在其他合法依据的复制或分发行为,权利人可以依法要求
83
+ 停止侵权、补正声明并承担相应责任。
84
+ 3. 是否发生授权终止、终止的范围以及补正后的法律效果,依许可证条件和适用法律确定。本说明
85
+ 不另行设置维护者可以任意撤销既有 MIT 授权的权利。
86
+ 4. 项目停止维护、仓库关闭或后续版本变更许可,不因该事实本身撤销用户对既有版本已经合法
87
+ 取得的授权。
88
+ 5. 任何终止或违约处理,不影响第三方依据有效许可独立取得的权利,也不影响依法继续有效的
89
+ 责任及争议解决约定。
90
+
91
+ ---
92
+
93
+ ## 六、法律适用与争议解决
94
+
95
+ 1. 对于有关双方另行依法达成、并明确纳入本说明的协议,双方可以约定适用中华人民共和国
96
+ 大陆地区法律;依法必须适用的其他强制性规定不受影响。
97
+ 2. 未形成有效法律选择约定的,适用法律按照有关法律规则确定,不因本说明单方发布而当然确定。
98
+ 3. 因本项目授权、使用或权利归属发生争议,当事人可以协商解决;协商不成的,依法向有管辖权
99
+ 的人民法院提起诉讼。
100
+ 4. 本说明部分条款被认定无效或不能执行的,其他可独立适用条款的效力依法律规定确定。
101
+ 5. 本说明与 MIT 许可证在软件授权范围或条件上存在不一致时,以 MIT 许可证为准;涉及强制性
102
+ 法律规定的,依法律规定处理。
103
+
104
+ ---
105
+
106
+ ## 需要说明的三点
107
+
108
+ - **这不是法律意见。** 本文件是本项目对其权利与责任的说明,不是律师出具的法律意见书,也
109
+ 不构成对任何具体使用场景的合规结论。
110
+ - **第六条若要作为双方合同约定使用**,建议由律师结合权利人所在地及协议订立方式确认后再定稿——
111
+ 单方发布的权利说明本身通常不足以在当事人之间成立一份有效选法约定。
112
+ - **能不能用,取决于你怎么用。** 工具只把你屏幕上已经能看到、有权限看到的内容变成表;这份
113
+ 说明约束的是工具的分发与使用方式,不改变你对自己采集内容所负的责任。第三条已经把话说
114
+ 清楚,这里不再重复免责。
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: clip2table
3
+ Version: 0.1.0
4
+ Summary: 把屏幕上看得到、选得中的任何内容,变成一张结构化表
5
+ Author-email: kdy <yangdurant978@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/yjq123-zyh/clip2table
8
+ Project-URL: Repository, https://github.com/yjq123-zyh/clip2table
9
+ Project-URL: Documentation, https://github.com/yjq123-zyh/clip2table/blob/main/docs/usage.md
10
+ Project-URL: Issues, https://github.com/yjq123-zyh/clip2table/issues
11
+ Project-URL: Changelog, https://github.com/yjq123-zyh/clip2table/blob/main/CHANGELOG.md
12
+ Keywords: clipboard,scraping,table,windows,cdp,csv,xlsx
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Win32 (MS Windows)
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Office/Business
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ License-File: NOTICE.md
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.2; extra == "dev"
28
+ Dynamic: license-file
29
+
30
+ # clip2table
31
+
32
+ **把屏幕上看得到、选得中的任何内容,变成一张结构化表。**
33
+
34
+ [![tests](https://github.com/yjq123-zyh/clip2table/actions/workflows/tests.yml/badge.svg)](https://github.com/yjq123-zyh/clip2table/actions/workflows/tests.yml)
35
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/)
36
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
37
+ [![Platform](https://img.shields.io/badge/platform-Windows-lightgrey)](#环境要求)
38
+
39
+ 不是爬虫。爬虫要假装自己是浏览器、绕过指纹、维护代理池,然后在你和站点之间
40
+ 永远打一场你赢不了的军备竞赛。clip2table 换了个思路:**让真浏览器、真程序自己去复制**,
41
+ 它只负责把复制出来的文本切成表。
42
+
43
+ 所以它对你自己的登录态、付费墙后的后台、只有客户端才能看到的列表,一视同仁——
44
+ 因为那些东西本来就在用户屏幕上,而用户有权限看。
45
+
46
+ ```
47
+ ┌ 你的屏幕 ─────────────────────────────┐
48
+ │ 一个需要登录的后台订单页 │
49
+ │ 一个反爬很凶的招聘站 │
50
+ │ 一个只在 Windows 客户端里存在的台账 │
51
+ └───────────────┬───────────────────────┘
52
+ │ 自动 Ctrl+A / Ctrl+C(真按键,不是 DOM 抓取)
53
+ ▼
54
+ 剪贴板:纯文本 + CF_HTML
55
+ │
56
+ ▼
57
+ sniff → normalize → segment → extract → assemble → export
58
+ │
59
+ ▼
60
+ CSV / Markdown / JSONL / XLSX
61
+ ```
62
+
63
+ ---
64
+
65
+ ## 看一眼真实输出
66
+
67
+ 不配任何规则,直接把一段乱糟糟的复制文本丢给它:
68
+
69
+ ```
70
+ $ clip2table infer --text-file fixtures/sample-ecom.txt --show 1
71
+ file | FILE | S1 | 3 行 × 5 列 | 未识别 0 行
72
+ 来源:未知(file 没有提供可核对的地址)
73
+ 字段填充:line1 100% | 日期 100% | line3 100% | 规格 100% | line5 100%
74
+ ✓ 行数守恒:15 行全部有归属(记录占用 15 行 + 未识别 0 行)
75
+ · S1:按周期锚点切成 3 条记录(每 5 行一条)
76
+ · 列名按每列的形态推断,键值列已去掉「键:」前缀,可在卡里改成业务名
77
+
78
+ 前 1 条:
79
+ 1. line1=星海无线耳机 Pro | 日期=2026-03-14 | line3=包装完好,降噪比上一代强不少,通勤地铁里基本听不到报站 | 规格=黑色 / 降噪版 | line5=有用(128)
80
+
81
+ 依据:
82
+ · 周期候选 1:每 5 行一条,从第 1 行起,3 条;边界一致 100%,最弱一位的结构占比 100%,完全同形的记录 3/3 条
83
+ ```
84
+
85
+ 注意最后那段「依据」。它不是在炫技——**它把「为什么认为这是 3 行 × 5 列」摊开给你看**,
86
+ 你才有机会说"你猜错了"。只给结论不给依据的自动推断,是最难发现错误的那种。
87
+
88
+ ---
89
+
90
+ ## 装
91
+
92
+ ```bash
93
+ pip install clip2table
94
+ clip2table doctor # 自检:剪贴板 / 卡目录 / 浏览器调试端口
95
+ ```
96
+
97
+ 零必需依赖,Python 3.11+,Windows 10/11。
98
+
99
+ > PyPI 上的首次上传还没做(要先配 trusted publisher,见
100
+ > [docs/publishing.md](docs/publishing.md))。在那之前 `pip install clip2table`
101
+ > 会 404,请从 [Releases](https://github.com/yjq123-zyh/clip2table/releases) 下 wheel 装:
102
+ >
103
+ > ```bash
104
+ > pip install https://github.com/yjq123-zyh/clip2table/releases/download/v0.1.0/clip2table-0.1.0-py3-none-any.whl
105
+ > ```
106
+
107
+ 要给这个项目改代码,从源码装:
108
+
109
+ ```bash
110
+ git clone https://github.com/yjq123-zyh/clip2table.git
111
+ cd clip2table
112
+ pip install -e .
113
+ ```
114
+
115
+ 完全不想装包也可以:`python _dev/c2t.py <子命令>`,效果一样(它只是把 `src/` 挂上 `sys.path`)。
116
+
117
+ ## 三条命令
118
+
119
+ ```bash
120
+ clip2table infer # 读剪贴板,反推结构,先看清楚再存
121
+ clip2table run --url https://example.com/list --show 3 # 自动选通道取一页,打印报告
122
+ clip2table harvest -p mysite.toml --max-pages 20 --yes -f csv -f xlsx --dir out
123
+ ```
124
+
125
+ `harvest` 是主线:按卡里的规则翻页、累积、去重、断点续采。卡怎么写、累积采集、
126
+ 断点续采、安全边界,全在 **[docs/usage.md](docs/usage.md)**。
127
+
128
+ ---
129
+
130
+ ## 三条不许
131
+
132
+ 这是这个项目存在的理由,也是它跟"剪贴板转表格小工具"的分界线。
133
+
134
+ **1. 不许静默丢行。** 每条源文本要么进了某条记录,要么进了未识别清单,两者之和必须等于源行数。
135
+
136
+ **2. 不许把切错的表当好表。** 行数守恒只能证明"没丢",证明不了"切对了"。
137
+ 所以还有独立答案键回归,以及卡里 `record_lines` / `required` 的逐页形状校验。
138
+
139
+ **3. 不许补值。** 表里没有的值就是空。空缺率本身就是信息——填平了就再也看不出来了。
140
+
141
+ ---
142
+
143
+ ## 三条通道
144
+
145
+ | 通道 | 引擎 | 用在哪 |
146
+ |---|---|---|
147
+ | `http` | HTTP 直连 | 页面服务端就渲染好了 |
148
+ | `browser` | E1:CDP 发真 Ctrl+A/Ctrl+C | 要登录、要 JS、有反爬 |
149
+ | `desktop` | E2:Win32 `SendInput` | 目标不是浏览器:客户端、Excel、记事本 |
150
+
151
+ 不指定时自动降级,**报告第一行永远写清楚这次走了哪条、为什么**。
152
+
153
+ ---
154
+
155
+ ## 能做什么 / 不能做什么
156
+
157
+ | 能做 | 说明 |
158
+ |---|---|
159
+ | 需要登录的后台列表 | 用你自己的浏览器、你自己的会话,不碰你的 Cookie |
160
+ | 反爬很凶的站点 | 请求是浏览器自己发的,指纹、IP、UA 全真 |
161
+ | Windows 客户端里的台账 | 只要能 Ctrl+A / Ctrl+C,就能进表 |
162
+ | Excel 选中区域 | 桌面通道直接读,含 CF_HTML 的表格结构 |
163
+
164
+ | 不能做 | 为什么 |
165
+ |---|---|
166
+ | 图片、Canvas 里的文字 | 它不在文本流里,复制不出来 |
167
+ | 要逐个点开才能看到的内容 | 卡里能写少量准备动作,复杂交互做不了 |
168
+ | 有验证码的页面 | 会停下来报错,不绕过。验证码该由人来过 |
169
+ | 浏览器窗口最小化时的无痛复制 | Chromium 不允许隐藏标签页写剪贴板,会短暂借前台再还回去 |
170
+ | 非 Windows | 通道 C 直接调 Win32 API,通道 B 靠窗口句柄还焦点 |
171
+
172
+ 还有一条要说清楚:**纯文本切分是启发式的**。页面结构变了就会切错——所以才有行形状校验、
173
+ 独立答案键回归、逐页偏离标记。它不能保证永远切对,它能保证**切错了你不会不知道**。
174
+
175
+ ---
176
+
177
+ ## 文档地图
178
+
179
+ | 想知道 | 看这里 |
180
+ |---|---|
181
+ | 现在能做什么、怎么用、卡怎么写 | **[docs/usage.md](docs/usage.md)** |
182
+ | 代码怎么组织、数据怎么流、状态存在哪 | **[docs/architecture.md](docs/architecture.md)** |
183
+ | 为什么这么设计、哪些方案被否掉了 | **[docs/design-notes.md](docs/design-notes.md)** |
184
+ | 十六项整改各自的验收证据 | [docs/verification.md](docs/verification.md) |
185
+ | 怎么发版、PyPI 怎么配 | [docs/publishing.md](docs/publishing.md) |
186
+ | 给 agent 用的决策循环 | [SKILL.md](SKILL.md) |
187
+ | 改了什么 | [CHANGELOG.md](CHANGELOG.md) |
188
+ | 权利归属、能拿去做什么、免责 | [NOTICE.md](NOTICE.md) |
189
+ | 历史方案、实验记录、验收过程 | [docs/history/](docs/history/) —— **不代表当前能力** |
190
+
191
+ ---
192
+
193
+ ## 开发
194
+
195
+ ```bash
196
+ pip install -e .
197
+ python -m unittest discover -s tests -t . # 472 项
198
+ python _dev/c2t.py selftest # 文档里的命令与真实参数表对账
199
+ python _dev/c2t.py test notice-list # 内置卡的逐字节回归
200
+ ```
201
+
202
+ `SKILL.md` 是给 agent 看的决策循环,不是参数手册——参数手册是 `--help` 的活。
203
+ 它里面出现的每条子命令都会被 `selftest` 拿去和真实 argparser 比对,写错了就非 0 退出。
204
+ 也就是说,**文档里写了不存在的命令,CI 会红**。它校验的是仓库里的文档,所以要在这个
205
+ 目录里跑;装好的副本里没有文档,它会报错退出而不是假装通过。
206
+
207
+ 欢迎 issue 和 PR。提 PR 前请确认 `unittest` 与 `selftest` 都是绿的;
208
+ 改内置卡或 fixtures 请连带更新 `.expect.tsv`,那是逐字节比对的基准。
209
+
210
+ ## 环境要求
211
+
212
+ Windows 10/11、Python 3.11+。通道 B 需要浏览器带 `--remote-debugging-port` 启动,
213
+ `clip2table doctor` 会给你一行可以直接粘贴的命令。
214
+
215
+ ## 许可
216
+
217
+ MIT,见 [LICENSE](LICENSE)。
218
+
219
+ 著作权归属、授权范围、禁止性规定、免责与争议解决见 [NOTICE.md](NOTICE.md)。
220
+ 软件许可的正式条款以 MIT 许可证为准;两者不一致时以 MIT 为准。
@@ -0,0 +1,191 @@
1
+ # clip2table
2
+
3
+ **把屏幕上看得到、选得中的任何内容,变成一张结构化表。**
4
+
5
+ [![tests](https://github.com/yjq123-zyh/clip2table/actions/workflows/tests.yml/badge.svg)](https://github.com/yjq123-zyh/clip2table/actions/workflows/tests.yml)
6
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/)
7
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
+ [![Platform](https://img.shields.io/badge/platform-Windows-lightgrey)](#环境要求)
9
+
10
+ 不是爬虫。爬虫要假装自己是浏览器、绕过指纹、维护代理池,然后在你和站点之间
11
+ 永远打一场你赢不了的军备竞赛。clip2table 换了个思路:**让真浏览器、真程序自己去复制**,
12
+ 它只负责把复制出来的文本切成表。
13
+
14
+ 所以它对你自己的登录态、付费墙后的后台、只有客户端才能看到的列表,一视同仁——
15
+ 因为那些东西本来就在用户屏幕上,而用户有权限看。
16
+
17
+ ```
18
+ ┌ 你的屏幕 ─────────────────────────────┐
19
+ │ 一个需要登录的后台订单页 │
20
+ │ 一个反爬很凶的招聘站 │
21
+ │ 一个只在 Windows 客户端里存在的台账 │
22
+ └───────────────┬───────────────────────┘
23
+ │ 自动 Ctrl+A / Ctrl+C(真按键,不是 DOM 抓取)
24
+ ▼
25
+ 剪贴板:纯文本 + CF_HTML
26
+ │
27
+ ▼
28
+ sniff → normalize → segment → extract → assemble → export
29
+ │
30
+ ▼
31
+ CSV / Markdown / JSONL / XLSX
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 看一眼真实输出
37
+
38
+ 不配任何规则,直接把一段乱糟糟的复制文本丢给它:
39
+
40
+ ```
41
+ $ clip2table infer --text-file fixtures/sample-ecom.txt --show 1
42
+ file | FILE | S1 | 3 行 × 5 列 | 未识别 0 行
43
+ 来源:未知(file 没有提供可核对的地址)
44
+ 字段填充:line1 100% | 日期 100% | line3 100% | 规格 100% | line5 100%
45
+ ✓ 行数守恒:15 行全部有归属(记录占用 15 行 + 未识别 0 行)
46
+ · S1:按周期锚点切成 3 条记录(每 5 行一条)
47
+ · 列名按每列的形态推断,键值列已去掉「键:」前缀,可在卡里改成业务名
48
+
49
+ 前 1 条:
50
+ 1. line1=星海无线耳机 Pro | 日期=2026-03-14 | line3=包装完好,降噪比上一代强不少,通勤地铁里基本听不到报站 | 规格=黑色 / 降噪版 | line5=有用(128)
51
+
52
+ 依据:
53
+ · 周期候选 1:每 5 行一条,从第 1 行起,3 条;边界一致 100%,最弱一位的结构占比 100%,完全同形的记录 3/3 条
54
+ ```
55
+
56
+ 注意最后那段「依据」。它不是在炫技——**它把「为什么认为这是 3 行 × 5 列」摊开给你看**,
57
+ 你才有机会说"你猜错了"。只给结论不给依据的自动推断,是最难发现错误的那种。
58
+
59
+ ---
60
+
61
+ ## 装
62
+
63
+ ```bash
64
+ pip install clip2table
65
+ clip2table doctor # 自检:剪贴板 / 卡目录 / 浏览器调试端口
66
+ ```
67
+
68
+ 零必需依赖,Python 3.11+,Windows 10/11。
69
+
70
+ > PyPI 上的首次上传还没做(要先配 trusted publisher,见
71
+ > [docs/publishing.md](docs/publishing.md))。在那之前 `pip install clip2table`
72
+ > 会 404,请从 [Releases](https://github.com/yjq123-zyh/clip2table/releases) 下 wheel 装:
73
+ >
74
+ > ```bash
75
+ > pip install https://github.com/yjq123-zyh/clip2table/releases/download/v0.1.0/clip2table-0.1.0-py3-none-any.whl
76
+ > ```
77
+
78
+ 要给这个项目改代码,从源码装:
79
+
80
+ ```bash
81
+ git clone https://github.com/yjq123-zyh/clip2table.git
82
+ cd clip2table
83
+ pip install -e .
84
+ ```
85
+
86
+ 完全不想装包也可以:`python _dev/c2t.py <子命令>`,效果一样(它只是把 `src/` 挂上 `sys.path`)。
87
+
88
+ ## 三条命令
89
+
90
+ ```bash
91
+ clip2table infer # 读剪贴板,反推结构,先看清楚再存
92
+ clip2table run --url https://example.com/list --show 3 # 自动选通道取一页,打印报告
93
+ clip2table harvest -p mysite.toml --max-pages 20 --yes -f csv -f xlsx --dir out
94
+ ```
95
+
96
+ `harvest` 是主线:按卡里的规则翻页、累积、去重、断点续采。卡怎么写、累积采集、
97
+ 断点续采、安全边界,全在 **[docs/usage.md](docs/usage.md)**。
98
+
99
+ ---
100
+
101
+ ## 三条不许
102
+
103
+ 这是这个项目存在的理由,也是它跟"剪贴板转表格小工具"的分界线。
104
+
105
+ **1. 不许静默丢行。** 每条源文本要么进了某条记录,要么进了未识别清单,两者之和必须等于源行数。
106
+
107
+ **2. 不许把切错的表当好表。** 行数守恒只能证明"没丢",证明不了"切对了"。
108
+ 所以还有独立答案键回归,以及卡里 `record_lines` / `required` 的逐页形状校验。
109
+
110
+ **3. 不许补值。** 表里没有的值就是空。空缺率本身就是信息——填平了就再也看不出来了。
111
+
112
+ ---
113
+
114
+ ## 三条通道
115
+
116
+ | 通道 | 引擎 | 用在哪 |
117
+ |---|---|---|
118
+ | `http` | HTTP 直连 | 页面服务端就渲染好了 |
119
+ | `browser` | E1:CDP 发真 Ctrl+A/Ctrl+C | 要登录、要 JS、有反爬 |
120
+ | `desktop` | E2:Win32 `SendInput` | 目标不是浏览器:客户端、Excel、记事本 |
121
+
122
+ 不指定时自动降级,**报告第一行永远写清楚这次走了哪条、为什么**。
123
+
124
+ ---
125
+
126
+ ## 能做什么 / 不能做什么
127
+
128
+ | 能做 | 说明 |
129
+ |---|---|
130
+ | 需要登录的后台列表 | 用你自己的浏览器、你自己的会话,不碰你的 Cookie |
131
+ | 反爬很凶的站点 | 请求是浏览器自己发的,指纹、IP、UA 全真 |
132
+ | Windows 客户端里的台账 | 只要能 Ctrl+A / Ctrl+C,就能进表 |
133
+ | Excel 选中区域 | 桌面通道直接读,含 CF_HTML 的表格结构 |
134
+
135
+ | 不能做 | 为什么 |
136
+ |---|---|
137
+ | 图片、Canvas 里的文字 | 它不在文本流里,复制不出来 |
138
+ | 要逐个点开才能看到的内容 | 卡里能写少量准备动作,复杂交互做不了 |
139
+ | 有验证码的页面 | 会停下来报错,不绕过。验证码该由人来过 |
140
+ | 浏览器窗口最小化时的无痛复制 | Chromium 不允许隐藏标签页写剪贴板,会短暂借前台再还回去 |
141
+ | 非 Windows | 通道 C 直接调 Win32 API,通道 B 靠窗口句柄还焦点 |
142
+
143
+ 还有一条要说清楚:**纯文本切分是启发式的**。页面结构变了就会切错——所以才有行形状校验、
144
+ 独立答案键回归、逐页偏离标记。它不能保证永远切对,它能保证**切错了你不会不知道**。
145
+
146
+ ---
147
+
148
+ ## 文档地图
149
+
150
+ | 想知道 | 看这里 |
151
+ |---|---|
152
+ | 现在能做什么、怎么用、卡怎么写 | **[docs/usage.md](docs/usage.md)** |
153
+ | 代码怎么组织、数据怎么流、状态存在哪 | **[docs/architecture.md](docs/architecture.md)** |
154
+ | 为什么这么设计、哪些方案被否掉了 | **[docs/design-notes.md](docs/design-notes.md)** |
155
+ | 十六项整改各自的验收证据 | [docs/verification.md](docs/verification.md) |
156
+ | 怎么发版、PyPI 怎么配 | [docs/publishing.md](docs/publishing.md) |
157
+ | 给 agent 用的决策循环 | [SKILL.md](SKILL.md) |
158
+ | 改了什么 | [CHANGELOG.md](CHANGELOG.md) |
159
+ | 权利归属、能拿去做什么、免责 | [NOTICE.md](NOTICE.md) |
160
+ | 历史方案、实验记录、验收过程 | [docs/history/](docs/history/) —— **不代表当前能力** |
161
+
162
+ ---
163
+
164
+ ## 开发
165
+
166
+ ```bash
167
+ pip install -e .
168
+ python -m unittest discover -s tests -t . # 472 项
169
+ python _dev/c2t.py selftest # 文档里的命令与真实参数表对账
170
+ python _dev/c2t.py test notice-list # 内置卡的逐字节回归
171
+ ```
172
+
173
+ `SKILL.md` 是给 agent 看的决策循环,不是参数手册——参数手册是 `--help` 的活。
174
+ 它里面出现的每条子命令都会被 `selftest` 拿去和真实 argparser 比对,写错了就非 0 退出。
175
+ 也就是说,**文档里写了不存在的命令,CI 会红**。它校验的是仓库里的文档,所以要在这个
176
+ 目录里跑;装好的副本里没有文档,它会报错退出而不是假装通过。
177
+
178
+ 欢迎 issue 和 PR。提 PR 前请确认 `unittest` 与 `selftest` 都是绿的;
179
+ 改内置卡或 fixtures 请连带更新 `.expect.tsv`,那是逐字节比对的基准。
180
+
181
+ ## 环境要求
182
+
183
+ Windows 10/11、Python 3.11+。通道 B 需要浏览器带 `--remote-debugging-port` 启动,
184
+ `clip2table doctor` 会给你一行可以直接粘贴的命令。
185
+
186
+ ## 许可
187
+
188
+ MIT,见 [LICENSE](LICENSE)。
189
+
190
+ 著作权归属、授权范围、禁止性规定、免责与争议解决见 [NOTICE.md](NOTICE.md)。
191
+ 软件许可的正式条款以 MIT 许可证为准;两者不一致时以 MIT 为准。