dsh-pictor 0.1.0 → 0.2.0

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
@@ -1,81 +1,78 @@
1
- # Pictor — 文档转信息图工作台
1
+ # Pictor — Document-to-Infographic Workbench
2
2
 
3
- <p align="right"><i>English below.</i></p>
3
+ [ EN | [中文](./README.zh-cn.md) ]
4
4
 
5
- [源码仓库](https://github.com/bandung-circuits/pictor) · [问题与讨论](https://github.com/bandung-circuits/pictor/issues)
5
+ Pictor turns a document into a set of infographics. It is a [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) plugin published to npm as [`dsh-pictor`](https://www.npmjs.com/package/dsh-pictor), with source at [github.com/bandung-circuits/pictor](https://github.com/bandung-circuits/pictor). Each project is one coherent agent session that carries the whole extract → advise → render flow; the GUI is a thin layer that only shows file-fact state and shapes your input. Design decisions live in [docs/DESIGN.zh.md](docs/DESIGN.zh.md) (Chinese).
6
6
 
7
- Pictor 把一份文档变成一组信息图。它是 DeepSeek Harness(DSH)的插件:每新建一个项目,宿主创建一个连贯的会话作为项目会话,整个「提取 → 方案 → 渲染」流程都在这个会话里推进,GUI 只做两件事:查看信息、规整输入。设计文档见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md)。
7
+ ![Pictor empty workbench](docs/screenshot-empty.png)
8
8
 
9
- ## 形态
9
+ ## Installation
10
10
 
11
- - 安装后首次使用自动生成 `~/.pictor` 工作区(注册为 dsh 工作区,侧栏归组见「Pictor」)。
12
- - 唯一入口是 dsh 左下角 footer 按钮「Pictor」,打开 shell.overlay 工作台:左栏列项目,右栏显示当前项目。
13
- - 项目步骤固定三步:提取结构 → 方案设计 → 渲染出图。每步由你做决定后,会话在同一会话里接着执行(agent-loop 的 resume 语义),不重跑已有阶段。
14
- - 项目名取文档首行,信息条内随时可改名,只动 index.json。
15
- - 状态只由文件事实驱动;运行中判定以 dsh agent 注册表为权威;把你对 orchestration 的关心都留给会话。
11
+ Pictor is published to npm as `dsh-pictor`:
16
12
 
17
- ## 两个模型
13
+ ```bash
14
+ dsh plugin --profile <profile> add dsh-pictor
15
+ ```
18
16
 
19
- - 推理模型:extract 与 advise 用 dsh 当前默认模型,无需在 Pictor 配置。
20
- - 画图模型:在工作台「设置」里配置(seedream / gemini / openai-compatible / mock)。密钥经 dsh 凭据子系统保存,配置只留引用。
17
+ Replace `<profile>` with the target profile name (e.g. `desktop`, `web`) and start that profile. A **Pictor** button appears at the bottom-left of the dsh footer. On first use a `~/.pictor` home is provisioned and registered as a DSH workspace; project sessions group under "Pictor" in the sidebar. For development installs (local link, live-reload while editing source) see [docs/DESIGN.zh.md](docs/DESIGN.zh.md).
21
18
 
22
- ## 安装
19
+ **For non-technical users**: install [DSH Desktop](https://dshdesktop.com/en/) (a community project, not an official DeepSeek product; it bundles the DeepSeek Harness runtime as a ready-to-run desktop app) and let the agent install the plugin for you — just send this one message in dsh:
23
20
 
24
- Pictor 以 npm 包 `dsh-pictor` 发布:
21
+ > Install the Pictor plugin from the npm package `dsh-pictor` into the current profile, restart if needed, and confirm when a Pictor button appears at the bottom left.
25
22
 
26
- ```bash
27
- dsh plugin --profile <profile> add dsh-pictor
28
- ```
23
+ ## Usage
29
24
 
30
- 重启 profile 后,footer 左下角出现「Pictor」按钮。开发安装(改动源码实时联调)见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md)。
25
+ 1. Open the workbench via the **Pictor** footer button and click **New project**: upload a file (`md` / `txt` / `docx` / `pdf` / image) or paste content (from Word or the web; formatting is kept, stored as sanitised HTML).
26
+ 2. The session reads and normalises the source (`docx` / `pdf` / image / HTML → `document.md` via DSH tooling) and proposes candidate structures.
27
+ 3. Tick the structures and confirm; the session moves to proposal design. Tick a proposal, pick an aspect ratio, and click **Render selected**.
28
+ 4. Preview and download from the result grid; open the discussion panel to talk to the session at any point, or use the step bar to go back and redo existing artifacts.
31
29
 
32
- ## 使用
30
+ ![Pictor workbench with a project in progress](docs/screenshot-workbench.png)
33
31
 
34
- 1. 打开工作台,点「新建项目」:上传文件(md/txt/docx/pdf/图片)或粘贴内容(从 Word/网页粘贴,格式保留,落盘为消毒 HTML)。
35
- 2. 会话读取并规整来源(docx/pdf/图片/HTML → document.md 由会话用 dsh 工具完成),提取候选结构。
36
- 3. 勾选结构,确认,会话进入方案设计;勾选方案,设画面比例,点「渲染所选方案」。
37
- 4. 结果网格里预览、下载;随时可展开讨论面板对会话说话,或点步骤条回到已有产物的步骤重做。
32
+ ## Form
38
33
 
39
- ## 开发与验证
34
+ - A `~/.pictor` home is created on first use (registered as a DSH workspace; sidebar shows "Pictor").
35
+ - The only entry point is the bottom-left **Pictor** footer button, which toggles a `shell.overlay` workbench: projects on the left, the selected project on the right.
36
+ - Every project has three fixed steps: extract structures → design proposals → render images. After each human decision the **same session resumes** (agent-loop resume semantics); existing artifacts are never re-derived.
37
+ - The project name is taken from the document's first line and can be renamed any time in the info bar (touches only `index.json`).
38
+ - State is driven purely by file facts; running status is authoritative via the DSH agent registry; orchestration concerns are left to the session.
39
+
40
+ ## Two models
41
+
42
+ - **Reasoner**: extract and advise run on DSH's current default model — no Pictor configuration needed.
43
+ - **Image model**: configured in the workbench Settings (`seedream` / `gemini` / `openai-compatible` / `mock`). API keys go through DSH's credential subsystem; the configuration stores only a reference.
44
+
45
+ ## Development & verification
40
46
 
41
47
  ```bash
42
- npm run build # esbuild 构建 host + 拼接 client
43
- npm run verify # L1 单元 + L2 host 集成(mock ctx,15 项)
44
- npm run verify:integration # L3 transport 冒烟(真实 dsh web)
45
- npm run test:e2e # L4a 浏览器 e2e(fixture 数据,确定性)
48
+ npm run build # esbuild build of host + bundled client
49
+ npm run verify # L1 unit + L2 host integration (mock ctx, 15 items)
50
+ npm run verify:integration # L3 transport smoke against a real dsh web
51
+ npm run test:e2e # L4a browser e2e (fixture data, deterministic)
46
52
  ```
47
53
 
48
- 测试分层的定义与理由见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md) 第 7 节。
54
+ Test-layer definitions and rationale: [docs/DESIGN.zh.md](docs/DESIGN.zh.md) §7.
49
55
 
