@ninkch/dsh-infinite-canvas 0.18.2 → 0.18.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.
@@ -39,19 +39,31 @@ B:……
39
39
  面板文字只允许 ≤4 字短标签(面板名 / 表情名 / 颜色名)。同一资产在所有分格中外观**完全一致**——
40
40
  **已连接参考图时,参考图形象是唯一事实**,逐格严格复制;未连接参考图时逐格复用设定集外观关键词。
41
41
 
42
- ### 角色设定图(16:9 横版——主图与三视图占大版面)
43
- - **版面主体(约三分之二)给画面**:中央大幅全身立绘主图(正面站姿),主图下方一排
44
- **三视图(正面 / 侧面 / 背面 全身)**——三视图是必须项,后续视频要靠它保证侧背面的准确形态。
45
- - **右侧窄栏做纯视觉辅助(不渲染文字段落)**:
46
- - 脸部特征:一格正面特写(或正 / 侧两小格);
47
- - 表情条:一行 4–6 个小头像,每格下方只标表情名(两字,按角色性格取舍,如 温柔 / 惊讶 / 坚定);
48
- - 细节特写(可选):1–2 格(项圈 / 挂牌 / 发饰),格内无文字。
49
- - **禁止**:姓名 / 性别 / 年龄等属性文本、服饰说明文字、旁白或诗句——这些属于设定集,
50
- 画进图里必成乱码,且会挤出角色形象的一致性注意力。
51
- - 所有分格中同一角色外观完全一致(以参考图形象为准逐格复制)。
52
- - 提示词结构示例:「角色人设图(16:9 横版设定集排版):中央大幅全身立绘,主图下方三视图一排
53
- (正面/侧面/背面全身);右侧窄栏:脸部特写一格、表情小头像一排(每格下方只标两字表情名);
54
- 画面中不出现任何成段文字。」
42
+ ### 角色设定图(16:9 横版——按「六段式」结构写提示词,实测出图最稳)
43
+
44
+ 提示词按以下六段组织,**方括号段名照写**(模型对显式分段结构的遵循度显著更好):
45
+
46
+ - **【总起】**一句话交代:16:9 横版专业设定集排版、深色简洁背景、风格基调(如 写实电影感)、
47
+ 同一角色在所有分格中外观完全一致。
48
+ - **【身份锚】**声明参考图是同一角色的真实照片:严格保持脸型 / 眼型 / 耳形 / 毛量 / 口鼻比例 / 体型,
49
+ 只改变姿势与视角;**忽略照片里的临时配饰**(狗绳、挂牌等),配饰一律按提示词文字描述绘制。
50
+ 绑定几张参考图就写几个 [图N]。
51
+ - **【主体】**中央大幅全身立绘(正面站姿):体型写量化并加反例(如「约 3 公斤、肩高 20 厘米;
52
+ 不是中型犬,不是萨摩耶/银狐/柴犬」——易混淆品种必须显式排除)、毛色毛发、五官、四肢尾巴,
53
+ 以及固定配饰的完整描述。
54
+ - **【主图下方·三视图(必须项)】**正面/侧面/背面三格全身站姿——同一角色只换角度,
55
+ 交代侧背形态(后续视频一致性靠它)。
56
+ - **【右侧窄栏·自上而下分块;每块内容全图只出现一次】**
57
+ ① 脸部特写一格 + 一行 ≤12 字的五官短标注;
58
+ ② 配饰格(有固定配饰时):正 / 背两张小图并排,可刻短字,并注明「该物在本图内只出现这一处」;
59
+ ③ 表情格:6 个头部小头像分两行(每行 3 个),每个下方只标两字表情名。
60
+ - **【文字规则】**图内文字极少:只保留两三个字的短标签,黑色细体、笔画清楚、字要写大;
61
+ 不写任何大标题、说明段或长句。
62
+ - **【画面白名单】**明确写出「画面中只有这一只角色和上述分格内容;没有人物、没有其他动物、
63
+ 没有乐器、没有文字水印」——反幻觉白名单实测能压住乱入元素。
64
+
65
+ 要点:**每块内容全图只出现一次**(配饰在配饰格画过,主图描述就不再重复强调它);
66
+ 反例约束有效——易混淆的品种 / 风格务必写进【主体】的排除句。
55
67
 
