Repository navigation
feat(ai-node-canvas): 画布 1.22.9:音轨一律走 mp3(厂商只收 wav 和 mp3);剪辑走时间轴、分离音频、AI 抠图与超分;没出产物不再报完成,上游不重跑,提示词横着读,转圈有上限;浮层消得掉,在飞的运行运行时自己问厂商 - #126
Merged
MyPrototypeWhat merged 9 commits intoNov 12, 2026
Conversation
这一版把「拿到素材之后能做的事」补齐了,也把两个只在亮色下才看得见的毛病修掉了。 卡片上的工具:视频卡挂「分离音频」和「剪辑」,音频卡挂「剪辑」,图片卡挂工具坞。 剪辑不再是两个数字框,而是素材本身——视频卡里是一排从文件抽出来的帧,音频卡里是 真实的波形,两端可以拖;下面一条走带带播放键和时间读数,播放头跟着声音走,也能 用手抓住拖(按住时暂停,松手继续)。图片多了两个走厂商的工具:「AI 抠图」把主体 从不是纯色的背景里抠出来,写出透明 PNG;「AI 放大」用 2× 超分把糊图变清晰。两 个工具各自写明厂商收什么样的图,超限的图在请求发出去之前就被拦下,不会白花钱。 修掉的两处:切主题之后波形与剪辑条不重画——canvas 里的像素是旧主题画的,暗色 画的浅色波形切到亮色几乎看不见,还被黑色遮罩压成一块黑。现在切主题和系统主题 变化都会重画;波形与剪辑条上那层遮罩原本是写死的黑色,亮色下等于把波形涂黑, 改成页面底色之后两种主题都只是「变淡」。 另外把音频卡上方那行浮标题删干净了;README 里本机媒体工具的数量与清单改成跟运 行时一致,并加了一条测试把两者绑在一起,以后加工具忘了改文档就会红。
- 新增 media-outcome.mjs:「媒体运行没出东西就不算成功」这个判断只放一处, 直连连接器与 Agent 回合两条路都问它,答案一样 - connector-runner:取下载链接的失败不再被静默吞掉,链接按 provider 自己的规矩 再问一次;文件已经做出来但取不到链接时,把 node_id 报出来,不把产物丢掉 - provider-upload:上传素材的 POST 加 2 分钟时限,对方不回就带着原因结束这次 运行,不再无限等待 - 版本 1.22.3
- 「整条链自己跑」只补**还欠产物**的那几步。产物落在出它的那张卡上(文本节点的产物 就是它自己那段文字),旧代码只认独立的 result 卡,于是每个生成节点都被当成「还没 跑」,跑下游必重跑上游:多花一次钱,还把你写好的提示词换成重新扩写出来的另一版。 这条判断收在一处 —— tools.mjs 的 nodeHasProduct,页面与运行时读同一个。 - 厂商拒绝一次请求时,接口回的 code 仍然是 0,原因写在 failed_items 里。只查 code 不为 0 的话,厂商的原话会被丢掉,用户只看到一句「没有取回来」。现在 err_code 和原因 原样写进运行日志与卡片(providerFailure 是纯函数,图片与图片工具共用一条)。 - 重跑时不再清空卡上的旧产物:这一次没跑出来,上一次那张图还在,而不是变成一张空卡。 单测 324 条全绿(新增 8 条),6 条变异自检全部被精确抓住,扫描器干净。
面板变宽的规则只认自己手写的编辑器(.nxc-composer),提示词从上游接过来时 渲染的是只读面板(.nxc-inherited),没命中这条规则,于是落回 380px 的窄列, 一段长提示词堆成一根竖柱。 现在「这块面板是给提示词用的」这个判断收在 node-ui.mjs 的 opsSurfaceClass() 一处:两种提示词都来自同一个 part,拿的是同一个答案;样式表按这个类给宽版 页面,继承过来的那段文字限高 150px,长了就在盒子里滚。 实测(本机无头 Chrome,真 app + 真 CSS):面板从 318×612 变成 620×279; 自带编辑器的面板不变(620×377)。单测 325 全绿,4 条变异注入全部变红。
Collaborator
|
感谢更新~还有一处需要对齐:README.md 的 Data & access / Connector access 和 README.zh-CN.md 的「数据与访问」/「连接器授权」仍写 matrix 只用于媒体生成,请补上 AI 抠图(remove_image_background)和 AI 放大(enhance_image):这两个工具会把用户的图片上传给厂商处理,可能产生费用,并且只在用户点击「应用」后才发起,不会在后台外发。补完就可以合。 |
宿主从不给 Agent 回合设超时:它可能在长时间思考、等工具、或等用户。插件这边 也没有上限,于是一个不结束的回合 = 卡片永远转圈:用户既看不出它在干什么,也 没法判断它是不是坏了。 现在运行自己给回合设一个上限(十分钟):到点就放弃这个回合、让宿主停掉它, 并把事情说清楚(模型可能在长时间思考,或卡在某个工具上)。卡上转圈的时候也会 说出它已经转了多久;运行面板里照旧看得见每一步。 实测(本机):两个文本节点的回合都停在「看完图、等最终回复」这一步——宿主回合 始终 status=started,最后一次模型响应距今 3~6 分钟,模型是 M3.1-Flash + thinking effort=max。 单测 326 全绿,5 条变异注入全部变红。
厂商把音频参考当一个能解码的文件读,而它只收 wav 和 mp3:给它一个 m4a, 它会报错点名那两种格式。我们自产的音轨全是 m4a(AAC),所以参考生视频 带着音轨时提交被拒。 改法分两层,判断只写在一处(miniapp/node/provider-audio.mjs): - 产出端:音画分离、分离音频、音频剪辑一律产出 mp3(libmp3lame)。 - 交接端:别处来的音频(用户带进来的文件、这条规矩之前做出的音轨)在 上传前转成 mp3,转出来的那份用完就删;画布指着的文件原样不动。 转不了就明说,不原样上传——原样上传等于把厂商那句英文拒绝原样丢回给用户。 单测 329/329。变异自检 5 条全部变红(改坏必红): 工具退回 m4a、上传前不转换、厂商收全部音频、换编码器、临时文件不清理。
变异自检发现:把「音画分离」改回产出 m4a,测试是绿的——因为原来只验了 它声明的 ext/mimeType,没验它真的写出什么。 现在三个工具都验运行时:产出的路径以 .mp3 结尾、mime 是 audio/mpeg、 ffmpeg 命令里是 libmp3lame 而不是 aac。
三个毛病一起修: 1. 在飞的运行被摘掉之后,厂商的活没人问。原来「问厂商」只有页面在做, 页面不问了(切走标签页、或画布在别处被改过之后重新载入并清空了那 张表),就没人问了——钱花了,片子拿不回来。现在运行时自己有一个钟, 每秒看一眼还有哪些运行在等厂商;真正问厂商的频率仍由原有的 10 秒 节流管着,不会多问。页面在画布重新载入之后也把在飞的运行接回来; 跑完而还没有任何页面读过的运行仍然留在列表里,页面丢掉的那次运行 找得回来。 2. 右键空白处弹出来的菜单消不掉。「按在别处,浮层都下去」这个判断 收成一处(dismissOverlays),文档级与节点级都走它。 3. 节点参数浮层(比例/清晰度/时长)消不掉。chip 同一次手势只读一遍 (不再同时挂 pointerdown 和 click),再点一次同一个 chip 就关掉它 自己开的那块面板。 单测 334/334,变异自检 5 条坏改法全红,真机无头 Chrome 点过 13 项全过 (全程没有发生成请求,没有花额度)。
按审核意见:README.md 的 Data & access / Connector access 与 README.zh-CN.md 的「数据与访问 / 连接器授权」两段原先只说 matrix 连接器用来生成媒体,漏了这两 个由厂商自己做的图片工具。现在写明:它们会把卡上那张图上传给厂商处理、可能产生 费用、只在用户点了工具面板里的「应用」之后才发起,不在后台外发,也没有别的 路径会把图发出去;「网络主机 / Network hosts」那段也把它们列进了走宿主连接器 的请求里。 包内容变了,版本 1.22.8 → 1.22.9。
Contributor
Author
|
两处 README 都补上了:README.md 的 Data & access / Connector access 与 README.zh-CN.md 的「数据与访问 / 连接器授权」现在写明 AI 抠图与 AI 放大是厂商自己的图片工具,会把卡上那张图上传给厂商处理、可能产生费用、只在用户点了「应用」之后才发起,不在后台外发,「网络主机」那段也把它们列进了走宿主连接器的请求;包内容变了,版本随之升到 1.22.9。 |
MyPrototypeWhat
approved these changes
Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
这一版做了什么
00:00 / 02:57),播放头跟着声音走,也能用手抓住拖——按住时暂停,松手继续;方向键一次挪 0.1 秒,按住 Shift 一次一秒,Home / End 到两端。面板挂在卡片下方并与卡片居中对齐,下面没地方时翻到上面。matrix连接器,各自写明厂商收什么样的图(AI 放大要长边 ≤2048、短边 ≥256、≤10MB),超限的图在请求发出去之前就被拒下,不会白花钱。MiniMax-H3-Image,默认 4K、画幅自适应。这一版修掉的三个「消不掉」和「没人问厂商」的毛病
用户报的是三件事:
1. 运行时自己问厂商,页面只读答案
原来「问厂商」只有页面在做:页面一轮轮问运行状态,页面不问了,就没人问了。而页面有两种情况会不再问——切走标签页;或者画布在别处被改过之后重新载入(重新载入会把页面手上那张「在飞的运行」表清空)。厂商照旧在生成,只是没人再去取结果。
现在运行时自己有一个钟:
miniapp/node/agent-runner.mjs里每 1 秒看一眼还有哪些运行在等厂商。真正问厂商的频率仍由原有的 10 秒节流管着(不会多问、不会多花钱),但页面在不在都问。页面这边也补上:画布被别处被改过、重新载入之后,同样把在飞的运行接回来(原来只有开画布时接一次)。还有一条容易漏的:已经跑完、但还没有任何页面读过的运行,仍然留在列表里(
listForPage()+reported标记)——页面丢掉的那次运行得找得回来,否则结果还是落不了地。2、3. 浮层消得掉:收成一处「浮层关闭点」
这两条根因是同一个:页面上没有一处统一的关闭点。右键菜单只认 Esc(文档级的 pointerdown 关闭器只管下拉框、@ 菜单和浮层,漏了右键菜单和添加菜单);而节点上的 pointerdown 会
stopPropagation(),所以点在卡片上的那次关闭根本收不到;参数 chip 又同时绑了 pointerdown 和 click(一次手势被读了两遍),于是同一个 chip 再点一次只会再开一遍,永远关不掉。现在收成一处:
overlaysDismissedBy()),文档级与节点级都走dismissOverlays();bindPanelControl():读 pointerdown,键盘另走 keydown,不再同时挂 click),并且再点一次同一个 chip 就关掉它自己开的那块面板(纯函数chipPress());证据
query_video_generation的调用,运行自己跑到了done,产物在手;另一条验「跑完但没人读过的运行仍然在列表里,页面读过一次之后就不再占位」。kind=params)→ 点卡片 → 面板下去 → 再点 chip → 又出来 → 再点同一个 chip → 关掉;点空白画布 → 面板也下去。13 项全过,全程没有发生成请求(没花额度)。这一版修掉的一个「音轨带不上去」的毛病
用户报的现象是:参考生视频(H3,参考集里带一段音轨)提交被厂商拒。厂商原话是
reference inputs reference, audios[0] must use wav or mp3 media; upload local media first, then use the documented url/data media source and supported formats。查下来是我们自产的音轨格式不对:厂商把音频参考当一个能解码的文件读,而它只收 wav 和 mp3;
而音画分离 / 分离音频 / 音频剪辑这三个工具写出来的一律是
.m4a(AAC)。审计日志里那几次upload_temp_url全部 completed(上传是成功的),被拒的是提交时的内容校验。改法分两层,但判断只写在一处(新文件
miniapp/node/provider-audio.mjs):.mp3(libmp3lame,192k),mime 写audio/mpeg。.m4a/.aac/.flac、这条规矩之前做出的音轨)在上传前转成 mp3,转出来的那份用完就删;画布指着的那个文件原样不动。
转不了的时候明说(
PROVIDER_AUDIO_UNCONVERTIBLE),不原样上传——原样上传等于把厂商那句英文拒绝原样丢回给用户。
这一版修掉的三个「不出东西却什么都不说」的毛病
用户报的现象是:出图跑完,运行面板写「完成,用时 45.6s」,节点上却没有产物。查宿主审计日志,那一次调用的顺序是
upload_temp_url(0.5s)→generate_image(14.7s)→get_asset_url:最后一次卡了 30.005 秒,被宿主中止(CONNECTOR_CALL_FAILED)。厂商其实已经把图做出来了,只是取链接那一步没回音。miniapp/node/media-outcome.mjs:「媒体运行没出东西就不算成功」这个判断只放一处,直连连接器与 Agent 回合两条路都问它,答案一样。厂商回「ok」却什么也没留下时,运行带着原因失败,不再谎报完成;Agent 回合没给图也走同一条路(原来这条只在降级分支里判过,是双份防线,现在并成一处)。catch { return null }吞掉,条目直接丢弃,media 变成空数组。现在:链接按厂商自己的规矩再问一次(get_asset_url对同一个 node 可以重复调);仍然拿不到时,运行失败并把 node_id 报出来——图还在厂商那边找得回来。AbortSignal.timeout)。这一版修掉的三个「链自己跑」和「报错不吭声」的毛病
用户报的现象是:把提示词节点的产物接进图片节点后点「生成」,插件又自己把提示词节点重跑了一遍;接着选三个「多角度」,它从文本生成节点开始重跑,而且开始报错。
两件事都查清了,根因是同一条规则写错了:
tools.mjs的nodeHasProduct),页面与运行时读同一个;整条链要补哪些上游也提成纯函数(actions.mjs的pendingAncestors)。code仍然是 0(code 只说明这次调用走通了),原因写在failed_items[].error_msg里。原代码只查code !== 0,于是厂商的 400 被丢掉,用户只看到一句「一个图片都没有取回来」。用一次故意构造的失败请求(零消耗)验到的厂商返回形状是:{"code":0,"message":"all 1 failed: …","total_success":0,"success_items":[],"failed_items":[{"err_code":400,"error_msg":"…"}]}。现在 err_code 和原因原样写进运行日志和卡片(providerFailure是纯函数,出图与图片工具共用一条)。actions.mjs的outputForRun)。这一版修掉的一个「提示词竖着堆」的毛病
用户报的现象是:提示词接进下游节点以后,卡下那块操作面变成一根竖着的窄柱,一段长提示词从上往下堆着读。
根因是一条 CSS 规则认错了对象。面板变宽的规则写的是
.nxc-node__ops:has(.nxc-composer)——只认自己手写的编辑器;而提示词从上游接过来时渲染的是只读面板.nxc-inherited,没命中这条规则,于是落回默认的max-width: 380px窄列。现在「这块面板是给提示词用的」这个判断收在
node-ui.mjs的opsSurfaceClass()一处:编辑器与继承面板本来就是同一个 part 的两种样子,所以拿的是同一个答案;样式表按这个类给宽版页面,继承过来的那段文字限高 150px,长了就在盒子里滚。实测(本机无头 Chrome,跑真 app + 真 CSS):修前面板 318×612(竖柱),修后 620×279(横版页面);自带编辑器的面板前后都是 620×377,没有回归。
这一版修掉的一个「转圈停不下来」的毛病
用户报的现象是:一个文本节点转圈转了好几分钟。
查宿主审计日志和会话记录,它没有卡死:那是两个文本节点(走宿主 Agent 回合,不走连接器,所以没有 30 秒上限),回合一直在干活——加载 H3 提示词 skill、读 27KB 的 skill 参考、下载并查看那张 @图,然后写六段式提示词。两个会话的
status一直是started,最后一次模型响应距今 3~6 分钟(模型是 M3.1-Flash + thinking effort=max)。但插件这侧没有上限:
await api.run(...)没有期限,宿主回合不结束,卡就永远转圈、永远不报错,用户也看不出它在干什么。现在运行自己给回合设一个上限(十分钟):到点就放弃这个回合、让宿主停掉它(
controller.abort),并把事情说清楚。卡上转圈的时候也会说出它已经转了多久;运行面板里本来就看得见每一步(▶ skill / ✓ skill)。修掉的两个只在亮色下才看得见的毛病
切主题之后波形与剪辑条不重画。 波形和剪辑条是 canvas 画的,像素是画的那一刻用当时的配色取的,canvas 不会自己重画:暗色下画的浅色波形切到亮色几乎看不见,还被那层黑色遮罩压成一块黑。现在切主题会重画,系统主题变化(
prefers-color-scheme)也会重画——重画这件事放在strip.mjs(画的地方),主题切换只调一个redrawStrips()。遮罩是写死的黑色。 波形上「还没听到的那段」和剪辑条上「剪掉的那段」用的遮罩是
rgb(0 0 0 / 48%)和rgb(0 0 0 / 58%):亮色下波形本身是深色的,压一层黑等于把波形涂黑。改成color-mix(in srgb, var(--mcode-bg) 55%, transparent)之后,两种主题下都只是「变淡」。压在媒体画面上的那几处黑(确认弹窗遮罩、时长角标、播放键、裁剪遮罩)保留黑——它们压的是画面,两种主题都需要黑底。顺带把音频卡上方那行浮标题删干净了。
README 跟运行时对齐
README 说本机媒体工具是 16 个、清单里少一个「音画分离」;运行时实际是 14 个 ffmpeg 工具(少了「无损改尺寸」「证件照」,多了「分离音频」「音频剪辑」)。数字和清单都改正了,并加了一条测试把两者绑在一起——以后加工具忘了改文档就会红。
(1.22.9,按审核意见补) README 的「Data & access / Connector access」与「数据与访问 / 连接器授权」两段原先只说
matrix连接器用来生成媒体,漏了 AI 抠图(remove_image_background)与 AI 放大(enhance_image)这两个由厂商自己做的图片工具。现在两段都写明:这两个工具会把用户卡上的那张图上传给厂商处理、可能产生费用、只在用户点了工具面板里的「应用」之后才发起,不在后台外发,也没有别的路径会把图发出去;「网络主机 / Network hosts」那段也把这两项列进了走宿主连接器的请求里。两个 README 一起改。测试环境与结果
node --test "tests/smoke.test.mjs":334 / 334 通过(1.22.8 加 5 条;1.22.7 加 3 条;1.22.6 加 1 条、并把一条老测试改成盯住「卡上有没有报时」;1.22.5 加 1 条、并把一条老测试改成盯住「面板是不是宽版」;1.22.4 加 8 条;1.22.3 加 5 条)npm run check(validate + 92 个仓库级测试)通过;plugins/antianqi/ai-node-canvas0 error、1 warning。HOST_CONNECTOR_UNVERIFIED这条 warning 改动前就有(hostConnectorAccess.providers由宿主在安装时授予),本次未新增诊断missing忽略掉、让providerFailure一律说不是失败、把重跑又改回清空产物、直连通道不再看failed_items、不再带出 err_code。1.22.5 的 4 条也全部被抓住:把「面板是不是宽版」永远判成不是、把样式表又改回只认编辑器、去掉继承提示词的限高、app 不再给面板挂上宽版类。1.22.6 的 5 条也全部被抓住:让截止线不触发、不读 context 里的上限、超时文案不说原因、放弃回合时不停掉宿主回合、卡上不显示已跑时长。1.22.7 的 5 条也全部被抓住:把工具改回产出 m4a、上传前不转换、让厂商收全部音频、把 mp3 换成别的编码器、转出来的临时文件不清理。1.22.8 的 5 条也全部被抓住:把节点上的浮层关闭点去掉(红)、让 chip 永远只开不关(红)、让一次 press 不再带走菜单(红)、让运行时不再自己问厂商(红)、让跑完而没人读过的运行不再留在列表里(红)upload_temp_url456ms、generate_image14.3s、get_asset_url552ms,产物落成 922KB 的本地副本并写进节点(status: success、/assets/m_*.jpg)。这就是修好之前断掉的那一步0:48 / 2:57、播完按钮回到 ▶;切主题后波形按新配色重画(亮色深色波形 / 暗色浅色波形)版本
0.87.0 → 1.22.9(
package.json、.minimax-plugin/plugin.json、两个 README 同步)。1.22.9 新增 0 个文件;1.22.8 新增 0 个文件;1.22.7 新增 1 个文件:miniapp/node/provider-audio.mjs;1.22.6 与 1.22.5 新增 0 个文件;1.22.4 新增 8 个文件:miniapp/client/{node-ui,strip,clip-place,clip-playhead,preview-loop}.mjs、miniapp/node/{http-range,image-size,media-outcome}.mjs。未验证
.m4a音轨节点三方对上),修完之后没有再真机跑一次参考生视频——那一次要花生成额度,等用户点一次再补结论。本机 ffmpeg 转 mp3 验过(本机libmp3lame在,10 秒音轨转出来时长不变、编码 mp3)。status始终started),修完之后的上限行为由单测与变异自检背书;真机上那一批卡住的回合随运行时重启而结束,没有再真机跑一次十分钟的慢回合。Open in code smith · Need help on this PR? Tag the code smith bot with what you need.