@leiyi-works/dsh-aivideo 2.7.0 → 2.7.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  DSH Web UI 的一站式视频生产插件:**Flova 负责小说→剧本→分镜**,**人物 / 场景 / 剧本素材面板**负责翻素材,**RunningHub H3 / Seedance 负责分镜→出片**,全部在同一个插件里。
4
4
 
5
- 面板**本体**内置八个页签:**生成 / 视频 / 人物 / 场景 / 剧本 / 使用说明 / 脚本 / Flova** —— 「视频」页只读浏览/播放 `D:\DSH工作区\AI创作\视频` 里的已生成成片;使用说明与运行脚本都随插件打包,并自动同步到 `~/.dsh/h3-kit/`,因此 DSH 备份会一并带上(换机可直接恢复)。
5
+ 面板**本体**内置**两级页签**:**4 个一级页签(H3 / 人物 / 场景 / 剧本)**,其中 H3 内含 **5 个二级页签(生成 / 视频 / 使用说明 / 脚本 / Flova)** —— H3 →「视频」子页只读浏览/播放 `D:\DSH工作区\AI创作\视频` 里的已生成成片;使用说明与运行脚本都随插件打包,并自动同步到 `~/.dsh/h3-kit/`,因此 DSH 备份会一并带上(换机可直接恢复)。
6
6
 
7
7
  ## 三个本地素材插件已合并进本插件(2026-11-14)
8
8
 
@@ -33,7 +33,7 @@ DSH Web UI 的一站式视频生产插件:**Flova 负责小说→剧本→分
33
33
  |---|---|
34
34
  | **Flova 技能** `flova` | Host 注册 skill provider,正文在 `skill/flova-SKILL.md`;对话里直接说「用 flova 写剧本/拆分镜」即可 |
35
35
  | **Flova 账号状态** | 只读路由 `GET /runninghub-app/flova/status`(调用 `flova auth status` + `flova account user`,带 5 秒缓存,不消耗积分)|
36
- | **面板「Flova」页** | 显示登录状态、积分余额、账号邮箱、CLI 路径、检查时间,可刷新;附使用说明 |
36
+ | **面板 H3 →「Flova」二级页签** | 显示登录状态、积分余额、账号邮箱、CLI 路径、检查时间,可刷新;附使用说明 |
37
37
 
38
38
  `dsh-flova` 源码目录仍保留在 `D:\DSH工作区\dsh-flova` 作参考,但已从 profile 的依赖与 bundles 中移除(避免重复注册技能)。
39
39
 
@@ -41,37 +41,44 @@ DSH Web UI 的一站式视频生产插件:**Flova 负责小说→剧本→分
41
41
 
42
42
  | 入口 | 行为 |
43
43
  |---|---|
44
- | **左侧栏「AI视频」** | 点一下:左侧对话留在前台,**DSH 原生右侧栏**打开/聚焦「AI视频」tab(八页签);右栏关掉后再点可重新打开;连续点只会聚焦同一 tab,不会重复实例。若原生右栏不可用,自动回退为整页面板。 |
44
+ | **左侧栏「AI视频」** | 点一下:左侧对话留在前台,**DSH 原生右侧栏**打开/聚焦「AI视频」tab(4 个一级页签,H3 内再分 5 个二级页签);右栏关掉后再点可重新打开;连续点只会聚焦同一 tab,不会重复实例。若原生右栏不可用,自动回退为整页面板。 |
45
45
  | 右侧栏「+」引导页 →「AI视频」 | 与上面相同的原生右栏 tab。 |
46
46
  | 设置 →「AI视频」 | 整页版本(同一面板)。 |
47
47
 
48
48
  左侧入口注册在 `sidebar.panellist`(order 42),图标复用本插件的 AI视频 tab 图标;点击时先 `ctx.layout.selectPanel(null)` 回到会话,再在 microtask 里调用 `ctx.sidebarRight.openTab("h3-direct")`(原生右栏 API 在会话被全局面板覆盖时会拒绝打开,所以顺序不能反)。内部 id(`h3-direct` / `h3-direct-panel`)保持不变,避免破坏既有 layout 状态;**用户可见文案一律是「AI视频」**。