56
68
  ### 道具设定图(横竖皆可——按道具特性选,不限制)
57
69
  - **画幅选择**:长条形/单件道具(剑、箫、杖)竖版更能展示完整形态;
@@ -141,8 +141,10 @@ description: 用无限画布(Infinite Canvas)做短剧/视频创作时使用
141
141
  (命名规范「角色-<名> 设定图」/「场景-<名> 设定图」/「道具-<名> 设定图」),
142
142
  **prompt 必须按 `canvas-craft`「资产设定图版式」一节的排版模板写详细版式提示词,
143
143
  禁止只写一句三视图/外观关键词**——信息量不足,撑不起后续视频的角色一致性。要点(完整模板见 canvas-craft):
144
- - **角色设定图**(16:9 横版):**主图 + 三视图一排(正/侧/背全身,必须项)占大版面(约 2/3)**;
145
- 右窄栏纯视觉辅助(脸部特写一格 / 表情条一行小头像,每格只标两字表情名);细节格可选
144
+ - **角色设定图**(16:9 横版):按 canvas-craft 的「六段式」结构写——【总起】(版式/背景/风格/
145
+ 全格一致)→【身份锚】(参考图=唯一形象事实,忽略照片配饰)→【主体】(量化体型+反例排除)→
146
+ 【三视图·必须项】→【右侧窄栏:脸部特写 / 配饰格 / 表情两行×3,每格只标两字标签,
147
+ 每块内容全图只出现一次】→【文字规则 + 画面白名单】
146
148
  - **道具设定图**(横竖皆可,按道具特性选):顶部道具名大标题(≤6 字);中央主图(斜 45° 悬浮+氛围光);
147
149
  细节特写 2–3 格(格内无文字);侧栏侧视图;底部圆形色卡 3–5 枚(色名 ≤3 字)
148
150
  - **场景设定图**(主图+环绕小图):一张主场景大图占主体,周边环绕补充小图(多角度 / 其他时间段 / 细节格,3–6 张各配 ≤6 字短标注)——大致参考不强约束