50
- ## 目录
56
+ ### Layout
51
57
 
52
58
  ```
53
59
  pictor/
54
- ├── agents/ # 声明式蓝图(orchestrator/extractor/advisor/renderer)
60
+ ├── agents/ # declarative blueprints (orchestrator/extractor/advisor/renderer)
55
61
  ├── references/domain/ # base-prompt + layouts/ + styles/ + diagram-types/ + visual-principles
56
- ├── src/host/ # 插件宿主:~/.pictor、项目会话、/pictor RPC、生图
57
- ├── src/client/ # 工作台 UI(React.createElement + 主题变量)
62
+ ├── src/host/ # plugin host: ~/.pictor, project sessions, /pictor RPC, image generation
63
+ ├── src/client/ # workbench UI (React.createElement + theme variables)
58
64
  ├── e2e/ # L4a Playwright
59
- ├── verify.mjs # L1+L2 离线冒烟
65
+ ├── verify.mjs # L1+L2 offline smoke
60
66
  └── scripts/ # build + transport-smoke
61
67
  ```
62
68
 
63
- 数据目录 `~/.pictor/`:`index.json`(项目索引)、`pictor-config.json`(画图模型配置)、`<项目 id>/`(自包含:agents/references 快照 + 10.input + 11.extraction + 12.advice + output)。
64
-
65
- ## 许可证
66
-
67
- MIT,见 [LICENSE](LICENSE)。
68
-
69
- ---
69
+ Data home `~/.pictor/`: `index.json` (project index), `pictor-config.json` (image-model configuration), `<project-id>/` (self-contained: agents/references snapshot + `10.input` + `11.extraction` + `12.advice` + `output`).
70
70
 