49
49
 
50
- ## 面板八页
50
+ ## 面板页签:4 个一级页签 + H3 内 5 个二级页签
51
51
 
52
- 从上面任一入口打开后,顶部可切换:
52
+ 从上面任一入口打开后,面板顶栏只有 **4 个一级页签**(从左到右),分别对应合并前的 4 个模块:
53
53
 
54
- | 页签 | 内容 |
54
+ | 一级页签 | 来源模块 | 内容 |
55
+ |---|---|---|
56
+ | **H3** | 原 `@leiyi-works/dsh-aivideo` | 出片相关能力;内容区顶部再有一行 5 个二级页签(见下表),默认停在「生成」 |
57
+ | **人物** | 原 `dsh-character-gallery` | 角色 chips 切换;三视图大图 + 定妆图/正面/侧面/背面/特写缩略网格;点图**放大预览**、复制完整路径;显示角色说明.md |
58
+ | **场景** | 原 `dsh-scene-gallery` | 按目录筛选的缩略图网格(全部 / 各目录 chips);点图放大预览、复制完整路径 |
59
+ | **剧本** | 原 `dsh-script-library` | 下拉切换剧本 + 正文 Markdown 渲染(标题/列表/表格/代码块/引用/行内代码);正文内嵌图片可点开放大;复制完整路径 |
60
+
61
+ 点开 **H3** 后,H3 内容区顶部出现一行 **5 个二级页签**(默认「生成」):
62
+
63
+ | H3 二级页签 | 内容 |
55
64
  |---|---|
56
65
  | **生成** | 现有工作流面板:载入 RunningHub 工作流 → 选择视频生成区块 → 编辑参数(提示词/时长/种子/比例等)→ 确认弹窗后提交 → 轮询输出成片 URL |
57
66
  | **视频** | 浏览 `D:\DSH工作区\AI创作\视频` 里的一层 `.mp4`(子目录「抽帧」等不列出):名称/大小/时间列表 + 点击**面板内播放**(原生 `<video controls>`,可拖动进度/音量/全屏)+ 刷新 + 复制目录路径;空态提示「还没有生成的视频」 |
58
- | **人物** | 角色 chips 切换;三视图大图 + 定妆图/正面/侧面/背面/特写缩略网格;点图**放大预览**、复制完整路径;显示角色说明.md |
59
- | **场景** | 按目录筛选的缩略图网格(全部 / 各目录 chips);点图放大预览、复制完整路径 |
60
- | **剧本** | 下拉切换剧本 + 正文 Markdown 渲染(标题/列表/表格/代码块/引用/行内代码);正文内嵌图片可点开放大;复制完整路径 |
61
67
  | **使用说明** | 渲染插件自带 `docs/H3工作流手册.md`(标题/表格/列表/代码块/引用/加粗),支持刷新 |
62
68
  | **脚本** | 列出插件自带 `scripts/` 下的运行脚本(文件名/大小/时间),点开只读查看代码,可「复制路径」 |
63
69
  | **Flova** | Flova 登录状态、积分余额、账号邮箱、CLI 路径与检查时间,可刷新 |
64
70
 
65
- > 「剧本」页是本地剧本库阅读器;「脚本」页是插件自带运行脚本查看器,两者不是一回事。
66
- > 不再有单独的「H3 使用说明」右侧栏入口;说明与脚本都在「AI视频」面板内部。
71
+ > 人物 / 场景 / 剧本三个一级页签直接显示各自面板,没有二级页签。
72
+ > 「剧本」页是本地剧本库阅读器;H3 →「脚本」是插件自带运行脚本查看器,两者不是一回事。
73
+ > 不再有单独的「H3 使用说明」右侧栏入口;说明与脚本在 H3 →「使用说明」/「脚本」里。
67
74
 
68
75
  ## Better Sidebar 可选
69
76
 
70
77
  「AI视频」的右栏 tab 通过 **DSH 原生右侧栏**注册(`ctx.sidebarRightTabs.register` + `sidebar.right.pane.tab` slot + `ctx.sidebarRight.openTab`),**不依赖 dsh-better-sidebar**:
71
78
 