package/client.js CHANGED
@@ -2243,12 +2243,20 @@ window.__ModuleLoader__.load({
2243
2243
  // 参考图走三步上传(upload_urls → PUT 签名直传 → assets 登记)换 asset_id;
2244
2244
  // 登记结果缓存在画布资产元数据(compAssetIdCache):同一张 CAS 资产不重复上传。
2245
2245
  const compAssetIdCache = new Map(); // 画布资产 id → 优云 asset_id
2246
- /** 参考图上传前置:统一 MIME/扩展名并测真实像素尺寸(平台校验上传宽高必须与图片一致)。 */
2246
+ /** 参考图上传前置:魔数嗅探真实编码(平台仅收 JPG/PNG/WebP,且校验声明 MIME 与实际字节一致——
2247
+ * 改后缀的 AVIF/HEIC 会在登记时报 "Params [Width/Height] not available")+ 测真实像素尺寸。 */
2247
2248
  async function imageBlobMeta(input) {
2248
2249
  const blob = input.blob || input;
2249
- const type = (blob.type || 'image/png').toLowerCase();
2250
- const mime = /jpeg|jpg/.test(type) ? 'image/jpeg' : /webp/.test(type) ? 'image/webp' : 'image/png';
2251
- const ext = mime === 'image/jpeg' ? 'jpg' : mime === 'image/webp' ? 'webp' : 'png';
2250
+ const head = new Uint8Array(await blob.slice(0, 16).arrayBuffer());
2251
+ const isPng = head[0] === 0x89 && head[1] === 0x50 && head[2] === 0x4e && head[3] === 0x47;
2252
+ const isJpg = head[0] === 0xff && head[1] === 0xd8 && head[2] === 0xff;
2253
+ const isWebp = head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46
2254
+ && head[8] === 0x57 && head[9] === 0x45 && head[10] === 0x42 && head[11] === 0x50;
2255
+ if (!isPng && !isJpg && !isWebp) {
2256
+ throw new Error('参考图编码不受支持:平台仅接受 JPG/PNG/WebP 原生编码(当前文件的实际字节不是这三类,常见于改后缀的 AVIF/HEIC)——请转成 JPG/PNG 后重新挂参考图');
2257
+ }
2258
+ const mime = isPng ? 'image/png' : isJpg ? 'image/jpeg' : 'image/webp';
2259
+ const ext = isPng ? 'png' : isJpg ? 'jpg' : 'webp';
2252
2260
  let width = 0;
2253
2261
  let height = 0;
2254
2262
  try {
@@ -2257,7 +2265,7 @@ window.__ModuleLoader__.load({
2257
2265
  height = bmp.height;
2258
2266
  try { bmp.close && bmp.close(); } catch { /* 忽略 */ }
2259
2267
  } catch { /* 测量失败交由下方显式报错 */ }
2260
- if (!width || !height) throw new Error('参考图尺寸测量失败(仅支持 PNG/JPG/WebP)');
2268
+ if (!width || !height) throw new Error('参考图尺寸测量失败');
2261
2269
  return { blob, mime, ext, width, height };
2262
2270
  }
2263
2271
  async function compshareImageTask(prompt, refs, s, opts) {
@@ -2271,11 +2279,11 @@ window.__ModuleLoader__.load({
2271
2279
  const h = { ...headers };
2272
2280
  if (idem) h['Idempotency-Key'] = idem;
2273
2281
  const r = await fetchViaProxy(base + path, { method, headers: h, ...(body !== undefined ? { body } : {}), timeoutMs: 120000, label: '优云图片 ' + method });
2274
- if (!r.ok) throw new Error('优云图片 API HTTP ' + r.status + ':' + r.text.slice(0, 300));
2275
- let j = null; try { j = JSON.parse(r.text); } catch { throw new Error('优云图片 API 响应非 JSON:' + r.text.slice(0, 200)); }
2282
+ if (!r.ok) throw new Error(`优云图片 API ${method} ${path} HTTP ${r.status}:` + r.text.slice(0, 300));
2283
+ let j = null; try { j = JSON.parse(r.text); } catch { throw new Error(`优云图片 API ${method} ${path} 响应非 JSON:` + r.text.slice(0, 200)); }
2276
2284
  // 网关业务错误可能 HTTP 200:RetCode≠0 或 {type:'error'} 都算失败
2277
2285
  if ((j.RetCode != null && Number(j.RetCode) !== 0) || j.type === 'error') {
2278
- throw new Error('优云图片 API 失败:' + ((j.error && j.error.message) || j.Message || JSON.stringify(j).slice(0, 200)));
2286
+ throw new Error(`优云图片 API ${method} ${path} 失败:` + ((j.error && j.error.message) || j.Message || JSON.stringify(j).slice(0, 200)));
2279
2287
  }
2280
2288
  return j;
2281
2289
  };
@@ -8546,7 +8554,7 @@ window.__ModuleLoader__.load({
8546
8554
  h('input', { type: 'checkbox', checked: p.generateAudio === true, onChange: (e) => patchProvider(p.id, 'generateAudio', e.target.checked), onPointerDown: (e) => e.stopPropagation() }),
8547
8555
  h('span', {}, T('vidAudioOpt'))) : null,
8548
8556
  ],
8549
- (kind === 'image' || detectVideoVendor(p) === 'openai' || detectVideoVendor(p) === 'seedance') ? [
8557
+ (kind === 'image' || kind === 'video') ? [
8550
8558
  h(Field, { key: 'extra', label: T('cfgExtra'), value: p.extra || '', onChange: (v) => patchProvider(p.id, 'extra', v), placeholder: '{"watermark":false,"prompt_extend":false}', tip: T('cfgExtraHint') }),
8551
8559
  ] : null,
8552
8560
  h('div', { className: 'dshic-row', style: { marginTop: 4 } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ninkch/dsh-infinite-canvas",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "description": "Node-based short-drama studio for DeepSeek Harness: script → storyboard → assets → shots → voiced, subtitled final cut on an infinite canvas.",
5
5
  "keywords": [
6
6
  "dsh",