{"id":"532db5d1-d6d4-4043-80dc-7c690fb99c9b","slug":"ai-image-master","kind":"skill","name":"ai-image-master","summary":"AI Image Master - Electron desktop application for AI image generation with multiple models support (Gemini, Seedream, Sora, Flux)","owner":"2799662352","repoUrl":"https://github.com/2799662352/ai-image-master","license":"MIT","licenseTier":"permissive","trust":"community","stars":8,"match":null,"fewStars":false,"format":"skill","createdAt":"2026-01-28T03:44:00.000Z","addedAt":"2026-10-21T16:42:56.763Z","logoUrl":"https://avatars.githubusercontent.com/u/90034279?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["design-media"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-25T18:11:09.136Z","sample":false,"url":"https://conduits.dev/tools/ai-image-master","language":"TypeScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# CATIMATION Cyberpunk Master / AI Image Master\n\nAn open-source Electron desktop workspace for AI-assisted image and video creation, visual storyboarding, and creative production workflows.\n\n**Downloads:** Official Windows installers are available from [GitHub Releases](https://github.com/2799662352/ai-image-master/releases).\n\n## Code signing policy\n\nFree code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/).\n\nOfficial Windows releases follow the project's [Code signing policy](docs/CODE_SIGNING_POLICY.md). Data handling is documented in the [Privacy Policy](docs/PRIVACY.md).\n\n## 中文简介\n\nAI 图片生成桌面应用 — Electron + React + LangChain/LangGraph。\n支持文生图、参考图、批量生成、Director 多阶段流水线和 StoryboardPro 分镜系统。\n\n![TypeScript](https://img.shields.io/badge/TypeScript-95%25-3178C6?style=flat-square&logo=typescript&logoColor=white)\n![Electron](https://img.shields.io/badge/Electron-35-47848F?style=flat-square&logo=electron&logoColor=white)\n![Vite](https://img.shields.io/badge/Vite-6-646CFF?style=flat-square&logo=vite&logoColor=white)\n![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)\n\n## 核心功能\n\n| 模块 | 描述 |\n|------|------|\n| **图片生成** | 多模型 (Gemini / Seedream / Sora / Flux Kontext)，参考图上传，多分辨率 (1K–4K) |\n| **Director 模式** | LangGraph 4-pass 流水线: 技能选择→场景分析→角色锚点→设计组装→一致性校验 |\n| **StoryboardPro** | 九宫格分镜、剧场版动画、电影级模板、Sora2 视频提示词生成 |\n| **批量生成** | 抽卡模式 + 多提示词模式，智能并发队列 |\n| **模型对比** | 双模型同时生成对比，参考图自动同步 |\n| **图像理解** | 多模型图像分析，流式输出 |\n| **历史记录** | 自动保存 + R2 云端存储，批量下载/删除，虚拟滚动 |\n| **Skills 系统** | 内置 + 用户自定义 Skill (Markdown)，运行时热加载 |\n| **国际化** | 简中 / 繁中 / English / Русский |\n\n## 快速开始\n\n```bash\ngit clone https://github.com/2799662352/ai-image-master.git\ncd ai-image-master\npnpm bootstrap           # pnpm install + 下载 Codex 二进制 (~239MB)\npnpm dev                 # 开发模式 (electron-vite)\npnpm build               # 构建并打包\npnpm build:win           # Windows 安装包\n```\n\n`pnpm bootstrap` 是 fresh clone 或新建 `git worktree` 后的一站式初始化:跑 `pnpm install`(自动触发 `prisma generate` + `electron-builder install-app-deps`)+ `pnpm codex:fetch` 把 Codex CLI 二进制下到 `resources/codex/`。`.gitignore` 排除了那个目录(避免 200+MB 进仓),所以新 worktree 不跑 bootstrap 会在启动时报 `CodexLocalBackend.send called before start`(本质是 `spawn codex.exe ENOENT`)。\n\n如果你已经在主 worktree 跑过 `codex:fetch`,新建 worktree 时可以直接 `Copy-Item ../../resources/codex/* ./resources/codex/ -Recurse`(或 `cp -r`),比重新下 239MB 快得多。\n\n纯 Electron 桌面应用,无 Docker 部署。\n\n## 远程 apiyi 网关 / BYOK(可选)\n\n(桌面端内置的 apiyi MCP 已于 2026-10-30 移除,本节说的是**自己服务器上**独立部署的版本,不受影响。)\n\n如果想跨设备/多人共用 `apiyi-mcp-server` 的能力 —— 在自己的服务器上挂一个 HTTPS endpoint,\n**每个用户填自己的 apiyi key**(BYOK,Bring Your Own Key):\n\n```\n[ Client ] ─ Authorization: Bearer sk-xxx ─→ https://api.example.com/mcp\n                                                    │\n                                                    ▼ (HTTPS / CDN / DDoS / WAF)\n                                          [ 腾讯 EdgeOne (或其他 CDN) ]\n                                                    │ 回源 HTTP:80\n                                                    ▼\n                                            [ apiyi-fastmcp (Python) ]\n                                                    │ per call: read Bearer,\n                                                    │ build google-genai client\n                                                    ▼\n                                              api.apiyi.com\n```\n\n实现细节:`deploy/apiyi-fastmcp/server.py` 用 **FastMCP** 重写了 `apiyi-mcp-server`(原 Node 版)\n的两个工具 `generate_content` + `generate_content_batch`,直接调用 `google-genai` Python SDK\n打到 apiyi 网关。**API key 来自每次请求的 Authorization 头**,服务端不持有任何 key、不缓存任何 key,\n是目前主流 MCP 网关(Docker MCP Gateway / Supergateway / mcp-proxy)都不原生支持的 BYOK 模式。\n\n源站只跑 FastMCP 监听 80 端口,HTTPS 由前面的 CDN / 反代统一终止(我们用的是腾讯 EdgeOne;\n没有 CDN 就翻 git 历史拉回 Caddy 配置自己签 Let's Encrypt 证书,二选一)。\n\n完整方案见 [`deploy/README.md`](./deploy/README.md) —— 包含 EdgeOne 配置、运维、安全模型、\n客户端 JSON 配置示例(支持 Cursor / Codex / Claude Desktop)。\n\n镜像已发布到 Docker Hub:[`zuozuoliang999/apiyi-fastmcp`](https://hub.docker.com/r/zuozuoliang999/apiyi-fastmcp)\n(linux/amd64)。在自己的服务器上一行 `docker run` 即可拉起:\n\n```bash\ndocker run -d --name apiyi-fastmcp --restart unless-stopped \\\n  -p 80:8000 \\\n  -e APIYI_BASE_URL=https://api.apiyi.com \\\n  -e GEMINI_MODEL=gemini-3.1-pro-preview-thinking \\\n  -e FASTMCP_HOST=0.0.0.0 -e FASTMCP_PORT=8000 \\\n  -e FASTMCP_PATH=/mcp -e FASTMCP_TRANSPORT=http \\\n  zuozuoliang999/apiyi-fastmcp:latest\n```\n\n## 项目结构\n\n```\nai-image-master/\n├── src/\n│   ├── main/                           # Electron 主进程\n│   │   └── index.ts\n│   ├── preload/                        # IPC 桥接 (contextBridge)\n│   │   └── index.ts\n│   ├── types/                          # 全局类型定义\n│   └── renderer/                       # 渲染进程\n│       └── src/\n│           ├── pages/                  # 页面模块\n│           │   ├── GeneratePage.ts     # 图片生成\n│           │   ├── BatchPage.ts        # 批量生成\n│           │   ├── ComparePage.ts      # 模型对比\n│           │   ├── HistoryPage.ts      # 历史记录\n│           │   ├── UnderstandPage.ts   # 图像理解\n│           │   └── PromptTemplates.ts  # 提示词模板\n│           ├── services/               # 服务层\n│           │   ├── pipeline/           # Director 流水线\n│           │   │   ├── DirectorPipeline.ts\n│           │   │   ├── BasePipeline.ts\n│           │   │   └── director-skills.ts\n│           │   ├── storyboard-pipeline/ # StoryboardPro\n│           │   ├── api/                # API 调用\n│           │   ├── r2-storage/         # R2 云存储\n│           │   ├── cache/              # 图片缓存\n│           │   ├── storage/            # 本地存储\n│           │   ├── i18n/               # 国际化\n│           │   ├── version-checker/    # 版本检查\n│           │   ├── LangChainDirectorService.ts\n│           │   ├── LangChainStoryboardService.ts\n│           │   └── ServiceBridge.ts    # 服务总线\n│           ├── features/               # 功能模块 (18+)\n│           │   ├── model-selector/     # 模型选择器\n│           │   ├── tab-manager/        # 标签管理\n│           │   ├── history/            # 历史数据\n│           │   ├── settings/           # 设置面板\n│           │   ├── image-viewer/       # 图片查看器\n│           │   ├── intelligent-resize/ # 智能缩放\n│           │   ├── keyboard/           # 快捷键\n│           │   ├── language/           # 语言切换\n│           │   ├── updater/            # 自动更新\n│           │   └── ...\n│           ├── core/                   # 核心模块\n│           │   ├── AppBootstrap.ts     # 应用引导\n│           │   ├── Router.ts           # SPA 路由\n│           │   ├── EventBus.ts         # 事件总线\n│           │   ├── VirtualScroller.ts  # 虚拟滚动\n│           │   └── RetryManager.ts     # 重试管理\n│           ├── utils/                  # 工具函数\n│           └── styles/                 # 样式\n│\n├── electron/                           # Electron 入口文件\n│   ├── main.js\n│   └── preload.js\n│\n├── skills/                             # Skills 定义 (19 个)\n│   ├── director-*/                    # Director 流水线 Skill (12 个)\n│   │   ├── director-anchor-extraction-quality\n│   │   ├── director-anime-quality-boost\n│   │   ├── director-character-consistency\n│   │   ├── director-cinematic-composition\n│   │   ├── director-lighting-continuity\n│   │   ├── director-narrative-flow\n│   │   ├── director-prompt-engineering\n│   │   ├── director-scene-analysis-depth\n│   │   ├── director-shot-sequence-patterns\n│   │   ├── director-structured-captioning\n│   │   ├── director-style-consistency\n│   │   └── director-visual-continuity\n│   └── storyboard-*/                  # StoryboardPro Skill (7 个)\n│       ├── storyboard-audio\n│       ├── storyboard-dialogue\n│       ├── storyboard-dodge\n│       ├── storyboard-physics\n│       ├── storyboard-structure\n│       ├── storyboard-style\n│       └── storyboard-visual\n│\n├── .agents/skills/                     # Deep Agents 元数据\n│   ├── deep-agents-core/\n│   ├── deep-agents-memory/\n│   └── deep-agents-orchestration/\n│\n├── docs/                               # 项目文档\n│   └── plans/                         # 功能实现计划 (100+)\n├── tests/                              # 单元测试 (Vitest)\n├── e2e/                                # E2E 测试 (Playwright)\n├── i18n/                               # 翻译资源\n├── images/                             # 参考素材\n└── config/                             # 构建配置\n```\n\n## Director 流水线\n\nLangGraph 驱动的 4-pass 多阶段图片生成流水线:\n\n```\nPass 1: 技能选择 + 场景分析\n    → 从 19 个 Skill 中选择最匹配的\n    → 深度场景理解 (角色/环境/氛围)\n\nPass 2: 角色锚点提取\n    → 角色一致性保证\n    → 关键特征锚定\n\nPass 3: 设计组装\n    → 结构化 prompt 生成\n    → 风格/构图/光影参数\n\nPass 4: 一致性校验\n    → 跨镜头角色一致\n    → 视觉连续性检查\n```\n\n### Director Skill 列表\n\n| Skill | 描述 |\n|-------|------|\n| `anchor-extraction-quality` | 角色锚点提取优化 |\n| `anime-quality-boost` | 动画画质增强 |\n| `character-consistency` | 角色一致性保证 |\n| `cinematic-composition` | 电影构图 |\n| `lighting-continuity` | 光影连续性 |\n| `narrative-flow` | 叙事流畅性 |\n| `prompt-engineering` | 提示词工程 |\n| `scene-analysis-depth` | 场景深度分析 |\n| `shot-sequence-patterns` | 镜头序列模式 |\n| `structured-captioning` | 结构化描述 |\n| `style-consistency` | 风格一致性 |\n| `visual-continuity` | 视觉连续性 |\n\n## StoryboardPro 风格模板\n\n| 模板 | 说明 | 特点 |\n|------|------|------|\n| `theatrical` | 剧场版动画 | 日式权重标记 `((style:1.5))` |\n| `cinematic` | 电影级九宫格 | 分形几何，角色一致性 |\n| `anime` | 动画截图 | 赛璐璐着色 |\n| `manga` | 漫画分镜 | 黑白墨线，网点 |\n| `movie` | 电影分镜 | 宽银幕，戏剧光影 |\n| `webtoon` | 韩漫/条漫 | 全彩，纵向排版 |\n\n## Skills 系统\n\n### 加载策略\n\n```\n最终 Skill 集 = 内置 skills/ + 用户 userData/skills/\n同名冲突: 用户 Skill 覆盖内置\n格式异常: 跳过不中断\n运行时热加载: 渲染进程手动刷新生效\n```\n\n### 用户自定义 Skill\n\n打包后用户可在 `app.getPath('userData')/skills/<skill-name>/SKILL.md` 添加自定义 Skill，刷新后即时生效。\n\n## 技术栈\n\n| 分类 | 技术 |\n|------|------|\n| 框架 | Electron 35 |\n| 构建 | Vite 6 + electron-vite |\n| 语言 | TypeScript 5.7 |\n| 样式 | Tailwind CSS 4 |\n| AI | LangChain + LangGraph |\n| 存储 | Cloudflare R2 |\n| 测试 | Vitest + Playwright |\n| 国际化 | 4 语言 (zh-CN/zh-TW/en/ru) |\n\n## Codex 页 DeepSeek (Miau) 通道:选择器次序与记忆模型\n\n2026-10-30 调整(`src/shared/modelSettings.ts`、`src/main/agent/gatewayModelRouting.ts`):\n\n- **选择器次序**:DeepSeek 组按 `火山 V4.1 Flash` → `火山 V4.1 Flash 经济版` → `DeepSeek V4 Pro` → `DeepSeek Flash` 排列(火山在前,官方在后,`v4-pro` 先于 `flash`)。次序以 `CANONICAL_MODEL_SETTINGS_ROWS` 为准;通道默认模型仍是 `deepseek-flash`。\n- **记忆模型**(`memories.extract_model` / `consolidation_model`)与会话自动起标题的模型:四个 DeepSeek 模型(火山两个 + 官方两个)**一律走火山标准版 `deepseek-v4-1-flash-261017`**(原为 `deepseek-flash`),在通道的 `memoriesModel` 里写一个固定值。\n  - 为什么是一个值而不是按对话模型分路:记忆模型是启动 Codex 时写死的 `-c` 参数,而同一通道内切换模型不会重启 Codex,分路会让它停在启动那一刻的值上(切换后过期)。一个值就没有这个问题,也不需要映射表。\n  - 通道默认对话模型不变,仍是 `deepseek-flash`。\n- **代价**:官方两个模型的用户也按火山标准版计费;同一时段它的单价是 `deepseek-flash` 的 6.7 倍(输入 ¥1 对 ¥0.15、输出 ¥4 对 ¥0.6,每百万 token,缓存命中的输入另有折扣)。**2026-10-30 按网关扣费日志实测(不是价格表推算)**:一次记忆提取(阶段 1,约 5 万输入 / 4 千输出 token)≈ ¥0.07;一次记忆合并(阶段 2,23 次请求、约 99 万输入 token 其中 97% 命中缓存、1.6 万输出)≈ ¥0.22;一次完整整理合计约 ¥0.3。按 6.7 倍推算,同样的工作用 `deepseek-flash` 约 ¥0.045(推算值,没有实测)。**此前文档里写的「单次记忆请求约 ¥0.0005 对 ¥0.00007、绝对值很小」是只按一条很小的请求估的,低估了阶段 2,以本条为准**。这些费用会出现在用量明细里;合并只在有新的提取结果时才跑,频率取决于用户开新会话的节奏。\n- **已验证(2026-10-30,对生产 Miau 网关,真实内置 Codex + 本机转发代理)**:`memories.extract_model` / `consolidation_model` = `deepseek-v4-1-flash-261017`,聊天模型 `deepseek-flash`。① 阶段 1(提取):`/v1/responses`、`stream=true`、`text.format=json_schema`、`reasoning.effort=low`、0 个工具,HTTP 200,返回合法结构化结果,`stage1_outputs` 落库、任务 `done`;② 阶段 2(合并):合并 agent 用同一个模型(`effort=medium`、9 个工具)连续 24 次请求,**全部 HTTP 200**,任务 `done`(耗时约 130 秒),写出 `MEMORY.md`(6.9 KB)、`memory_summary.md`(4.1 KB)、`raw_memories.md`(5.6 KB)、1 份 rollout 摘要和 1 个自动生成的技能,agent 最后一句是 \"Phase 2 complete\"。聊天轮(`deepseek-flash`)与记忆请求(火山标准版)走的是两个不同的模型,互不影响。\n- **没有覆盖到的**:① 只用一个很小的会话做样本(1 份 rollout、从空记忆库起步的 INIT 合并),没有测多份 rollout 的增量合并,也没有评估合并产出的质量(只确认写完了文件、任务状态 `done`);② 有一次运行里阶段 1 请求撞上一次瞬时 `fetch failed`(5 秒,本机到网关),Codex 自动重试后成功,没有查网络层原因;③ 验证脚本曾在第一个 Codex 被强杀后留下一条「合并任务 running、租约 1 小时」的记录,第二个进程 10 分钟内没有接手(清掉这条记录后正常跑完)——正常退出不会留下这条记录,但**用户强杀应用后的下一次启动是否同样 1 小时内合并不了,没有单独验证**。\n\n## apiyi (Gemini) MCP 已从应用移除(2026-10-30)\n\n桌面端不再内置、不再启动 apiyi 的 Gemini MCP(`apiyi-mcp-server`)。**API易作为 Codex 网关 / 出图站点(provider id `apiyi`、`APIYI_PRESET`、设置里的「API易」key)不受影响。**\n\n- **原因**:这条 MCP 早已对 agent 隐藏(`APIYI_MCP_EXPOSED=false`),能力与千问 omni 的 `understand_*` 工具重复;而它的休眠写法 `-c mcp_servers.apiyi.enabled=false` 在配置里没有 `[mcp_servers.apiyi]` 表时会凭空造出一张没有 transport 的表,Codex 直接以 `invalid transport in mcp_servers.apiyi` 起不来(例如全新的 `CODEX_HOME`)。\n- **主进程**:`codexLaunch.ts` 不再发任何 `mcp_servers.apiyi.*` / `apiyi` direct_only 项,`buildCodexLaunchArgs` 去掉 `apiyiKey` 选项;`AgentManager` / `CodexLocalBackend` / `codexProviders` 去掉 apiyi 密钥桥(`APIYI_MCP_PROVIDER_ID`);删除 `apiyiMcpLauncher.ts`、`apiyiMcpSeed.ts` 及其测试;启动时不再往 `~/.codex/config.toml` 写 `[mcp_servers.apiyi]`。`whichNode` / `resolveStdioNodeCommand`(「系统 node,否则 Electron-as-Node」)抽到 `src/main/agent/stdioNodeCommand.ts`,catimation 桥与运镜知识库共用。\n- **老用户配置自动清理**:`src/main/agent/legacyApiyiMcpCleanup.ts` 在 Codex 启动前,只在 `mcp_servers.apiyi` 的 `args[0]` 指向应用自带路径(以 `/apiyi-mcp/dist/index.js` 结尾)时删掉这一条;用户自己写的同名条目保留、TOML 解析失败不改写、没有可删内容不写盘、幂等。结果写日志 `[apiyi-mcp] legacy entry cleanup: removed|kept-user-entry|malformed`。\n- **渲染层**:去掉 `ApiService.syncApiyiKeyToMcp`(构造器 / 保存 key / MCP 页冷启动三处调用)与 `APIYI_MCP_PROVIDER_ID`;MCP 页「预装」分组机制保留,`APP_BUNDLED_MCP_NAMES` 现为空集;「已连接但无工具」提示去掉 apiyi 专属分支(只剩 Docker / 通用两条)。\n- **打包**:`electron-builder.yml` 去掉 `resources/apiyi-mcp` 两段 `extraResources`,`package.json` 去掉 `vendor:apiyi-mcp*` 脚本与 `build:vite` 里的前置 vendor 步骤,`.gitignore` 去掉对应条目,删除 `scripts/vendor-apiyi-mcp.mjs` / `.lock.json`。评测 harness 与 12 个冒烟 / 诊断脚本里写 `[mcp_servers.apiyi]` 惰性占位的代码一并去掉(运镜知识库那条保留)。\n- **技能**:`catimation-understand` 的「apiyi-mcp(Gemini)只作深度复核的备选」整节与 frontmatter 里的对应句子删除(已 `pnpm skills:gen`)。\n- **没有动的**:`deploy/apiyi-fastmcp`(自己服务器上跑的 BYOK 版,见上面「远程 apiyi 网关」一节)、用户 `~/.cursor/mcp.json` 里 Cursor 自己的 `apiyi` / `apiyi-remote`、已持久化在 provider store 里的 `apiKeys[\"apiyi-mcp\"]`(不再被读取,是 API易 key 的一份重复拷贝)。\n- **测试**:新增 `legacyApiyiMcpCleanup.test.ts`(8 条)、`stdioNodeCommand.test.ts`、`codexLaunch.test.ts` 里「apiyi MCP 已移除」3 条、`McpServerCard.test.tsx` 里 docker / 通用空工具提示 2 条。\n\n## NovelAI 四个模型接入(2026-10-30,PR1)\n\n对齐网关 new-api #174(渠道类型 77,TTAPI 聚合)与 sora-ui 的做法,桌面端新增四个出图渠道:`nai-diffusion-5-full` / `nai-diffusion-5-curated` / `nai-diffusion-4-5-full` / `nai-diffusion-4-5-curated`(V5 与 V4.5,各有 Full / Curated 两套数据集)。\n\n- **只经 Miau 网关**(`miauOnly`,出图时自动钉 Miau 站点)。按**每张图**固定价计费,`count` 1–4 直接对应网关的 `n`,1K / 2K 同价;新增厂商分组「NovelAI」。默认出图渠道不变。\n- **参数(`shared/imageModelParams.ts` 的 `novelAiParamSpecs`,生成页 / 批量页 / MCP 共用一张表)**:`dataset_mode`(anime / furry)、`negative_prompt`、`characters`(角色提示词,每个角色 prompt + uc + 位置 x / y;V5 ≤ 22、V4.5 ≤ 6)、`steps`、`scale`、`sampler`、`noise_schedule`、`seed`、`img2img_strength` / `img2img_noise`(仅带 1 张参考图时生效)。未设置 = 自动 / 默认;**越界一律拒绝而不是截断**(按张计费)。透明底(`transparentBackground`)只有 V5;参考图至多 1 张(图生图)。氛围迁移 / 角色精准参考上游暂不可用,不暴露。\n- **生成页**:`ModelParamsPanel` 支持多行文本框(负面提示词)、角色提示词编辑器、「默认 N」占位符;值等于默认时清掉键,不把默认值当成「用户改过」存下来。NovelAI 不自动重试(`maxRetries: 0`),失败后由用户决定是否重来。\n- **MCP**:`generate_image` / `generate_images` 的 `modelParams` 现在是「悠船 ∪ NovelAI」的合并 schema(`modelParamsSchema.ts` 的 `buildChannelModelParamsSchema`,同名键 `seed` 定义一致),说明里写明各自只对自己的渠道生效、其它渠道忽略。为了压回 Codex 的 16384 字节工具 schema 预算(`toolSchemaBudget.test.ts`),同时把说明里重复的内容去掉:`generate_image` 接入后一度到 17845 字节,压到 16384 以内(余量不大,以后再加渠道要先看这条测试)。\n- **技能**:`catimation-image` 补 NovelAI 四条渠道与用法(点名 NovelAI / 动漫插画 / 多角色同框时选用),`commands/gen-image.md` 补渠道清单;skill 正文有 21500 字符的债务钉(只许缩不许涨),本次从 21490 压到 21447(顺手去掉了几处重复的「常见错误」条目和日期轶事)。`firstPartySkills.generated.ts` 已重新生成。\n- **没做 / 没验证**:① **没有对真实网关出过图**(要用户确认过的测试令牌,且每张约 ¥0.6);② 64 张风格封面选择器是二期;③ **插件市场(catimation-core 的 catimation-image)还没发布**,发布要用户点头;④ Codex 页「出图渠道」选择器的两栏改版见下一节(PR2)。\n- **测试**:新增 `imageModelParams.novelai` / `ApiService.novelai` / `imageTools.novelai` / `ModelParamsPanel.novelai` 四个测试文件;相关目录 217 个文件 2798 条全过,`typecheck:ci` 7 existing / 0 new,`audit:skill-arch` 0 违规,`test:skill-arch` 21/21,`skills:gen:check` parity。\n\n## Codex 页「出图渠道」改成两栏分区面板(2026-10-30,PR2)\n\n复刻 sora-ui 网页版「图像模型」下拉(ModelPanel)的结构与交互:左边分区栏 **最近使用 / 常用 / 备用渠道 / 更多模型·按系列**,右边是该分区的渠道(名称 + 一句话说明 + 价格量级)。只改 Codex 输入框下面的 `ImageChannelPicker`,`selectedImageChannel`、`IMAGE_CHANNELS`、MCP 枚举、默认渠道回落一律不动(默认仍是 `custom-imagemodel-gt` 腾讯 image2)。\n\n- **摆放表是桌面端自己的**(`features/agent-chat/imageChannelPlacement.ts`):常用 = Seedream 5.0 Pro / 腾讯 image2 / 腾讯 2.5 Flare / 腾讯 2.5 Sunburst / Nano Banana 2.1 / 万相 2.7 pro(照用户点名的顺序);备用渠道 = 官转的 2.5 Flare / Sunburst(跟随各自的腾讯同款排序);更多按系列:GPT Image 系列 / Gemini 系列 / 阿里百炼 / 风格化(悠船 ×2 + NovelAI ×4)。表里没有的渠道进「更多 · 其他」,不会自己挤进常用;测试里有守卫:注册表每个渠道都必须显式摆放、表里不许有过期条目、备用渠道必须指向存在的常用渠道。\n- **交互**(`ImageChannelPanel.tsx`,逻辑对照网页版):打开时停在当前渠道所在分区并用圆点标出;分区可点击 / 悬停(约 90 ms 延迟,斜着划向右栏不误切)/ 键盘聚焦切换;键盘:分区栏 ↑↓ / Home / End,→ 进右栏;右栏 ↑↓ 移动、←→ 切分区、Enter 选择;Esc 收起并把焦点还给触发按钮;右栏 roving tabindex。面板宽度 < 440 px 时分区栏变成顶部横向标签。「选择」与「固定」是同一行里的两个兄弟按钮,所以右栏不用 listbox。触发按钮在选了备用 / 更多渠道时带小标签(官转 / 更多),避免误触后不自知。\n- **固定与最近使用**(`imageChannelPrefs.ts`,localStorage `catimation.agent.imageChannelPrefs.v1`):备用 / 更多里的渠道可「固定到常用」(常用总数 ≤ 10,满了按钮置灰并说明),已固定的常驻取消按钮;最近使用最多显示 3 个、不含常用里的。读出来的内容一律当不可信输入(逐项校验、去重、截断),坏 JSON 当没有;渠道下架后存着的 id 自动忽略。\n- **价格量级**:只给确认过口径的渠道填 —— 目前只有 NovelAI 四个「≈¥0.6/张」(网关按张计费 ¥0.6–0.65);其他渠道**不抄网页版的近 30 天均价**(口径不同,且与注册表里「与 image2 fast 同价」之类的描述对不上)。分区底部的提示只在该分区确有价格时出现。\n- **配色**:走桌面端 Codex 页的 zinc + fuchsia(CSS 变量 `--icp-*`),网页版的蓝色锚点换成 fuchsia;类名 `mrp-*` → `icp-*`。\n- **没做**:NEW 标签(桌面端没有「模型上新」通知)、「暂未开放」置灰(桌面端渠道清单是写死的精选子集,没有这个状态)、按账号分键(桌面端单机单用户,与 `selectedImageChannel` 一致)。\n- **测试**:新增 `ImageChannelPanel` / `imageChannelPlacement` / `imageChannelPrefs` 三个测试文件并重写 `ImageChannelPicker` 测试(共 53 条);变异检查 6 种(悬停延迟归零 / 选择不记最近 / 固定按钮处处可见 / 钉了不从备用移除 / 最近使用含常用 / 窄容器阈值)全被抓到;`features/agent-chat` + `shared` 138 文件 1536 条全过,`typecheck:ci` 7 existing / 0 new。**视觉**:把真实组件渲染出的 HTML + 项目自己的 Tailwind 编出的 CSS 放进内置浏览器看过三种状态(默认 / 选了 NovelAI / 窄容器),**没有在真实的 Electron 窗口里看过**,悬停时「固定」按钮的出现、真实输入框旁的弹出位置要真机再看一眼。\n\n## Codex 页输入框工具栏窄屏收起 + 弹层夹在输入框内(2026-10-30)\n\n真机反馈两件事:① 出图渠道 / 模型弹层右侧被面板边缘挡住;② 聊天面板缩窄后,工具栏的药丸被挤成多行、按钮看不见。要求:**药丸形状不变、可以省略文字、不丢任何按钮**,参考 Codex 官方的做法。\n\n- **做法:分级收起,不换形状**(`features/agent-chat/toolbarCompact.ts` + `MentionInput.tsx` 工具栏行)。名称先用省略号收短(纯 CSS:药丸 `min-w` + `shrink`,触发按钮 `w-full min-w-0`,文字 `truncate`,图标 / 徽标 / 箭头 `shrink-0`);行宽 ≤ 600 px(c1)收起出图渠道徽标、模型推理等级徽标和待机的「语音」字样;≤ 520 px(c2)协作模式只留图标 + 箭头(Default / Plan 文字收起);≤ 450 px(wrap)才允许整颗药丸换行,作为最后兜底。四颗主药丸(模型 / 出图渠道 / 协作模式 / 语音)统一 `h-[27px]`,收起文字不改变高度;协作模式和语音药丸 `shrink-0`,只靠收起文字让位。Agent 标签和右侧 Send 组都不收缩。\n- **为什么不用 CSS 容器查询**:`container-type` 会带来布局包含,让该元素成为层叠上下文和定位祖先,会把 `z-[40001]` 的弹层困在工具栏里。改成 `useToolbarCompact`(ResizeObserver + `useLayoutEffect` 量行宽)在行上设 `data-c1` / `data-c2` / `data-wrap`,子元素用 Tailwind 的 `group-data-[c1]/tb:hidden`(与视口断点 `sm:inline` 冲突的地方用结尾 `!` 的重要形式)。没有布局引擎(jsdom)时三个标志全为 false,行为与改动前一致。\n- **弹层夹在输入框内**:`popoverPlacement.ts`(纯函数 `computePopoverPlacement`)+ `usePopoverPlacement` 把弹层的 `left` / `width` 夹在所在 `form` 的范围内;出图渠道弹层最宽 540、模型弹层最宽 360;没有布局时回落到原来的 CSS 默认位置。\n- **验证**:新增 `toolbarCompact` / `popoverPlacement` / `MentionInput.toolbarCompact` 测试并更新 `ImageChannelPicker.placement`;**变异检查 10 种**(语音药丸去固定高度 / 模型名不 truncate / 推理徽标不收起 / 模型弹层不夹 / 渠道徽标不收缩 / 协作文字不收起 / 协作药丸可收缩 / 行默认换行 / c2 阈值改大 / 不再监听行宽)全部被抓到,其中「协作药丸可收缩」第一次漏过,补了一条契约后抓到。`features/agent-chat` + `features/voice` 139 文件 1392 条全过,`typecheck:ci` 7 existing / 0 new,`build:vite` 通过。**真浏览器验证**:把真实 `MentionInput` 渲染出的 DOM + 项目 Tailwind 编出的 CSS 放进内置浏览器,在 340–760 px 逐档量:无横向溢出、四颗药丸高度恒为 27 px、≥ 460 px 单行不丢任何按钮、≤ 450 px 整颗换行(两行药丸)。**没有在真实 Electron 窗口里看过**:harness 用的是系统字体,真实的 Exo 2 / 中文字体宽度略有不同,阈值(600 / 520 / 450)与 27 px 高度需要真机再确认一次。\n\n## 许可证\n\nMIT License\n"}