71
- ## English
71
+ ## Documents
72
72
 
73
- Pictor turns a document into a set of infographics. It is a DeepSeek Harness (DSH) plugin published on npm as [`dsh-pictor`](https://www.npmjs.com/package/dsh-pictor); the source lives at [github.com/bandung-circuits/pictor](https://github.com/bandung-circuits/pictor). Each project is one coherent agent session that carries the whole extract → advise → render flow, and the GUI is a thin layer that only shows file-fact state and shapes your input. Design decisions live in [docs/DESIGN.zh.md](docs/DESIGN.zh.md).
73
+ - Design decisions: [docs/DESIGN.zh.md](docs/DESIGN.zh.md) (Chinese)
74
+ - Verification checklist: [docs/verification-checklist.zh.md](docs/verification-checklist.zh.md) (Chinese)
74
75
 
75
- - A `~/.pictor` home is provisioned on first use and registered as a DSH workspace; all project sessions group under "Pictor" in the sidebar.
76
- - The only entry point is the bottom-left "Pictor" footer button, which toggles a `shell.overlay` workbench: projects on the left, the selected project on the right.
77
- - Every project has three fixed steps: extract structures → design proposals → render images. After each human decision the **same session resumes** to keep working; existing artifacts are never re-derived (file facts are the source of truth).
78
- - Reasoner: DSH's default model (no Pictor config). Image model: configured in the workbench Settings (seedream / gemini / openai-compatible / mock); the API key goes through DSH's credentials subsystem.
79
- - Tests: L1 unit + L2 host integration (`npm run verify`), L3 real-dsh transport smoke (`npm run verify:integration`), L4a browser e2e against a seeded fixture (`npm run test:e2e`).
76
+ ## License
80
77
 
81
- MIT licensed.
78
+ MIT — see [LICENSE](LICENSE). Third-party content notices: [NOTICE.md](NOTICE.md).
@@ -0,0 +1,78 @@
1
+ # Pictor — 文档转信息图工作台
2
+
3
+ [ [English](./README.md) | 中文 ]
4
+
5
+ Pictor 把一份文档变成一组信息图。它是 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(dsh)的插件,已发布到 npm 包 [`dsh-pictor`](https://www.npmjs.com/package/dsh-pictor),源码仓库在 [github.com/bandung-circuits/pictor](https://github.com/bandung-circuits/pictor)。每新建一个项目,宿主创建一个连贯的会话作为项目会话,整个「提取 → 方案 → 渲染」流程都在这个会话里推进,GUI 只做两件事:查看信息、规整输入。设计文档见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md)。
6
+
7
+ ![Pictor 初始工作台](docs/screenshot-empty.png)
8
+
9
+ ## 安装
10
+
11
+ 已发布到 npm,安装即一行命令:
12
+
13
+ ```bash
14
+ dsh plugin --profile <profile> add dsh-pictor
15
+ ```
16
+
17
+ 把 `<profile>` 换成目标 profile 名(如 `desktop`、`web`),然后启动该 profile,dsh 左下角会出现「Pictor」按钮。首次使用自动生成 `~/.pictor` 工作区(注册为 dsh 工作区,侧栏归组见「Pictor」)。开发安装(改动源码实时联调)见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md)。
18
+
19
+ 非技术用户可安装 [DSH Desktop](https://dshdesktop.com/en/)(社区维护项目,非 DeepSeek 官方产品,把 DeepSeek Harness 运行封装成可直接打开的桌面应用),然后让 dsh 里的 AI 帮装,发这一句即可:
20
+
21
+ > 帮我把 Pictor 插件装上,从 npm 包 dsh-pictor 装到当前 profile,需要的话重启,装好告诉我左下角有没有 Pictor 入口。
22
+
23
+ ## 使用
24
+
25
+ 1. 打开工作台,点「新建项目」:上传文件(md/txt/docx/pdf/图片)或粘贴内容(从 Word/网页粘贴,格式保留,落盘为消毒 HTML)。
26
+ 2. 会话读取并规整来源(docx/pdf/图片/HTML → document.md 由会话用 dsh 工具完成),提取候选结构。
27
+ 3. 勾选结构,确认,会话进入方案设计;勾选方案,设画面比例,点「渲染所选方案」。
28
+ 4. 结果网格里预览、下载;随时可展开讨论面板对会话说话,或点步骤条回到已有产物的步骤重做。
29
+
30
+ ![Pictor 项目工作中](docs/screenshot-workbench.png)
31
+
32
+ ## 形态
33
+
34
+ - 安装后首次使用自动生成 `~/.pictor` 工作区(注册为 dsh 工作区,侧栏归组见「Pictor」)。
35
+ - 唯一入口是 dsh 左下角 footer 按钮「Pictor」,打开 shell.overlay 工作台:左栏列项目,右栏显示当前项目。
36
+ - 项目步骤固定三步:提取结构 → 方案设计 → 渲染出图。每步由你做决定后,会话在同一会话里接着执行(agent-loop 的 resume 语义),不重跑已有阶段。
37
+ - 项目名取文档首行,信息条内随时可改名,只动 index.json。
38
+ - 状态只由文件事实驱动;运行中判定以 dsh agent 注册表为权威;把你对 orchestration 的关心都留给会话。
39
+
40
+ ## 两个模型
41
+
42
+ - 推理模型:extract 与 advise 用 dsh 当前默认模型,无需在 Pictor 配置。
43
+ - 画图模型:在工作台「设置」里配置(seedream / gemini / openai-compatible / mock)。密钥经 dsh 凭据子系统保存,配置只留引用。
44
+
45
+ ## 开发与验证
46
+
47
+ ```bash
48
+ npm run build # esbuild 构建 host + 拼接 client
49
+ npm run verify # L1 单元 + L2 host 集成(mock ctx,15 项)
50
+ npm run verify:integration # L3 transport 冒烟(真实 dsh web)
51
+ npm run test:e2e # L4a 浏览器 e2e(fixture 数据,确定性)
52
+ ```
53
+
54
+ 测试分层的定义与理由见 [docs/DESIGN.zh.md](docs/DESIGN.zh.md) 第 7 节。
55
+
56
+ ## 目录
57
+
58
+ ```
59
+ pictor/
60
+ ├── agents/ # 声明式蓝图(orchestrator/extractor/advisor/renderer)
61
+ ├── references/domain/ # base-prompt + layouts/ + styles/ + diagram-types/ + visual-principles
62
+ ├── src/host/ # 插件宿主:~/.pictor、项目会话、/pictor RPC、生图
63
+ ├── src/client/ # 工作台 UI(React.createElement + 主题变量)
64
+ ├── e2e/ # L4a Playwright
65
+ ├── verify.mjs # L1+L2 离线冒烟
66
+ └── scripts/ # build + transport-smoke
67
+ ```
68
+
69
+ 数据目录 `~/.pictor/`:`index.json`(项目索引)、`pictor-config.json`(画图模型配置)、`<项目 id>/`(自包含:agents/references 快照 + 10.input + 11.extraction + 12.advice + output)。
70
+
71
+ ## 文档
72
+
73
+ - 设计与决策:[docs/DESIGN.zh.md](docs/DESIGN.zh.md)
74
+ - 验证清单:[docs/verification-checklist.zh.md](docs/verification-checklist.zh.md)
75
+
76
+ ## 许可证
77
+
78
+ MIT,见 [LICENSE](LICENSE)。第三方内容声明见 [NOTICE.md](NOTICE.md)。
Binary file
Binary file
package/lib/client.js CHANGED
@@ -303,6 +303,7 @@ const I18N = {
303
303
  layoutLabel: '布局', styleLabel: '风格', metaConfirmedHint: '确认后作为项目名与提取依据,可编辑。',
304
304
  chooseLayout: '选择布局', chooseStyle: '选择风格', resetToAi: '回到 AI 推荐',
305
305
  layoutChanged: '·已改', aiSuggestion: 'AI 推荐',
306
+ batchStyleLabel: '批量设置风格', batchStyleHint: '为所选方案套用同一风格(未选则全部)',
306
307
  close: '关闭',
307
308
  discussion: '讨论', discussHint: '对项目会话自由说话:补充参考、要求搜索、调整方向。你的决定会在同一会话里接着执行。',
308
309
  discussPlaceholder: '例如:先不要继续,帮我查一下这篇文章提到的背景…',
@@ -360,6 +361,7 @@ const I18N = {
360
361
  layoutLabel: 'Layout', styleLabel: 'Style', metaConfirmedHint: 'Confirmed as the project name and extraction basis; editable.',
361
362
  chooseLayout: 'Choose layout', chooseStyle: 'Choose style', resetToAi: 'Reset to AI',
362
363
  layoutChanged: '· edited', aiSuggestion: 'AI',
364
+ batchStyleLabel: 'Batch style', batchStyleHint: 'Apply one style to all selected proposals (all if none selected)',
363
365
  close: 'Close',
364
366
  discussion: 'Discussion', discussHint: 'Talk to the project session: add context, ask for research, adjust direction. Your decisions continue it in the same session.',
365
367
  discussPlaceholder: 'e.g. Hold on — research the background this article mentions…',
@@ -793,9 +795,17 @@ function ProposalsPanel({ stage, proposals, suggestedStyle, steps, selected, onT
793
795
  const ratioFor = (id) => ratios[id] || '16:9'
794
796
  const activePrompt = promptId ? proposals.find((x) => x.id === promptId) || null : null
795
797
 
798
+ function batchTargetIds() {
799
+ return selected.length ? selected : proposals.map((p) => p.id)
800
+ }
796
801
  function pickOption(name) {
797
802
  const cur = pick
798
803
  if (!cur) return
804
+ if (cur.batch) {
805
+ batchTargetIds().forEach((id) => setSty(id, name))
806
+ setPick(null)
807
+ return
808
+ }
799
809
  if (cur.kind === 'layout') setLay(cur.id, name)
800
810
  else setSty(cur.id, name)
801
811
  setPick(null)
@@ -803,6 +813,11 @@ function ProposalsPanel({ stage, proposals, suggestedStyle, steps, selected, onT
803
813
  function resetCurrent() {
804
814
  const cur = pick
805
815
  if (!cur) return
816
+ if (cur.batch) {
817
+ batchTargetIds().forEach((id) => setSty(id, undefined))
818
+ setPick(null)
819
+ return
820
+ }
806
821
  if (cur.kind === 'layout') setLay(cur.id, undefined)
807
822
  else setSty(cur.id, undefined)
808
823
  setPick(null)
@@ -859,34 +874,60 @@ function ProposalsPanel({ stage, proposals, suggestedStyle, steps, selected, onT
859
874
  : null
860
875
 
861
876
  const chooserModal = pick ? (function chooser() {
862
- const p = proposals.find((x) => x.id === pick.id)
863
- if (!p) return null
864
877
  const kind = pick.kind
878
+ const batch = Boolean(pick.batch)
879
+ const p = batch ? null : proposals.find((x) => x.id === pick.id)
880
+ if (!batch && !p) return null
865
881
  const list = kind === 'layout'
866
- ? (layouts && layouts.length ? layouts : [p.suggested_layout])
867
- : (styles && styles.length ? styles : [p.suggested_style || suggestedStyle])
868
- const base = kind === 'layout' ? p.suggested_layout : (p.suggested_style || suggestedStyle)
869
- const current = kind === 'layout' ? (lay[pick.id] || p.suggested_layout) : (sty[pick.id] || p.suggested_style || suggestedStyle)
870
- const changed = kind === 'layout'
871
- ? Boolean(lay[pick.id] && lay[pick.id] !== base)
872
- : Boolean(sty[pick.id] && sty[pick.id] !== base)
882
+ ? (layouts && layouts.length ? layouts : (p ? [p.suggested_layout] : []))
883
+ : (styles && styles.length ? styles : (p ? [p.suggested_style || suggestedStyle] : (suggestedStyle ? [suggestedStyle] : [])))
884
+ const base = batch ? '' : (kind === 'layout' ? p.suggested_layout : (p.suggested_style || suggestedStyle))
885
+ // batch:高亮所有目标方案共享的同一风格(若有),否则不高亮
886
+ let current
887
+ if (batch) {
888
+ const ids = batchTargetIds()
889
+ const eff = ids.map((id) => {
890
+ const pp = proposals.find((x) => x.id === id)
891
+ return (pp ? (sty[id] || pp.suggested_style || suggestedStyle || '') : (sty[id] || ''))
892
+ })
893
+ current = eff.length && eff.every((s) => s === eff[0]) ? eff[0] : ''
894
+ } else {
895
+ current = kind === 'layout' ? (lay[pick.id] || p.suggested_layout) : (sty[pick.id] || p.suggested_style || suggestedStyle)
896
+ }
897
+ const changed = batch
898
+ ? ids_haveAnyOverrideStyle()
899
+ : (kind === 'layout' ? Boolean(lay[pick.id] && lay[pick.id] !== base) : Boolean(sty[pick.id] && sty[pick.id] !== base))
900
+ function ids_haveAnyOverrideStyle() {
901
+ return batchTargetIds().some((id) => Boolean(sty[id]))
902
+ }
873
903
  const items = list.map((n) => h('div', { key: n, className: 'pt-preview-item' + (n === current ? ' sel' : ''), onClick: () => pickOption(n) },
874
- n === base ? h('span', { className: 'ai-chip' }, t('aiSuggestion')) : null,
904
+ (!batch && n === base) ? h('span', { className: 'ai-chip' }, t('aiSuggestion')) : null,
875
905
  h('img', { src: '/pictor/preview/' + (kind === 'layout' ? 'layouts' : 'styles') + '/' + encodeURIComponent(n) + '.webp', alt: kind === 'layout' ? layoutLabel(n) : n }),
876
906
  h('div', { className: 'nm' }, kind === 'layout' ? layoutLabel(n) : n)))
907
+ const title = batch ? t('batchStyleLabel') : (kind === 'layout' ? t('chooseLayout') : t('chooseStyle'))
908
+ const batchSub = batch ? h('p', { className: 'pt-meta', style: { margin: '0 0 10px' }, key: 'sub' },
909
+ t('batchStyleHint') + ' · ' + t('selectCount', { s: selected.length, t: proposals.length })) : null
877
910
  return h('div', { className: 'pt-modal-backdrop', onClick: () => setPick(null) },
878
911
  h('div', { className: 'pt-modal pt-modal-wide', onClick: (e) => e.stopPropagation() },
879
912
  h('div', { className: 'pt-modal-head' },
880
- h('h3', { className: 'pt-option-title', style: { margin: 0 } }, kind === 'layout' ? t('chooseLayout') : t('chooseStyle')),
913
+ h('h3', { className: 'pt-option-title', style: { margin: 0 } }, title),
881
914
  h('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
882
915
  changed ? h('button', { className: 'pt-btn', style: { padding: '6px 12px', fontSize: 13 }, onClick: resetCurrent }, t('resetToAi')) : null,
883
916
  h('button', { className: 'pt-modal-close', onClick: () => setPick(null), title: t('close'), 'aria-label': t('close') }, '×'))),
884
- h('div', { className: 'pt-modal-body' }, h('div', { className: 'pt-preview-grid' }, items))))
917
+ h('div', { className: 'pt-modal-body' }, batchSub, h('div', { className: 'pt-preview-grid' }, items))))
885
918
  })() : null
886
919
 
920
+ const styleList = (styles && styles.length ? styles : (suggestedStyle ? [suggestedStyle] : []))
921
+ const batchStyleBtn = styleList.length ? h('button', {
922
+ className: 'pt-btn', style: { padding: '4px 10px', fontSize: 13 }, disabled: proposals.length === 0,
923
+ onClick: () => setPick({ id: null, kind: 'style', batch: true }),
924
+ title: t('batchStyleHint'),
925
+ }, t('batchStyleLabel')) : null
926
+
887
927
  const actionRow = h('div', { className: 'pt-card-row', style: { marginTop: 18 } },
888
- h('div', { style: { display: 'flex', gap: 10, alignItems: 'center' } },
928
+ h('div', { style: { display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' } },
889
929
  h('button', { className: 'pt-btn', onClick: onToggleAll }, allSelected ? t('deselectAll') : t('selectAll')),
930
+ batchStyleBtn,
890
931
  h('span', { className: 'pt-meta' }, t('selectCount', { s: selected.length, t: proposals.length }))),
891
932
  hasImageKey === false
892
933
  ? h('p', { className: 'pt-warn', style: { margin: 0 } }, t('imgKeyHint'))
@@ -124,7 +124,7 @@ class DashScopeProvider {
124
124
  const base = this.cfg.baseUrl || "https://dashscope.aliyuncs.com/api/v1";
125
125
  const size = DASHSCOPE_SIZE_MAP[aspectRatio] || "1024*1024";
126
126
  const url = base.replace(/\/$/, "") + "/services/aigc/multimodal-generation/generation";
127
- const parameters = { size, prompt_extend: true, watermark: false };
127
+ const parameters = { size, watermark: false };
128
128
  const body = {
129
129
  model,
130
130
  input: { messages: [{ role: "user", content: [{ type: "text", text: prompt }] }] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-pictor",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pictor:文档转信息图工作台。每项目一个连贯 dsh 会话,GUI 为 shell.overlay 工作台(左栏项目、右栏三步:提取/方案/渲染),行为全部交给 DSH 智能运行时。",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,4 +1,4 @@
1
- Create a professional infographic following these specifications:
1
+ Create a professional infographic. This prompt separates what to render from how to render it: the Content section is the subject matter, data, and labels to put in the infographic; the Layout and Style sections are visual specifications (structure and aesthetic — they describe the look, not the subject). Put the content in first, then apply the layout and style to present it.
2
2
 
3
3
  ## Image Specifications
4
4
 
@@ -8,6 +8,13 @@ Create a professional infographic following these specifications:
8
8
  - **Aspect Ratio**: {{ASPECT_RATIO}}
9
9
  - **Language**: {{LANGUAGE}}
10
10
 
11
+ ## Content — the subject matter, data, and labels to render
12
+
13
+ {{CONTENT}}
14
+
15
+ Text labels (in {{LANGUAGE}}):
16
+ {{TEXT_LABELS}}
17
+
11
18
  ## Core Principles
12
19
 
13
20
  - Follow the layout structure precisely for information architecture
@@ -26,19 +33,10 @@ Create a professional infographic following these specifications:
26
33
  - Labels should be clear and appropriately sized
27
34
  - Use the specified language for all text content
28
35
 
29
- ## Layout Guidelines
36
+ ## Layout Guidelines — visual structure (how to arrange; not content)
30
37
 
31
38
  {{LAYOUT_GUIDELINES}}
32
39
 
33
- ## Style Guidelines
34
-
35
- {{STYLE_GUIDELINES}}
40
+ ## Style Guidelines — visual aesthetic (how it looks; not subject matter)
36
41
 
37
- ---
38
-
39
- Generate the infographic based on the content below:
40
-
41
- {{CONTENT}}
42
-
43
- Text labels (in {{LANGUAGE}}):
44
- {{TEXT_LABELS}}
42
+ {{STYLE_GUIDELINES}}
@@ -1,6 +1,6 @@
1
1
  # bandung-circuit
2
2
 
3
- Editorial illustration style of the Bandung Circuit column — a muted, paper-printed aesthetic in the lineage of left publishing-house interior pages (Tricontinental, Verso, *New Left Review*). Cream stock, duotone restraint, thin-line hand-drawn icons, and typography-led hierarchy. Reads as the inside of a thoughtful journal, not a marketing poster.
3
+ Editorial illustration style — a muted, paper-printed aesthetic. Cream stock, duotone restraint, thin-line hand-drawn icons, and typography-led hierarchy. Reads as an interior print spread, not a marketing poster.
4
4
 
5
5
  ## Color Palette
6
6
 
@@ -25,10 +25,10 @@ Editorial illustration style of the Bandung Circuit column — a muted, paper-pr
25
25
 
26
26
  ## Typography
27
27
 
28
- - **Font family: Poppins** — used as the single typeface across the entire composition (headlines, body, labels, captions, numerals). Poppins is a geometric sans with humanist warmth, which suits Bandung Circuit's editorial-but-warm register; do not pair it with a second family.
28
+ - **Font family: Poppins** — used as the single typeface across the entire composition (headlines, body, labels, captions, numerals). Poppins is a geometric sans with humanist warmth, which suits an editorial-but-warm register; do not pair it with a second family.
29
29
  - Headlines: Poppins SemiBold or Bold — uppercase or title case acceptable, tracking slightly opened
30
30
  - Body text and labels: Poppins Regular or Medium — comfortable reading weight
31
- - All-caps short labels for block headers (ONE CORE, FOUR PILLARS, etc.) — Poppins Medium/SemiBold, set in cream on solid duotone bar
31
+ - All-caps short labels for block headers — Poppins Medium/SemiBold, set in cream on solid duotone bar
32
32
  - Sub-captions and explanatory phrases beneath block titles: Poppins Italic or Light
33
33
  - Numerals match the body face (Poppins lining figures) — no oversized statement numerals (this style trusts the layout, not hero stats)
34
34
 
@@ -49,13 +49,9 @@ Editorial illustration style of the Bandung Circuit column — a muted, paper-pr
49
49
  - Use filled / solid icons (Material-style) — icons are line-art only
50
50
  - Introduce a fourth or fifth color — composition must feel like a duotone with a single accent
51
51
  - Use glossy or "tech product" iconography — keep icons modest and editorial
52
- - Use heavy textures, parchment overlays, ink splatter, or cross-hatching (that is `aged-academia`, not Bandung)
52
+ - Use heavy textures, parchment overlays, ink splatter, or cross-hatching (that is `aged-academia`, not this style)
53
53
  - Use bold red blocks or full-bleed red panels (that is `tricon-infographic`, the poster sibling — not this)
54
- - Crowd the canvas — Bandung Circuit pages always breathe
55
-
56
- ## Best For
57
-
58
- Editorial illustrations for left-publishing journal articles, conceptual frameworks, multi-pillar architectures, theoretical model diagrams, methodology overviews, Global South research publications, long-form essay infographics, in-text figures (not standalone posters)
54
+ - Crowd the canvas — these pages always breathe
59
55
 
60
56
  ## Recommended Pairings
61
57
 
@@ -67,16 +63,13 @@ Editorial illustrations for left-publishing journal articles, conceptual framewo
67
63
 
68
64
  ## Distinction from Sibling Styles
69
65
 
70
- - **vs `tricon-infographic`**: That style is the *cover/poster* voice of the same publisher — bold red on white, authoritative, full-bleed. Bandung Circuit is the *interior/essay* voice — cream stock, duotone, quiet.
71
- - **vs `aged-academia`**: That style is sepia parchment with cross-hatched scientific illustration. Bandung Circuit is contemporary editorial print, not historical specimen plate.
72
- - **vs `mckinsey-report`**: That style is white-background institutional consulting with serif/sans dual typography. Bandung Circuit is warm-paper publishing-house aesthetic with single sans family and political-intellectual register.
66
+ - **vs `tricon-infographic`**: That style is a poster voice — bold red on white, authoritative, full-bleed. `bandung-circuit` is an interior/essay voice — cream stock, duotone, quiet.
67
+ - **vs `aged-academia`**: That style is sepia parchment with cross-hatched scientific illustration. `bandung-circuit` is contemporary editorial print, not historical specimen plate.
68
+ - **vs `mckinsey-report`**: That style is white-background institutional consulting with serif/sans dual typography. `bandung-circuit` is a warm-paper aesthetic with a single sans family.
73
69
 
74
70
  ## Register
75
71
 
76
72
  - formal-casual: formal but warm
77
73
  - abstract-representational: slightly abstract
78
74
  - bright-dark: bright (warm-bright, not white-bright)
79
- - digital-handcrafted: leans handcrafted (digital execution with hand-drawn iconography)
80
- - cultural-anchor: left publishing-house interior page design (Tricontinental, Verso, *New Left Review*, *Monthly Review*) — editorial register of the international left intellectual tradition
81
- - best-for: in-text editorial illustrations for journal articles, conceptual diagrams in long-form essays, Global South research publications
82
- - avoid-for: marketing posters, product UI, children's content, entertainment, corporate consulting decks, social-media graphics requiring stopping power
75
+ - digital-handcrafted: leans handcrafted (digital execution with hand-drawn iconography)