skill
ai-image-master
AI Image Master - Electron desktop application for AI image generation with multiple models support (Gemini, Seedream, Sora, Flux)
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/2799662352/ai-image-master itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 25, 2026CATIMATION Cyberpunk Master / AI Image Master
An open-source Electron desktop workspace for AI-assisted image and video creation, visual storyboarding, and creative production workflows.
Downloads: Official Windows installers are available from GitHub Releases.
Code signing policy
Free code signing provided by SignPath.io, certificate by SignPath Foundation.
Official Windows releases follow the project's Code signing policy. Data handling is documented in the Privacy Policy.
中文简介
AI 图片生成桌面应用 — Electron + React + LangChain/LangGraph。 支持文生图、参考图、批量生成、Director 多阶段流水线和 StoryboardPro 分镜系统。
核心功能
| 模块 | 描述 |
|---|---|
| 图片生成 | 多模型 (Gemini / Seedream / Sora / Flux Kontext),参考图上传,多分辨率 (1K–4K) |
| Director 模式 | LangGraph 4-pass 流水线: 技能选择→场景分析→角色锚点→设计组装→一致性校验 |
| StoryboardPro | 九宫格分镜、剧场版动画、电影级模板、Sora2 视频提示词生成 |
| 批量生成 | 抽卡模式 + 多提示词模式,智能并发队列 |
| 模型对比 | 双模型同时生成对比,参考图自动同步 |
| 图像理解 | 多模型图像分析,流式输出 |
| 历史记录 | 自动保存 + R2 云端存储,批量下载/删除,虚拟滚动 |
| Skills 系统 | 内置 + 用户自定义 Skill (Markdown),运行时热加载 |
| 国际化 | 简中 / 繁中 / English / Русский |
快速开始
git clone https://github.com/2799662352/ai-image-master.git
cd ai-image-master
pnpm bootstrap # pnpm install + 下载 Codex 二进制 (~239MB)
pnpm dev # 开发模式 (electron-vite)
pnpm build # 构建并打包
pnpm build:win # Windows 安装包
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)。
如果你已经在主 worktree 跑过 codex:fetch,新建 worktree 时可以直接 Copy-Item ../../resources/codex/* ./resources/codex/ -Recurse(或 cp -r),比重新下 239MB 快得多。
纯 Electron 桌面应用,无 Docker 部署。
远程 apiyi 网关 / BYOK(可选)
(桌面端内置的 apiyi MCP 已于 2026-10-30 移除,本节说的是自己服务器上独立部署的版本,不受影响。)
如果想跨设备/多人共用 apiyi-mcp-server 的能力 —— 在自己的服务器上挂一个 HTTPS endpoint,
每个用户填自己的 apiyi key(BYOK,Bring Your Own Key):
[ Client ] ─ Authorization: Bearer sk-xxx ─→ https://api.example.com/mcp
│
▼ (HTTPS / CDN / DDoS / WAF)
[ 腾讯 EdgeOne (或其他 CDN) ]
│ 回源 HTTP:80
▼
[ apiyi-fastmcp (Python) ]
│ per call: read Bearer,
│ build google-genai client
▼
api.apiyi.com
实现细节:deploy/apiyi-fastmcp/server.py 用 FastMCP 重写了 apiyi-mcp-server(原 Node 版)
的两个工具 generate_content + generate_content_batch,直接调用 google-genai Python SDK
打到 apiyi 网关。API key 来自每次请求的 Authorization 头,服务端不持有任何 key、不缓存任何 key,
是目前主流 MCP 网关(Docker MCP Gateway / Supergateway / mcp-proxy)都不原生支持的 BYOK 模式。
源站只跑 FastMCP 监听 80 端口,HTTPS 由前面的 CDN / 反代统一终止(我们用的是腾讯 EdgeOne; 没有 CDN 就翻 git 历史拉回 Caddy 配置自己签 Let's Encrypt 证书,二选一)。
完整方案见 deploy/README.md —— 包含 EdgeOne 配置、运维、安全模型、
客户端 JSON 配置示例(支持 Cursor / Codex / Claude Desktop)。
镜像已发布到 Docker Hub:zuozuoliang999/apiyi-fastmcp
(linux/amd64)。在自己的服务器上一行 docker run 即可拉起:
docker run -d --name apiyi-fastmcp --restart unless-stopped \
-p 80:8000 \
-e APIYI_BASE_URL=https://api.apiyi.com \
-e GEMINI_MODEL=gemini-3.1-pro-preview-thinking \
-e FASTMCP_HOST=0.0.0.0 -e FASTMCP_PORT=8000 \
-e FASTMCP_PATH=/mcp -e FASTMCP_TRANSPORT=http \
zuozuoliang999/apiyi-fastmcp:latest
项目结构
ai-image-master/
├── src/
│ ├── main/ # Electron 主进程
│ │ └── index.ts
│ ├── preload/ # IPC 桥接 (contextBridge)
│ │ └── index.ts
│ ├── types/ # 全局类型定义
│ └── renderer/ # 渲染进程
│ └── src/
│ ├── pages/ # 页面模块
│ │ ├── GeneratePage.ts # 图片生成
│ │ ├── BatchPage.ts # 批量生成
│ │ ├── ComparePage.ts # 模型对比
│ │ ├── HistoryPage.ts # 历史记录
│ │ ├── UnderstandPage.ts # 图像理解
│ │ └── PromptTemplates.ts # 提示词模板
│ ├── services/ # 服务层
│ │ ├── pipeline/ # Director 流水线
│ │ │ ├── DirectorPipeline.ts
│ │ │ ├── BasePipeline.ts
│ │ │ └── director-skills.ts
│ │ ├── storyboard-pipeline/ # StoryboardPro
│ │ ├── api/ # API 调用
│ │ ├── r2-storage/ # R2 云存储
│ │ ├── cache/ # 图片缓存
│ │ ├── storage/ # 本地存储
│ │ ├── i18n/ # 国际化
│ │ ├── version-checker/ # 版本检查
│ │ ├── LangChainDirectorService.ts
│ │ ├── LangChainStoryboardService.ts
│ │ └── ServiceBridge.ts # 服务总线
│ ├── features/ # 功能模块 (18+)
│ │ ├── model-selector/ # 模型选择器
│ │ ├── tab-manager/ # 标签管理
│ │ ├── history/ # 历史数据
│ │ ├── settings/ # 设置面板
│ │ ├── image-viewer/ # 图片查看器
│ │ ├── intelligent-resize/ # 智能缩放
│ │ ├── keyboard/ # 快捷键
│ │ ├── language/ # 语言切换
│ │ ├── updater/ # 自动更新
│ │ └── ...
│ ├── core/ # 核心模块
│ │ ├── AppBootstrap.ts # 应用引导
│ │ ├── Router.ts # SPA 路由
│ │ ├── EventBus.ts # 事件总线
│ │ ├── VirtualScroller.ts # 虚拟滚动
│ │ └── RetryManager.ts # 重试管理
│ ├── utils/ # 工具函数
│ └── styles/ # 样式
│
├── electron/ # Electron 入口文件
│ ├── main.js
│ └── preload.js
│
├── skills/ # Skills 定义 (19 个)
│ ├── director-*/ # Director 流水线 Skill (12 个)
│ │ ├── director-anchor-extraction-quality
│ │ ├── director-anime-quality-boost
│ │ ├── director-character-consistency
│ │ ├── director-cinematic-composition
│ │ ├── director-lighting-continuity
│ │ ├── director-narrative-flow
│ │ ├── director-prompt-engineering
│ │ ├── director-scene-analysis-depth
│ │ ├── director-shot-sequence-patterns
│ │ ├── director-structured-captioning
│ │ ├── director-style-consistency
│ │ └── director-visual-continuity
│ └── storyboard-*/ # StoryboardPro Skill (7 个)
│ ├── storyboard-audio
│ ├── storyboard-dialogue
│ ├── storyboard-dodge
│ ├── storyboard-physics
│ ├── storyboard-structure
│ ├── storyboard-style
│ └── storyboard-visual
│
├── .agents/skills/ # Deep Agents 元数据
│ ├── deep-agents-core/
│ ├── deep-agents-memory/
│ └── deep-agents-orchestration/
│
├── docs/ # 项目文档
│ └── plans/ # 功能实现计划 (100+)
├── tests/ # 单元测试 (Vitest)
├── e2e/ # E2E 测试 (Playwright)
├── i18n/ # 翻译资源
├── images/ # 参考素材
└── config/ # 构建配置
Director 流水线
LangGraph 驱动的 4-pass 多阶段图片生成流水线:
Pass 1: 技能选择 + 场景分析
→ 从 19 个 Skill 中选择最匹配的
→ 深度场景理解 (角色/环境/氛围)
Pass 2: 角色锚点提取
→ 角色一致性保证
→ 关键特征锚定
Pass 3: 设计组装
→ 结构化 prompt 生成
→ 风格/构图/光影参数
Pass 4: 一致性校验
→ 跨镜头角色一致
→ 视觉连续性检查
Director Skill 列表
| Skill | 描述 |
|---|---|
anchor-extraction-quality | 角色锚点提取优化 |
anime-quality-boost | 动画画质增强 |
character-consistency | 角色一致性保证 |
cinematic-composition | 电影构图 |
lighting-continuity | 光影连续性 |
narrative-flow | 叙事流畅性 |
prompt-engineering | 提示词工程 |
scene-analysis-depth | 场景深度分析 |
shot-sequence-patterns | 镜头序列模式 |
structured-captioning | 结构化描述 |
style-consistency | 风格一致性 |
visual-continuity | 视觉连续性 |
StoryboardPro 风格模板
| 模板 | 说明 | 特点 |
|---|---|---|
theatrical | 剧场版动画 | 日式权重标记 ((style:1.5)) |
cinematic | 电影级九宫格 | 分形几何,角色一致性 |
anime | 动画截图 | 赛璐璐着色 |
manga | 漫画分镜 | 黑白墨线,网点 |
movie | 电影分镜 | 宽银幕,戏剧光影 |
webtoon | 韩漫/条漫 | 全彩,纵向排版 |
Skills 系统
加载策略
最终 Skill 集 = 内置 skills/ + 用户 userData/skills/
同名冲突: 用户 Skill 覆盖内置
格式异常: 跳过不中断
运行时热加载: 渲染进程手动刷新生效
用户自定义 Skill
打包后用户可在 app.getPath('userData')/skills/<skill-name>/SKILL.md 添加自定义 Skill,刷新后即时生效。
技术栈
| 分类 | 技术 |
|---|---|
| 框架 | Electron 35 |
| 构建 | Vite 6 + electron-vite |
| 语言 | TypeScript 5.7 |
| 样式 | Tailwind CSS 4 |
| AI | LangChain + LangGraph |
| 存储 | Cloudflare R2 |
| 测试 | Vitest + Playwright |
| 国际化 | 4 语言 (zh-CN/zh-TW/en/ru) |
Codex 页 DeepSeek (Miau) 通道:选择器次序与记忆模型
2026-10-30 调整(src/shared/modelSettings.ts、src/main/agent/gatewayModelRouting.ts):
- 选择器次序:DeepSeek 组按
火山 V4.1 Flash→火山 V4.1 Flash 经济版→DeepSeek V4 Pro→DeepSeek Flash排列(火山在前,官方在后,v4-pro先于flash)。次序以CANONICAL_MODEL_SETTINGS_ROWS为准;通道默认模型仍是deepseek-flash。 - 记忆模型(
memories.extract_model/consolidation_model)与会话自动起标题的模型:四个 DeepSeek 模型(火山两个 + 官方两个)一律走火山标准版deepseek-v4-1-flash-261017(原为deepseek-flash),在通道的memoriesModel里写一个固定值。- 为什么是一个值而不是按对话模型分路:记忆模型是启动 Codex 时写死的
-c参数,而同一通道内切换模型不会重启 Codex,分路会让它停在启动那一刻的值上(切换后过期)。一个值就没有这个问题,也不需要映射表。 - 通道默认对话模型不变,仍是
deepseek-flash。
- 为什么是一个值而不是按对话模型分路:记忆模型是启动 Codex 时写死的
- 代价:官方两个模型的用户也按火山标准版计费;同一时段它的单价是
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,以本条为准。这些费用会出现在用量明细里;合并只在有新的提取结果时才跑,频率取决于用户开新会话的节奏。 - 已验证(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)与记忆请求(火山标准版)走的是两个不同的模型,互不影响。 - 没有覆盖到的:① 只用一个很小的会话做样本(1 份 rollout、从空记忆库起步的 INIT 合并),没有测多份 rollout 的增量合并,也没有评估合并产出的质量(只确认写完了文件、任务状态
done);② 有一次运行里阶段 1 请求撞上一次瞬时fetch failed(5 秒,本机到网关),Codex 自动重试后成功,没有查网络层原因;③ 验证脚本曾在第一个 Codex 被强杀后留下一条「合并任务 running、租约 1 小时」的记录,第二个进程 10 分钟内没有接手(清掉这条记录后正常跑完)——正常退出不会留下这条记录,但用户强杀应用后的下一次启动是否同样 1 小时内合并不了,没有单独验证。
apiyi (Gemini) MCP 已从应用移除(2026-10-30)
桌面端不再内置、不再启动 apiyi 的 Gemini MCP(apiyi-mcp-server)。API易作为 Codex 网关 / 出图站点(provider id apiyi、APIYI_PRESET、设置里的「API易」key)不受影响。
- 原因:这条 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)。 - 主进程:
codexLaunch.ts不再发任何mcp_servers.apiyi.*/apiyidirect_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 桥与运镜知识库共用。 - 老用户配置自动清理:
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。 - 渲染层:去掉
ApiService.syncApiyiKeyToMcp(构造器 / 保存 key / MCP 页冷启动三处调用)与APIYI_MCP_PROVIDER_ID;MCP 页「预装」分组机制保留,APP_BUNDLED_MCP_NAMES现为空集;「已连接但无工具」提示去掉 apiyi 专属分支(只剩 Docker / 通用两条)。 - 打包:
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]惰性占位的代码一并去掉(运镜知识库那条保留)。 - 技能:
catimation-understand的「apiyi-mcp(Gemini)只作深度复核的备选」整节与 frontmatter 里的对应句子删除(已pnpm skills:gen)。 - 没有动的:
deploy/apiyi-fastmcp(自己服务器上跑的 BYOK 版,见上面「远程 apiyi 网关」一节)、用户~/.cursor/mcp.json里 Cursor 自己的apiyi/apiyi-remote、已持久化在 provider store 里的apiKeys["apiyi-mcp"](不再被读取,是 API易 key 的一份重复拷贝)。 - 测试:新增
legacyApiyiMcpCleanup.test.ts(8 条)、stdioNodeCommand.test.ts、codexLaunch.test.ts里「apiyi MCP 已移除」3 条、McpServerCard.test.tsx里 docker / 通用空工具提示 2 条。
NovelAI 四个模型接入(2026-10-30,PR1)
对齐网关 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 两套数据集)。
- 只经 Miau 网关(
miauOnly,出图时自动钉 Miau 站点)。按每张图固定价计费,count1–4 直接对应网关的n,1K / 2K 同价;新增厂商分组「NovelAI」。默认出图渠道不变。 - 参数(
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 张(图生图)。氛围迁移 / 角色精准参考上游暂不可用,不暴露。 - 生成页:
ModelParamsPanel支持多行文本框(负面提示词)、角色提示词编辑器、「默认 N」占位符;值等于默认时清掉键,不把默认值当成「用户改过」存下来。NovelAI 不自动重试(maxRetries: 0),失败后由用户决定是否重来。 - MCP:
generate_image/generate_images的modelParams现在是「悠船 ∪ NovelAI」的合并 schema(modelParamsSchema.ts的buildChannelModelParamsSchema,同名键seed定义一致),说明里写明各自只对自己的渠道生效、其它渠道忽略。为了压回 Codex 的 16384 字节工具 schema 预算(toolSchemaBudget.test.ts),同时把说明里重复的内容去掉:generate_image接入后一度到 17845 字节,压到 16384 以内(余量不大,以后再加渠道要先看这条测试)。 - 技能:
catimation-image补 NovelAI 四条渠道与用法(点名 NovelAI / 动漫插画 / 多角色同框时选用),commands/gen-image.md补渠道清单;skill 正文有 21500 字符的债务钉(只许缩不许涨),本次从 21490 压到 21447(顺手去掉了几处重复的「常见错误」条目和日期轶事)。firstPartySkills.generated.ts已重新生成。 - 没做 / 没验证:① 没有对真实网关出过图(要用户确认过的测试令牌,且每张约 ¥0.6);② 64 张风格封面选择器是二期;③ 插件市场(catimation-core 的 catimation-image)还没发布,发布要用户点头;④ Codex 页「出图渠道」选择器的两栏改版见下一节(PR2)。
- 测试:新增
imageModelParams.novelai/ApiService.novelai/imageTools.novelai/ModelParamsPanel.novelai四个测试文件;相关目录 217 个文件 2798 条全过,typecheck:ci7 existing / 0 new,audit:skill-arch0 违规,test:skill-arch21/21,skills:gen:checkparity。
Codex 页「出图渠道」改成两栏分区面板(2026-10-30,PR2)
复刻 sora-ui 网页版「图像模型」下拉(ModelPanel)的结构与交互:左边分区栏 最近使用 / 常用 / 备用渠道 / 更多模型·按系列,右边是该分区的渠道(名称 + 一句话说明 + 价格量级)。只改 Codex 输入框下面的 ImageChannelPicker,selectedImageChannel、IMAGE_CHANNELS、MCP 枚举、默认渠道回落一律不动(默认仍是 custom-imagemodel-gt 腾讯 image2)。
- 摆放表是桌面端自己的(
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)。表里没有的渠道进「更多 · 其他」,不会自己挤进常用;测试里有守卫:注册表每个渠道都必须显式摆放、表里不许有过期条目、备用渠道必须指向存在的常用渠道。 - 交互(
ImageChannelPanel.tsx,逻辑对照网页版):打开时停在当前渠道所在分区并用圆点标出;分区可点击 / 悬停(约 90 ms 延迟,斜着划向右栏不误切)/ 键盘聚焦切换;键盘:分区栏 ↑↓ / Home / End,→ 进右栏;右栏 ↑↓ 移动、←→ 切分区、Enter 选择;Esc 收起并把焦点还给触发按钮;右栏 roving tabindex。面板宽度 < 440 px 时分区栏变成顶部横向标签。「选择」与「固定」是同一行里的两个兄弟按钮,所以右栏不用 listbox。触发按钮在选了备用 / 更多渠道时带小标签(官转 / 更多),避免误触后不自知。 - 固定与最近使用(
imageChannelPrefs.ts,localStoragecatimation.agent.imageChannelPrefs.v1):备用 / 更多里的渠道可「固定到常用」(常用总数 ≤ 10,满了按钮置灰并说明),已固定的常驻取消按钮;最近使用最多显示 3 个、不含常用里的。读出来的内容一律当不可信输入(逐项校验、去重、截断),坏 JSON 当没有;渠道下架后存着的 id 自动忽略。 - 价格量级:只给确认过口径的渠道填 —— 目前只有 NovelAI 四个「≈¥0.6/张」(网关按张计费 ¥0.6–0.65);其他渠道不抄网页版的近 30 天均价(口径不同,且与注册表里「与 image2 fast 同价」之类的描述对不上)。分区底部的提示只在该分区确有价格时出现。
- 配色:走桌面端 Codex 页的 zinc + fuchsia(CSS 变量
--icp-*),网页版的蓝色锚点换成 fuchsia;类名mrp-*→icp-*。 - 没做:NEW 标签(桌面端没有「模型上新」通知)、「暂未开放」置灰(桌面端渠道清单是写死的精选子集,没有这个状态)、按账号分键(桌面端单机单用户,与
selectedImageChannel一致)。 - 测试:新增
ImageChannelPanel/imageChannelPlacement/imageChannelPrefs三个测试文件并重写ImageChannelPicker测试(共 53 条);变异检查 6 种(悬停延迟归零 / 选择不记最近 / 固定按钮处处可见 / 钉了不从备用移除 / 最近使用含常用 / 窄容器阈值)全被抓到;features/agent-chat+shared138 文件 1536 条全过,typecheck:ci7 existing / 0 new。视觉:把真实组件渲染出的 HTML + 项目自己的 Tailwind 编出的 CSS 放进内置浏览器看过三种状态(默认 / 选了 NovelAI / 窄容器),没有在真实的 Electron 窗口里看过,悬停时「固定」按钮的出现、真实输入框旁的弹出位置要真机再看一眼。
Codex 页输入框工具栏窄屏收起 + 弹层夹在输入框内(2026-10-30)
真机反馈两件事:① 出图渠道 / 模型弹层右侧被面板边缘挡住;② 聊天面板缩窄后,工具栏的药丸被挤成多行、按钮看不见。要求:药丸形状不变、可以省略文字、不丢任何按钮,参考 Codex 官方的做法。
- 做法:分级收起,不换形状(
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 组都不收缩。 - 为什么不用 CSS 容器查询:
container-type会带来布局包含,让该元素成为层叠上下文和定位祖先,会把z-[40001]的弹层困在工具栏里。改成useToolbarCompact(ResizeObserver +useLayoutEffect量行宽)在行上设data-c1/data-c2/data-wrap,子元素用 Tailwind 的group-data-[c1]/tb:hidden(与视口断点sm:inline冲突的地方用结尾!的重要形式)。没有布局引擎(jsdom)时三个标志全为 false,行为与改动前一致。 - 弹层夹在输入框内:
popoverPlacement.ts(纯函数computePopoverPlacement)+usePopoverPlacement把弹层的left/width夹在所在form的范围内;出图渠道弹层最宽 540、模型弹层最宽 360;没有布局时回落到原来的 CSS 默认位置。 - 验证:新增
toolbarCompact/popoverPlacement/MentionInput.toolbarCompact测试并更新ImageChannelPicker.placement;变异检查 10 种(语音药丸去固定高度 / 模型名不 truncate / 推理徽标不收起 / 模型弹层不夹 / 渠道徽标不收缩 / 协作文字不收起 / 协作药丸可收缩 / 行默认换行 / c2 阈值改大 / 不再监听行宽)全部被抓到,其中「协作药丸可收缩」第一次漏过,补了一条契约后抓到。features/agent-chat+features/voice139 文件 1392 条全过,typecheck:ci7 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 高度需要真机再确认一次。
许可证
MIT License