72
79
  - 装了 Better Sidebar:仍从原生右栏「+」引导页打开,行为一致(本插件不调用 `betterSidebar` 服务);也可从 设置 →「AI视频」打开同一面板。
73
- - **没装 Better Sidebar(当前默认)**:功能完全不变——右栏「+」→「AI视频」,八页签正常。
74
- - 八页签在右栏和设置页里都可用;说明/脚本的 `~/.dsh/h3-kit/` 同步与 Better Sidebar 无关,不受影响。
80
+ - **没装 Better Sidebar(当前默认)**:功能完全不变——右栏「+」→「AI视频」,4 个一级页签 + H3 内 5 个二级页签正常。
81
+ - 两级页签在右栏和设置页里都可用;说明/脚本的 `~/.dsh/h3-kit/` 同步与 Better Sidebar 无关,不受影响。
75
82
 
76
83
  ## 内置说明 + 脚本与自动同步(重要)
77
84
 
@@ -94,7 +101,7 @@ DSH Web UI 的一站式视频生产插件:**Flova 负责小说→剧本→分
94
101
  同步失败只记日志、不影响插件。这样 `@xiaoyuyu6420/dsh-backup` 备份 `~/.dsh` 时,会连同**使用说明 + 脚本**一起打包。
95
102
 
96
103
  - 改说明/脚本只改插件里的 `docs/`、`scripts/`,然后重载插件(`set_plugin` 关→开触发 HMR,**禁止 remove_bundle**)或打开一次面板,`~/.dsh/h3-kit/` 即更新。
97
- - 手动刷新说明:面板「使用说明」页 → 刷新(`GET /runninghub-app/manual` 每次调用也会重同步手册)。
104
+ - 手动刷新说明:面板 H3 →「使用说明」二级页签 → 刷新(`GET /runninghub-app/manual` 每次调用也会重同步手册)。
98
105
 
99
106
  ## Host 只读路由
100
107
 
@@ -121,14 +128,14 @@ DSH Web UI 的一站式视频生产插件:**Flova 负责小说→剧本→分
121
128
  1. 新机器装好 DSH,恢复 `~/.dsh`(用 dsh-backup 的恢复功能)。
122
129
  2. 恢复后 `~/.dsh/h3-kit/H3工作流手册.md` 与 `~/.dsh/h3-kit/scripts/` 已随备份还原(运行脚本可直接 `node ~/.dsh/h3-kit/scripts/run-xianxia-v7.js`)。
123
130
  3. 安装/启用本插件(`@leiyi-works/dsh-aivideo`)后,插件加载会以插件自带版本覆盖同步一次 `~/.dsh/h3-kit/`,保证与插件版本一致。
124
- 4. 配置 `~/.dsh/runninghub-app.json`(`apiKey`、`workflowId`、可选 `baseUrl`、`pollIntervalMs`;素材目录可选 `characterDir` / `dirs` / `scriptDirs` / `imageDirs`)后即可在面板「生成」页与三个素材页使用。
131
+ 4. 配置 `~/.dsh/runninghub-app.json`(`apiKey`、`workflowId`、可选 `baseUrl`、`pollIntervalMs`;素材目录可选 `characterDir` / `dirs` / `scriptDirs` / `imageDirs`)后即可在面板 H3 →「生成」及人物/场景/剧本三个一级页签使用。
125
132
 
126
133
  ## 文件结构
127
134
 
