picturereader 1.0.1 → 1.0.3

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.
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  | 版本 | 平台 | 形态 | 源码 | 安装 |
16
16
  |---|---|---|---|---|
17
17
  | **DSH 版** | DeepSeek Harness(含 EAC 桌面端) | npm 插件(`dsh.bundle`) | 仓库根目录 | `dsh plugin --profile web add picturereader` |
18
- | **ZCode 版** | ZCode 桌面端 | 本地 marketplace 插件(MCP server + skill) | `zcode/` 子目录 | Settings Plugin Management → Discover → 本地目录 |
18
+ | **ZCode 版** | ZCode 桌面端 | 本地 marketplace 插件(MCP server + skill) | [jing-hy/picturereader-zcode](https://github.com/jing-hy/picturereader-zcode)(独立仓库) | 见其 README |
19
19
 
20
20
  两个版本共用同一套业务核心(`src/core.js`)与读图方法论 skill(`image-reading`),
21
21
  三个工具行为完全一致:`image_scan` / `image_ocr` / `image_sample`。
@@ -72,26 +72,11 @@ copy skills\image-reading.md %USERPROFILE%\.dsh\skills\ # Windows
72
72
 
73
73
  ### ZCode 版
74
74
 
75
- ZCode 版源码在本仓库 `zcode/` 子目录(本地开发目录为 `D:\coding\picturereader_zcode`)。
75
+ ZCode 版是**独立仓库**:[jing-hy/picturereader-zcode](https://github.com/jing-hy/picturereader-zcode)。
76
76
 
77
- ```text
78
- ZCode 桌面端 → Settings → Plugin Management → Discover → "+" → 本地目录
79
- 选择 zcode/ 目录(或本地开发目录 picturereader_zcode)
80
- → 市场列表出现 picturereader → 安装
81
- ```
82
-
83
- 安装并启用后(重启 ZCode 或按提示重连):
84
-
85
- 1. **MCP server `picturereader` 自动连接**(stdio 子进程 `node mcp/server.js`),模型工具列表出现 `image_scan` / `image_ocr` / `image_sample`;
86
- 2. **skill `image-reading` 自动出现**在技能列表(图片任务自动触发);
87
- 3. **(可选)PaddleOCR 增强引擎**:`node scripts/setup-ocr.mjs`。
88
-
89
- > **关于安装后的运行位置**:市场安装会把插件目录拷贝到 ZCode 插件缓存
90
- > (`~/.zcode/cli/plugins/cache/...`),但 `.zcode-plugin/plugin.json` 里 MCP server 的
91
- > `args` 是**具体绝对路径**,指向所选目录的 `mcp/server.js`——因此 MCP server 始终从
92
- > **所选目录**运行:改 `src/core.js` 下次调用即生效(热加载)。skill / 工具定义变更
93
- > 需在 Discover 里卸载后重装生效(或手动同步缓存副本)。若插件目录移动,请同步更新
94
- > `plugin.json` 里的 `args` 路径。
77
+ ZCode 版通过 **MCP server**(`mcp/server.js`,stdio)把三个工具暴露给 ZCode,
78
+ 读图方法论作为 **skill**(`skills/image-reading/`)随插件分发,业务逻辑 `src/core.js`
79
+ 与本仓库完全一致。安装与使用请见其 README。
95
80
 
96
81
  ## 使用
97
82
 
@@ -123,14 +108,15 @@ luminance grid / color grid
123
108
  ## 开发
124
109
 
125
110
  ```sh
126
- # DSH 版(仓库根目录)
111
+ # DSH 版(本仓库)
127
112
  npm install
128
113
  npm test # node:test,76 个测试全绿
129
114
  node scripts/setup-ocr.mjs # 可选:装 PaddleOCR
130
115
  node scripts/preview.mjs # 生成 fixtures 并预览渲染
131
116
 
132
- # ZCode 版(zcode/ 子目录)
133
- cd zcode
117
+ # ZCode 版(独立仓库 jing-hy/picturereader-zcode
118
+ git clone https://github.com/jing-hy/picturereader-zcode.git
119
+ cd picturereader-zcode
134
120
  npm install
135
121
  npm test # node:test
136
122
  node scripts/setup-ocr.mjs # 可选
@@ -141,7 +127,7 @@ node scripts/setup-ocr.mjs # 可选
141
127
  下次调用即生效**;工具定义(schema/描述)改动需重启桌面端。
142
128
 
143
129
  **热插拔(ZCode 版)**:MCP server 从所选目录运行,改 `src/core.js` 下次调用即生效
144
- (见上"安装后运行位置")。
130
+ (详见 picturereader-zcode 仓库 README)。
145
131
 
146
132
  ## 优势
147
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picturereader",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "DSH plugin: pixel-to-text image reading for text-only models. Downscales and color-quantizes PNG/JPEG/GIF/BMP and feeds the coarse pixel grid to the model so DeepSeek can \u0027see\u0027 layout, colors and rough shapes without a vision model.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "src",
14
+ "scripts",
14
15
  "cordis.patch.yml",
15
16
  "skills",
16
17
  "README.md",
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Generate fixture images on disk and print the image_scan render for a
3
+ * quick visual sanity check without booting a harness.
4
+ * Usage: node scripts/preview.mjs
5
+ * @module picturereader/scripts/preview
6
+ */
7
+
8
+ import { mkdirSync, writeFileSync } from 'node:fs';
9
+ import { join, dirname } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { analyzeImage, renderImageScan } from '../src/core.js';
12
+ import { makeChartRgba, makeQuadrantRgba, pngFromRgba, jpegFromRgba, gifFromRgba, bmpFromRgba } from '../tests/fixtures.mjs';
13
+
14
+ const outDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'tests', 'fixtures-out');
15
+ mkdirSync(outDir, { recursive: true });
16
+
17
+ const images = [
18
+ { name: 'chart', width: 600, height: 400, rgba: makeChartRgba(), formats: ['png', 'jpeg', 'gif', 'bmp'] },
19
+ { name: 'quadrant', width: 100, height: 100, rgba: makeQuadrantRgba(), formats: ['png'] }
20
+ ];
21
+
22
+ for (const image of images) {
23
+ for (const format of image.formats) {
24
+ const buffer =
25
+ format === 'png' ? pngFromRgba(image.width, image.height, image.rgba)
26
+ : format === 'jpeg' ? jpegFromRgba(image.width, image.height, image.rgba)
27
+ : format === 'gif' ? gifFromRgba(image.width, image.height, image.rgba)
28
+ : bmpFromRgba(image.width, image.height, image.rgba, 24);
29
+ writeFileSync(join(outDir, `${image.name}.${format}`), buffer);
30
+ }
31
+ const analysis = analyzeImage(image.rgba, image.width, image.height, { size: 32, mode: 'auto', region: undefined });
32
+ console.log(renderImageScan({
33
+ path: join(outDir, `${image.name}.png`),
34
+ width: image.width,
35
+ height: image.height,
36
+ region: 'full',
37
+ ...analysis
38
+ }));
39
+ console.log();
40
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Optional install helper for the PaddleOCR engine (image_ocr engine="paddle").
3
+ * PaddleOCR is RECOMMENDED (far better at glowing/curved/game text) but
4
+ * OPTIONAL — image_ocr degrades to the Windows engine when it is missing.
5
+ *
6
+ * What this does:
7
+ * 1. Ensures a Python 3.12+ interpreter exists (downloads the official
8
+ * 3.12.10 installer from the npmmirror mirror if missing).
9
+ * 2. Creates/repairs the paddle_venv.
10
+ * 3. Installs paddlepaddle + paddleocr from the Tsinghua PyPI mirror.
11
+ * 4. Warms the model cache by running one recognition on a test image.
12
+ *
13
+ * Usage: node scripts/setup-ocr.mjs
14
+ */
15
+ import { existsSync, mkdirSync } from 'node:fs';
16
+ import { spawnSync } from 'node:child_process';
17
+ import { join, dirname } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const PY312 = 'C:\\Users\\Administrator\\Python312\\python.exe';
21
+ const VENV = 'C:\\Users\\Administrator\\paddle_venv\\Scripts\\python.exe';
22
+ const INSTALLER = 'C:\\Users\\Administrator\\Downloads\\python-3.12.10-amd64.exe';
23
+ const INSTALLER_URL = 'https://registry.npmmirror.com/-/binary/python/3.12.10/python-3.12.10-amd64.exe';
24
+ const PYPI = 'https://pypi.tuna.tsinghua.edu.cn/simple';
25
+ const CACHE = join(dirname(fileURLToPath(import.meta.url)), '..', '.paddlex-cache');
26
+
27
+ function run(cmd, args, opts = {}) {
28
+ console.log(`> ${cmd} ${args.join(' ')}`);
29
+ const result = spawnSync(cmd, args, { stdio: 'inherit', ...opts });
30
+ if (result.status !== 0) {
31
+ console.error(`!! command failed (exit ${result.status})`);
32
+ process.exit(1);
33
+ }
34
+ }
35
+
36
+ // 1. base Python 3.12
37
+ if (!existsSync(PY312)) {
38
+ console.log('[1/4] Python 3.12 missing — downloading installer (npmmirror mirror)...');
39
+ run('curl.exe', ['-L', '-o', INSTALLER, INSTALLER_URL]);
40
+ console.log('[1/4] Installing Python 3.12 to C:\\Users\\Administrator\\Python312 (user-level, silent)...');
41
+ spawnSync(INSTALLER, [
42
+ '/quiet', 'InstallAllUsers=0', 'TargetDir=C:\\Users\\Administrator\\Python312',
43
+ 'Include_pip=1', 'PrependPath=0', 'Include_test=0', 'Include_launcher=0'
44
+ ], { stdio: 'inherit' });
45
+ if (!existsSync(PY312)) {
46
+ console.error('!! Python install did not produce ' + PY312);
47
+ process.exit(1);
48
+ }
49
+ } else {
50
+ console.log('[1/4] Python 3.12 found at ' + PY312);
51
+ }
52
+
53
+ // 2. venv
54
+ if (!existsSync(VENV)) {
55
+ console.log('[2/4] Creating paddle_venv...');
56
+ mkdirSync(dirname(VENV), { recursive: true });
57
+ run(PY312, ['-m', 'venv', 'C:\\Users\\Administrator\\paddle_venv']);
58
+ } else {
59
+ console.log('[2/4] paddle_venv found');
60
+ }
61
+
62
+ // 3. paddlepaddle + paddleocr
63
+ const probe = spawnSync(VENV, ['-c', 'import paddleocr; print(paddleocr.__version__)'], { encoding: 'utf8' });
64
+ if (probe.status !== 0) {
65
+ console.log('[3/4] Installing paddlepaddle + paddleocr (Tsinghua mirror, ~1-3 min)...');
66
+ run(VENV, ['-m', 'pip', 'install', '-i', PYPI, '--upgrade', 'pip']);
67
+ run(VENV, ['-m', 'pip', 'install', '-i', PYPI, 'paddlepaddle==3.3.1', 'paddleocr']);
68
+ } else {
69
+ console.log(`[3/4] paddleocr already installed (${probe.stdout.trim()})`);
70
+ }
71
+
72
+ // 4. warm the model cache with one recognition
73
+ console.log('[4/4] Warming the model cache (first run downloads detection/recognition models)...');
74
+ const testImage = join(dirname(fileURLToPath(import.meta.url)), '..', 'tests', 'fixtures-out', 'ocr-test.png');
75
+ mkdirSync(join(CACHE), { recursive: true });
76
+ if (existsSync(testImage)) {
77
+ const warm = spawnSync(VENV, ['-c', [
78
+ 'from paddleocr import PaddleOCR',
79
+ "ocr = PaddleOCR(lang='ch', use_doc_orientation_classify=False, use_doc_unwarping=False, use_textline_orientation=False, enable_mkldnn=False)",
80
+ `result = ocr.predict(r'${testImage.replaceAll("'", "''")}')`,
81
+ 'print("warm-up OCR ok, lines:", sum(len(r.get("rec_texts") or []) for r in result))'
82
+ ].join('; ')], {
83
+ env: { ...process.env, PADDLE_PDX_CACHE_HOME: CACHE, PYTHONIOENCODING: 'utf-8' },
84
+ encoding: 'utf8'
85
+ });
86
+ if (warm.status !== 0) {
87
+ console.error('!! warm-up failed — see output above; the engine may still work once models download');
88
+ process.exit(1);
89
+ }
90
+ console.log(warm.stdout.trim());
91
+ } else {
92
+ console.log('[4/4] test image missing — skip warm-up (first image_ocr paddle call will download models)');
93
+ }
94
+
95
+ console.log('\nDone. image_ocr engine="paddle" is now available.');
96
+ console.log('Verify: ask the model to read an image with image_ocr(engine="paddle").');