128
135
  ```
129
136
  index.js # Host:RunningHub API 工具/路由 + manual/scripts/video 只读路由
130
137
  # + 人物/场景/剧本素材只读路由 + 说明&脚本同步
131
- client.js # Client:AI视频 面板(生成/视频/人物/场景/剧本/使用说明/脚本/Flova 八页签)
138
+ client.js # Client:AI视频 面板(4 个一级页签 H3/人物/场景/剧本;H3 内含 5 个二级页签)
132
139
  docs/H3工作流手册.md # 内置使用说明(同步源)
133
140
  scripts/*.js # 内置运行脚本(同步源)
134
141
  scripts/README.md # 脚本说明(同步源)
@@ -144,4 +151,4 @@ icon.svg # 图标
144
151
 
145
152
  ## 面板设置入口
146
153
 
147
- 本插件**没有可在界面修改的设置**(`apiKey`/`baseUrl`/`workflowId`/`pollIntervalMs` 与素材目录只能编辑 `~/.dsh/runninghub-app.json`,Host 也无配置写路由),因此面板头部**不放**齿轮按钮。原 DSH 设置页里的「H3」入口已按用户要求**删除**(不再注册 settings.section),不影响 AI视频 面板八页签与生成功能。
154
+ 本插件**没有可在界面修改的设置**(`apiKey`/`baseUrl`/`workflowId`/`pollIntervalMs` 与素材目录只能编辑 `~/.dsh/runninghub-app.json`,Host 也无配置写路由),因此面板头部**不放**齿轮按钮。原 DSH 设置页里的「H3」入口已按用户要求**删除**(不再注册 settings.section),不影响 AI视频 面板的两级页签与生成功能。
package/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  window.__ModuleLoader__.load({
2
- id: '@leiyi-works/dsh-aivideo',
2
+ id: "@leiyi-works/dsh-aivideo",
3
3
  factory(require) {
4
4
  const React = require("react");
5
5
  const h = React.createElement;
@@ -17,6 +17,10 @@ window.__ModuleLoader__.load({
17
17
  title: { fontSize: 16, fontWeight: 600, margin: 0 },
18
18
  body: { flex: 1, overflowY: "auto", padding: 16, display: "flex", flexDirection: "column", gap: 14 },
19
19
  pagePane: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column", overflow: "hidden" },
20
+ /** H3 一级页签的外壳(隐藏时用 display:none,保住「生成」页已填的参数与已载入的工作流)。 */
21
+ h3Root: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column", overflow: "hidden" },
22
+ h3Nav: { padding: "8px 18px", borderBottom: BORDER, display: "flex", flex: "0 0 auto", minWidth: 0 },
23
+ h3Pane: { flex: 1, minHeight: 0, display: "flex", flexDirection: "column", overflow: "hidden" },
20
24
  card: { border: BORDER, background: CARD_BG, borderRadius: 12, padding: 14, display: "flex", flexDirection: "column", gap: 12 },
21
25
  section: { fontSize: 14, fontWeight: 600, opacity: 0.9 },
22
26
  label: { fontSize: 12.5, opacity: 0.64, marginBottom: 5, display: "block" },
@@ -90,7 +94,9 @@ window.__ModuleLoader__.load({
90
94
  };
91
95
 
92
96
  function Panel() {
93
- const [page, setPage] = React.useState("generate");
97
+ // 一级页签:4 个模块(H3 / 人物 / 场景 / 剧本);H3 内部再用 h3Page 记二级页签。
98
+ const [page, setPage] = React.useState("h3");
99
+ const [h3Page, setH3Page] = React.useState("generate");
94
100
  const [workflowId, setWorkflowId] = React.useState("");
95
101
  const [loading, setLoading] = React.useState(false);
96
102
  const [videoNodes, setVideoNodes] = React.useState([]);
@@ -300,7 +306,7 @@ window.__ModuleLoader__.load({
300
306
  h(
301
307
  "div",
302
308
  { style: pageTabs.row },
303
- ...PAGE_LIST.map((item) =>
309
+ ...TOP_TABS.map((item) =>
304
310
  h(
305
311
  "button",
306
312
  {
@@ -316,124 +322,152 @@ window.__ModuleLoader__.load({
316
322
  ),
317
323
  h(
318
324
  "div",
319
- { style: page === "generate" ? styles.body : { ...styles.body, display: "none" } },
325
+ { style: page === "h3" ? styles.h3Root : { ...styles.h3Root, display: "none" } },
320
326
  h(
321
327
  "div",
322
- { style: styles.card },
323
- h("span", { style: styles.section }, "工作流"),
328
+ { style: styles.h3Nav },
324
329
  h(
325
330
  "div",
326
- { style: styles.row },
327
- h("input", {
328
- style: styles.input,
329
- placeholder: "RunningHub 工作流 ID",
330
- value: workflowId,
331
- onChange: (event) => setWorkflowId(event.target.value),
332
- }),
333
- h("button", { style: loading ? { ...styles.ghost, ...styles.buttonOff } : styles.ghost, disabled: loading, onClick: () => loadGraph(workflowId) }, loading ? "读取中…" : "载入")
334
- ),
335
- videoNodes.length === 0
336
- ? h("span", { style: styles.hint }, "载入后会列出这个工作流里所有可调的视频生成区块。")
337
- : h(
338
- "div",
339
- null,
340
- h("span", { style: styles.label }, "视频生成区块"),
341
- h(
342
- "select",
343
- {
344
- style: styles.input,
345
- value: String(nodeIndex),
346
- onChange: (event) => {
347
- const index = Number(event.target.value);
348
- setNodeIndex(index);
349
- applyNode(videoNodes, index);
350
- },
351
- },
352
- ...videoNodes.map((node, index) => h("option", { key: node.id, value: String(index) }, `#${node.id} · ${node.title}(${node.params.length} 项)`))
353
- )
331
+ { style: { ...pageTabs.row, marginLeft: 0, flex: "0 1 auto", justifyContent: "flex-start" } },
332
+ ...H3_TABS.map((item) =>
333
+ h(
334
+ "button",
335
+ {
336
+ key: item.id,
337
+ type: "button",
338
+ style: h3Page === item.id ? pageTabs.activeSm : pageTabs.itemSm,
339
+ onClick: () => setH3Page(item.id),
340
+ },
341
+ item.label
354
342
  )
343
+ )
344
+ )
355
345
  ),
356
- selected === undefined
357
- ? null
358
- : h(
346
+ h(
347
+ "div",
348
+ { style: styles.h3Pane },
349
+ h(
350
+ "div",
351
+ { style: h3Page === "generate" ? styles.body : { ...styles.body, display: "none" } },
352
+ h(
359
353
  "div",
360
- { style: styles.nodeCard },
361
- h("span", { style: styles.section }, "生成参数"),
362
- ...selected.params.map((param, index) => renderParam(param, index)),
354
+ { style: styles.card },
355
+ h("span", { style: styles.section }, "工作流"),
363
356
  h(
364
357
  "div",
365
358
  { style: styles.row },
366
- h("button", { style: busy ? { ...styles.button, ...styles.buttonOff } : styles.button, disabled: busy, onClick: run }, busy ? "生成中…" : "开始生成"),
367
- h(
368
- "button",
369
- {
370
- style: styles.ghost,
371
- onClick: () => setValues({ ...initValues }),
372
- title: "恢复为工作流里的当前值",
373
- },
374
- "重置"
375
- )
359
+ h("input", {
360
+ style: styles.input,
361
+ placeholder: "RunningHub 工作流 ID",
362
+ value: workflowId,
363
+ onChange: (event) => setWorkflowId(event.target.value),
364
+ }),
365
+ h("button", { style: loading ? { ...styles.ghost, ...styles.buttonOff } : styles.ghost, disabled: loading, onClick: () => loadGraph(workflowId) }, loading ? "读取中…" : "载入")
376
366
  ),
377
- status === "" ? null : h("div", { style: styles.status }, status)
367
+ videoNodes.length === 0
368
+ ? h("span", { style: styles.hint }, "载入后会列出这个工作流里所有可调的视频生成区块。")
369
+ : h(
370
+ "div",
371
+ null,
372
+ h("span", { style: styles.label }, "视频生成区块"),
373
+ h(
374
+ "select",
375
+ {
376
+ style: styles.input,
377
+ value: String(nodeIndex),
378
+ onChange: (event) => {
379
+ const index = Number(event.target.value);
380
+ setNodeIndex(index);
381
+ applyNode(videoNodes, index);
382
+ },
383
+ },
384
+ ...videoNodes.map((node, index) => h("option", { key: node.id, value: String(index) }, `#${node.id} · ${node.title}(${node.params.length} 项)`))
385
+ )
386
+ )
378
387
  ),
379
- confirmOpen
380
- ? h(
381
- "div",
382
- { style: styles.backdrop, onClick: () => setConfirmOpen(false) },
383
- h(
384
- "div",
385
- { style: styles.modal, onClick: (event) => event.stopPropagation() },
386
- h("div", { style: { fontSize: 17, fontWeight: 600 } }, "确认提交?"),
387
- h(
388
+ selected === undefined
389
+ ? null
390
+ : h(
388
391
  "div",
389
- { style: { fontSize: 14, lineHeight: 1.8, opacity: 0.85 } },
390
- h("div", null, `工作流:${workflowId}`),
391
- h("div", null, `视频区块:#${selected === undefined ? "" : selected.id} · ${selected === undefined ? "" : selected.title}`),
392
- h("div", null, `时长:${values["132.value"] ?? "?"} 秒`),
392
+ { style: styles.nodeCard },
393
+ h("span", { style: styles.section }, "生成参数"),
394
+ ...selected.params.map((param, index) => renderParam(param, index)),
393
395
  h(
394
396
  "div",
395
- { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 4 } },
396
- h("span", null, "实例:"),
397
+ { style: styles.row },
398
+ h("button", { style: busy ? { ...styles.button, ...styles.buttonOff } : styles.button, disabled: busy, onClick: run }, busy ? "生成中…" : "开始生成"),
397
399
  h(
398
- "select",
399
- { style: { ...styles.input, width: 200 }, value: instanceType, onChange: (event) => setInstanceType(event.target.value) },
400
- h("option", { value: "plus" }, "plus(48G 大显存,30 秒必选)"),
401
- h("option", { value: "standard" }, "standard(默认,短片省钱)")
400
+ "button",
401
+ {
402
+ style: styles.ghost,
403
+ onClick: () => setValues({ ...initValues }),
404
+ title: "恢复为工作流里的当前值",
405
+ },
406
+ "重置"
402
407
  )
403
408
  ),
409
+ status === "" ? null : h("div", { style: styles.status }, status)
410
+ ),
411
+ confirmOpen
412
+ ? h(
413
+ "div",
414
+ { style: styles.backdrop, onClick: () => setConfirmOpen(false) },
404
415
  h(
405
416
  "div",
406
- { style: { color: ACCENT, fontWeight: 600, marginTop: 4 } },
407
- `预估消耗:≈ ${Math.round(Number(values["132.value"] ?? 5) * (instanceType === "plus" ? 11.4 : 10.6))} 币(约 ¥${(Number(values["132.value"] ?? 5) * (instanceType === "plus" ? 11.4 : 10.6) * 0.004).toFixed(2)})`
417
+ { style: styles.modal, onClick: (event) => event.stopPropagation() },
418
+ h("div", { style: { fontSize: 17, fontWeight: 600 } }, "确认提交?"),
419
+ h(
420
+ "div",
421
+ { style: { fontSize: 14, lineHeight: 1.8, opacity: 0.85 } },
422
+ h("div", null, `工作流:${workflowId}`),
423
+ h("div", null, `视频区块:#${selected === undefined ? "" : selected.id} · ${selected === undefined ? "" : selected.title}`),
424
+ h("div", null, `时长:${values["132.value"] ?? "?"} 秒`),
425
+ h(
426
+ "div",
427
+ { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 4 } },
428
+ h("span", null, "实例:"),
429
+ h(
430
+ "select",
431
+ { style: { ...styles.input, width: 200 }, value: instanceType, onChange: (event) => setInstanceType(event.target.value) },
432
+ h("option", { value: "plus" }, "plus(48G 大显存,30 秒必选)"),
433
+ h("option", { value: "standard" }, "standard(默认,短片省钱)")
434
+ )
435
+ ),
436
+ h(
437
+ "div",
438
+ { style: { color: ACCENT, fontWeight: 600, marginTop: 4 } },
439
+ `预估消耗:≈ ${Math.round(Number(values["132.value"] ?? 5) * (instanceType === "plus" ? 11.4 : 10.6))} 币(约 ¥${(Number(values["132.value"] ?? 5) * (instanceType === "plus" ? 11.4 : 10.6) * 0.004).toFixed(2)})`
440
+ )
441
+ ),
442
+ h("div", { style: styles.warn }, "⚠ RunningHub 支持取消(实测取消不扣费),但排队/运行期间仍可能产生部分计费。30 秒任务必须用 plus 实例,否则会爆显存。" ),
443
+ h(
444
+ "div",
445
+ { style: { display: "flex", gap: 10, justifyContent: "flex-end" } },
446
+ h("button", { style: styles.ghost, onClick: () => setConfirmOpen(false) }, "取消"),
447
+ h("button", { style: styles.primaryBtn, onClick: submitNow }, "确认提交")
448
+ )
408
449
  )
409
- ),
410
- h("div", { style: styles.warn }, "⚠ RunningHub 支持取消(实测取消不扣费),但排队/运行期间仍可能产生部分计费。30 秒任务必须用 plus 实例,否则会爆显存。" ),
411
- h(
450
+ )
451
+ : null,
452
+ files.length > 0
453
+ ? h(
412
454
  "div",
413
- { style: { display: "flex", gap: 10, justifyContent: "flex-end" } },
414
- h("button", { style: styles.ghost, onClick: () => setConfirmOpen(false) }, "取消"),
415
- h("button", { style: styles.primaryBtn, onClick: submitNow }, "确认提交")
455
+ { style: styles.out },
456
+ h("span", { style: styles.label }, "输出"),
457
+ ...files.map((file, index) => h("a", { key: index, style: styles.link, href: file.url, target: "_blank", rel: "noreferrer" }, file.url))
416
458
  )
417
- )
418
- )
419
- : null,
420
- files.length > 0
421
- ? h(
422
- "div",
423
- { style: styles.out },
424
- h("span", { style: styles.label }, "输出"),
425
- ...files.map((file, index) => h("a", { key: index, style: styles.link, href: file.url, target: "_blank", rel: "noreferrer" }, file.url))
426
- )
427
- : null,
428
- selected === undefined && status !== "" ? h("div", { style: styles.status }, status) : null
459
+ : null,
460
+ selected === undefined && status !== "" ? h("div", { style: styles.status }, status) : null
461
+ ),
462
+ h3Page === "video" ? h("div", { style: styles.pagePane }, h(VideoPanel)) : null,
463
+ h3Page === "manual" ? h("div", { style: styles.pagePane }, h(ManualPanel)) : null,
464
+ h3Page === "scripts" ? h("div", { style: styles.pagePane }, h(ScriptsPanel)) : null,
465
+ h3Page === "flova" ? h("div", { style: styles.pagePane }, h(FlovaPanel)) : null
466
+ )
429
467
  ),
430
- page === "video" ? h("div", { style: styles.pagePane }, h(VideoPanel)) : null,
431
468
  page === "characters" ? h("div", { style: styles.pagePane }, h(CharacterPanel, { variant: "pane" })) : null,
432
469
  page === "scenes" ? h("div", { style: styles.pagePane }, h(ScenePanel, { variant: "pane" })) : null,
433
- page === "script-library" ? h("div", { style: styles.pagePane }, h(ScriptLibraryPanel)) : null,
434
- page === "manual" ? h("div", { style: styles.pagePane }, h(ManualPanel)) : null,
435
- page === "scripts" ? h("div", { style: styles.pagePane }, h(ScriptsPanel)) : null,
436
- page === "flova" ? h("div", { style: styles.pagePane }, h(FlovaPanel)) : null
470
+ page === "script-library" ? h("div", { style: styles.pagePane }, h(ScriptLibraryPanel)) : null
437
471
  );
438
472
  }
439
473
 
@@ -734,13 +768,17 @@ window.__ModuleLoader__.load({
734
768
  );
735
769
  }
736
770
 
737
- /** Pages of the AI视频 panel: generate, videos, the three asset libraries, manual, bundled scripts and Flova. */
738
- const PAGE_LIST = [
739
- { id: "generate", label: "生成" },
740
- { id: "video", label: "视频" },
771
+ /** 一级页签:4 个模块(H3 出片 / 人物 / 场景 / 剧本)。 */
772
+ const TOP_TABS = [
773
+ { id: "h3", label: "H3" },
741
774
  { id: "characters", label: "人物" },
742
775
  { id: "scenes", label: "场景" },
743
776
  { id: "script-library", label: "剧本" },
777
+ ];
778
+ /** 二级页签:H3 页签内部的 5 个页(默认「生成」)。 */
779
+ const H3_TABS = [
780
+ { id: "generate", label: "生成" },
781
+ { id: "video", label: "视频" },
744
782
  { id: "manual", label: "使用说明" },
745
783
  { id: "scripts", label: "脚本" },
746
784
  { id: "flova", label: "Flova" },
@@ -756,11 +794,14 @@ window.__ModuleLoader__.load({
756
794
  border: BORDER,
757
795
  borderRadius: 8,
758
796
  padding: 2,
759
- flex: "1 1 auto",
797
+ flex: "0 1 auto",
760
798
  minWidth: 0,
761
799
  },
762
800
  item: { border: "none", background: "transparent", color: "inherit", fontSize: 12.5, padding: "4px 10px", borderRadius: 6, cursor: "pointer", opacity: 0.72 },
763
801
  active: { border: "none", background: ACCENT, color: "#fff", fontSize: 12.5, padding: "4px 10px", borderRadius: 6, cursor: "pointer", fontWeight: 600, opacity: 1 },
802
+ /** 二级页签(H3 内部):比一级页签小一号。 */
803
+ itemSm: { border: "none", background: "transparent", color: "inherit", fontSize: 11.5, padding: "3px 9px", borderRadius: 6, cursor: "pointer", opacity: 0.72 },
804
+ activeSm: { border: "none", background: ACCENT, color: "#fff", fontSize: 11.5, padding: "3px 9px", borderRadius: 6, cursor: "pointer", fontWeight: 600, opacity: 1 },
764
805
  };
765
806
 
766
807
  /** 「Flova」page: 只读显示 Flova 登录状态与积分(合并自 dsh-flova)。 */
package/cordis.patch.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  - insert:
2
- - id: aivideo
2
+ - id: runninghub-app
3
3
  name: '@leiyi-works/dsh-aivideo'
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "AI视频",
4
- "description": "One-stop video production: Flova for scripts and storyboards, built-in character / scene / script library browsing, and RunningHub MiniMax H3 generation, with eight sidebar tabs (Generate / Videos / Characters / Scenes / Scripts / Manual / Script files / Flova)."
4
+ "description": "One-stop video production: Flova for scripts and storyboards, built-in character / scene / script library browsing, and RunningHub MiniMax H3 generation; the sidebar panel has four top tabs (H3 / Characters / Scenes / Scripts), with five sub-tabs inside H3 (Generate / Videos / Manual / Script files / Flova)."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
3
  "title": "AI视频",
4
- "description": "一站式视频生产:Flova 写剧本 / 拆分镜,内置人物、场景、剧本素材库的浏览面板,RunningHub MiniMax H3 出片,右侧栏面板含生成 / 视频 / 人物 / 场景 / 剧本 / 使用说明 / 脚本 / Flova 八个页签。"
4
+ "description": "一站式视频生产:Flova 写剧本 / 拆分镜,内置人物、场景、剧本素材库的浏览面板,RunningHub MiniMax H3 出片;右侧栏面板为 4 个一级页签(H3 / 人物 / 场景 / 剧本),H3 内含生成 / 视频 / 使用说明 / 脚本 / Flova 五个二级页签。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@leiyi-works/dsh-aivideo",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "type": "module",
5
5
  "title": "AI视频",
6
- "description": "AI视频:一站式 AI 短剧生产插件。Flova(小说→剧本→分镜)+ 本机素材库(人物三视图 / 场景图 / 剧本 Markdown)+ RunningHub MiniMax H3 / Seedance(首帧→出片→预览),八个页签一个入口。",
6
+ "description": "AI视频:一站式 AI 短剧生产插件。4 个一级页签(H3 / 人物 / 场景 / 剧本),H3 内含 5 个二级页签(生成 / 视频 / 使用说明 / 脚本 / Flova);Flova 做剧本分镜,本机素材库管理人物三视图 / 场景图 / 剧本,RunningHub MiniMax H3 / Seedance 出片。",
7
7
  "exports": {
8
8
  ".": "./index.js",
9
9
  "./client": "./client.js",