dsh-plugin-deck-studio 0.2.3 → 0.2.5
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 +6 -1
- package/client.js +85 -10
- package/cordis.example.yml +3 -0
- package/cordis.patch.yml +4 -0
- package/index.js +15 -4
- package/lib/config.js +13 -2
- package/package.json +10 -3
- package/seeds/docs/compat.md +21 -28
- package/seeds/docs/install.en.md +4 -5
- package/seeds/docs/install.md +38 -4
- package/ui/workbench.html +64 -17
- package/seeds/docs/productization.en.md +0 -212
- package/seeds/docs/productization.md +0 -193
package/README.md
CHANGED
|
@@ -10,7 +10,10 @@ DSH(DeepSeek Harness)的 **image deck 工作台插件**:左侧原生 Chat
|
|
|
10
10
|
dsh --profile deckline --from-default-profile web
|
|
11
11
|
|
|
12
12
|
# 2) 安装本插件(安装后会自动注册 bundle,无需手动改配置文件)
|
|
13
|
-
dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.
|
|
13
|
+
dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.4
|
|
14
|
+
|
|
15
|
+
# 国内网络建议走镜像(否则从 npmjs.org 拉 20 MB 包可能很慢)
|
|
16
|
+
npm_config_registry=https://registry.npmmirror.com dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.4
|
|
14
17
|
|
|
15
18
|
# 3) 启动,浏览器打开终端打印的带 token 链接
|
|
16
19
|
dsh --profile deckline --port 13090
|
|
@@ -22,6 +25,8 @@ dsh --profile deckline --port 13090
|
|
|
22
25
|
dsh --profile deckline --dump-config | grep -c "preset-deck-critic"
|
|
23
26
|
```
|
|
24
27
|
|
|
28
|
+
在 npmjs.com 搜 `deckline` 或 `ppt dsh` 也能找到。也可以在 DSH「插件 → 发现」里加索引源 `https://cdn.jsdelivr.net/gh/yuna78/dsh-deck-studio@main/market-index.json` 后点安装。
|
|
29
|
+
|
|
25
30
|
从源码 / Git 安装、Windows、以及完整配置(数据目录 / deck 源 / 出图节点 / key)见仓库文档:
|
|
26
31
|
|
|
27
32
|
- [docs/install.md](https://github.com/yuna78/dsh-deck-studio/blob/main/docs/install.md)(中文)
|
package/client.js
CHANGED
|
@@ -361,9 +361,88 @@ div[data-sidebar-collapsed]{grid-template-columns:0px minmax(0px,1fr) 0px!import
|
|
|
361
361
|
return () => window.removeEventListener("message", onMessage);
|
|
362
362
|
}, "deck-studio: prompt bridge");
|
|
363
363
|
|
|
364
|
+
/**
|
|
365
|
+
* F-164:自动新建的会话要落进一个 DSH 工作区,而不是「未分组」。
|
|
366
|
+
* F-165:显式 workspacePath → 启动目录所属工作区 / 登记启动目录 → 源目录工作区 → 默认工作区。
|
|
367
|
+
* 工作区创建失败必须报错,不能悄悄建立未分组会话。
|
|
368
|
+
*/
|
|
369
|
+
/** 取一个可选服务:没 inject 的服务用 ctx.xxx 直取会 throw,这里统一吞掉。 */
|
|
370
|
+
function optionalService(name) {
|
|
371
|
+
try {
|
|
372
|
+
const viaGet = ctx.get?.(name);
|
|
373
|
+
if (viaGet) return viaGet;
|
|
374
|
+
} catch {
|
|
375
|
+
/* ctx.get 不可用 */
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
return ctx[name] ?? null;
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function resolveSessionWorkspace(data) {
|
|
385
|
+
const workspaces = optionalService("workspaces");
|
|
386
|
+
if (!workspaces?.list?.getSnapshot || !workspaces?.create) {
|
|
387
|
+
throw new Error("工作区服务不可用,未创建会话;请检查 DSH 版本并重试");
|
|
388
|
+
}
|
|
389
|
+
await workspaces.refresh?.();
|
|
390
|
+
const items = workspaces.list.getSnapshot().items ?? [];
|
|
391
|
+
const normalize = (path) => String(path ?? "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
392
|
+
const containing = (path) => items
|
|
393
|
+
.filter((item) => normalize(path) === normalize(item.path) || normalize(path).startsWith(`${normalize(item.path)}/`))
|
|
394
|
+
.sort((a, b) => b.path.length - a.path.length)[0];
|
|
395
|
+
const register = async (path) => {
|
|
396
|
+
const workspace = await workspaces.create({ path });
|
|
397
|
+
if (!workspace?.workspaceId) throw new Error("工作区创建未返回 workspaceId");
|
|
398
|
+
return workspace.workspaceId;
|
|
399
|
+
};
|
|
400
|
+
const configured = String(data.workspacePath ?? "").trim();
|
|
401
|
+
if (configured) return register(configured);
|
|
402
|
+
const cwd = String(data.cwd ?? "").trim();
|
|
403
|
+
if (cwd) {
|
|
404
|
+
const hit = containing(cwd);
|
|
405
|
+
if (hit) return hit.workspaceId;
|
|
406
|
+
// 不把插件安装目录或 DSH 私有数据目录登记成用户工作区。
|
|
407
|
+
const path = normalize(cwd);
|
|
408
|
+
const internal = /(^|\/)node_modules(\/|$)|(^|\/)\.dsh(\/|$)/.test(path);
|
|
409
|
+
if (path && path !== "/" && !internal) return register(cwd);
|
|
410
|
+
}
|
|
411
|
+
const sourceRoot = String(data.sourceRoot ?? "").trim();
|
|
412
|
+
const sourceWorkspace = sourceRoot ? containing(sourceRoot) : null;
|
|
413
|
+
if (sourceWorkspace) return sourceWorkspace.workspaceId;
|
|
414
|
+
const fallback = await workspaces.initializeDefault?.();
|
|
415
|
+
if (fallback?.workspaceId) return fallback.workspaceId;
|
|
416
|
+
throw new Error("无法确定启动工作区,未创建会话;请配置 workspacePath 后重试");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async function ensureConversationSession(data) {
|
|
420
|
+
const sessions = ctx.sessions;
|
|
421
|
+
if (!sessions?.create) throw new Error("sessions service unavailable");
|
|
422
|
+
const sessionId = String(data.sessionId ?? "").trim();
|
|
423
|
+
if (sessionId) {
|
|
424
|
+
await sessions.refresh?.();
|
|
425
|
+
const readPreset = () => {
|
|
426
|
+
const state = sessions.list?.getSnapshot?.();
|
|
427
|
+
const row = state?.byId?.[sessionId] || state?.items?.find((item) => (item.sessionId || item.id) === sessionId);
|
|
428
|
+
return row?.projectionValues?.agentPreset ?? row?.projections?.values?.agentPreset
|
|
429
|
+
?? state?.projectionsBySession?.[sessionId]?.values?.agentPreset;
|
|
430
|
+
};
|
|
431
|
+
await sessions.refreshProjections?.(sessionId);
|
|
432
|
+
const preset = readPreset();
|
|
433
|
+
if (preset === "deck-studio") return sessionId;
|
|
434
|
+
if (!preset) throw new Error("无法核对会话模式,请刷新后重试;原会话已保留");
|
|
435
|
+
// 已有非空会话不能换 preset:保留原历史,另建制作会话,工作台负责追加绑定。
|
|
436
|
+
}
|
|
437
|
+
const workspaceId = await resolveSessionWorkspace(data);
|
|
438
|
+
const created = await sessions.create({ workspaceId, agentPreset: "deck-studio" });
|
|
439
|
+
if (!created) throw new Error("session create returned no id");
|
|
440
|
+
return created;
|
|
441
|
+
}
|
|
442
|
+
|
|
364
443
|
/* 工作台 → 宿主:建/切会话并绑定 deck|design(F-43:每个会话必须有绑定)。
|
|
365
444
|
* - action=open:切到给定 sessionId;
|
|
366
|
-
* - action=ensure
|
|
445
|
+
* - action=ensure:核对制作模式后复用,否则指定工作区和 Deckline preset 新建再切,
|
|
367
446
|
* 然后可选地把首条 prompt 发进去(design 会话确认后「chat 开始工作」)。 */
|
|
368
447
|
ctx.effect(() => {
|
|
369
448
|
const onMessage = async (event) => {
|
|
@@ -377,19 +456,15 @@ div[data-sidebar-collapsed]{grid-template-columns:0px minmax(0px,1fr) 0px!import
|
|
|
377
456
|
}
|
|
378
457
|
};
|
|
379
458
|
try {
|
|
380
|
-
const uiWorkspace =
|
|
459
|
+
const uiWorkspace = optionalService("uiWorkspace");
|
|
381
460
|
if (!uiWorkspace?.openSession) throw new Error("uiWorkspace service unavailable");
|
|
382
461
|
let sessionId = String(data.sessionId ?? "").trim();
|
|
383
|
-
if (data.action === "ensure"
|
|
384
|
-
const sessions = ctx.sessions;
|
|
385
|
-
if (!sessions?.create) throw new Error("sessions service unavailable");
|
|
386
|
-
// 注意:客户端 sessions.create 直接 resolve 新 sessionId,失败才 throw(不是 {ok,value})。
|
|
387
|
-
sessionId = await sessions.create({ cwd: String(data.cwd ?? "") || undefined });
|
|
388
|
-
if (!sessionId) throw new Error("session create returned no id");
|
|
389
|
-
}
|
|
462
|
+
if (data.action === "ensure") sessionId = await ensureConversationSession(data);
|
|
390
463
|
if (!sessionId) throw new Error("没有目标会话");
|
|
391
464
|
if (data.action === "open" || data.action === "ensure") uiWorkspace.openSession(sessionId);
|
|
392
|
-
if (data.prompt && data.action === "ensure"
|
|
465
|
+
if (data.prompt && data.action === "ensure" && sessionId !== String(data.sessionId ?? "")) {
|
|
466
|
+
await sendToSession(sessionId, String(data.prompt));
|
|
467
|
+
}
|
|
393
468
|
reply({ ok: true, sessionId });
|
|
394
469
|
} catch (error) {
|
|
395
470
|
ctx.logger?.warn?.("deck-studio: conversation bridge failed", error);
|
package/cordis.example.yml
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
# dataRoot: '~/.dsh/deckline'
|
|
17
17
|
# 可选:默认收起 DSH 外壳左侧栏(只建议在专门的产品 profile 里开;默认 false)
|
|
18
18
|
hideShellSidebar: true # 收起 DSH 外壳左侧栏(工作区/会话);不想收就改 false
|
|
19
|
+
# F-164:工作台自动新建的会话落在哪个 DSH 工作区。留空 = 自动:
|
|
20
|
+
# 宿主启动目录所属工作区(没有则登记)→ deck 源目录所在工作区 → DSH 默认工作区。
|
|
21
|
+
# workspacePath: '~/Documents/我的 deck'
|
|
19
22
|
bootstrap: true # 首次启动自动登记示例 deck / 资源库 / 预设风格
|
|
20
23
|
userName: '' # 留空 = 首页只显示「欢迎你」
|
|
21
24
|
# 新建 deck 的落盘根;留空 = 插件数据目录 .deck/projects/<id>/source
|
package/cordis.patch.yml
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
userName: '' # 留空 = 首页只显示「欢迎你」;填了就显示「XX,欢迎你」
|
|
13
13
|
# F-157:产品默认收起 DSH 外壳左侧栏(工作区/会话那栏),要保留就改 false。
|
|
14
14
|
hideShellSidebar: true
|
|
15
|
+
# F-164:工作台自动新建的会话落在哪个 DSH 工作区(目录)。
|
|
16
|
+
# 留空 = 自动:宿主启动目录所属工作区(没有则登记)→ deck 源目录所在工作区 → DSH 默认工作区。
|
|
17
|
+
# 想固定就填绝对路径(如 '~/Documents/我的 deck'),会自动登记成工作区。
|
|
18
|
+
# workspacePath: ''
|
|
15
19
|
# F-157:首次启动自动登记示例 deck / 拷资源库 / 导入 8 个预设风格;不想要就改 false。
|
|
16
20
|
bootstrap: true
|
|
17
21
|
# 新建 deck 落盘根:留空 = 插件数据目录 .deck/projects/<id>/source
|
package/index.js
CHANGED
|
@@ -1705,6 +1705,7 @@ export function createService(config, logger, env = process.env) {
|
|
|
1705
1705
|
uiVersion: uiVersion(),
|
|
1706
1706
|
displayName: config.displayName,
|
|
1707
1707
|
hideShellSidebar: config.hideShellSidebar === true,
|
|
1708
|
+
workspacePath: config.workspacePath,
|
|
1708
1709
|
dataRoot: config.dataRoot,
|
|
1709
1710
|
toolsMounted: config.tools,
|
|
1710
1711
|
renderMode: config.render.mode,
|
|
@@ -1968,7 +1969,10 @@ export function createService(config, logger, env = process.env) {
|
|
|
1968
1969
|
}
|
|
1969
1970
|
return {
|
|
1970
1971
|
...base,
|
|
1971
|
-
|
|
1972
|
+
// F-164:会话默认落在哪个目录 / 工作区。npm 布局下 repoRootFrom 会退到插件包内的 seeds/,
|
|
1973
|
+
// 不能拿来当会话 cwd(会落到「未分组」)。优先显式配置的 workspacePath,否则用宿主启动目录。
|
|
1974
|
+
cwd: config.workspacePath || process.cwd(),
|
|
1975
|
+
workspacePath: config.workspacePath,
|
|
1972
1976
|
config: {
|
|
1973
1977
|
preset: "deck-studio",
|
|
1974
1978
|
renderMode: config.render.mode,
|
|
@@ -2273,9 +2277,16 @@ export function apply(ctx, rawConfig) {
|
|
|
2273
2277
|
const url = new URL(request?.url ?? "/", "http://localhost");
|
|
2274
2278
|
const deckId = url.searchParams.get("deckId") ?? "";
|
|
2275
2279
|
const docId = url.searchParams.get("docId") ?? "";
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2280
|
+
// F-163:读取失败要把真实原因吐给前端,不能 500 空手而归(前端以前会一直转圈)。
|
|
2281
|
+
try {
|
|
2282
|
+
const doc = service.deckDoc(deckId, docId);
|
|
2283
|
+
if (!doc) return json({ ok: false, error: { message: `unknown deck or doc:deckId=${deckId || "(空)"} docId=${docId || "(空)"}` } }, 404);
|
|
2284
|
+
return json({ ok: true, deckId, ...doc });
|
|
2285
|
+
} catch (error) {
|
|
2286
|
+
const message = String(error?.message ?? error);
|
|
2287
|
+
logger?.warn?.(`deck-studio.doc 读取失败 deckId=${deckId} docId=${docId}:${message}`);
|
|
2288
|
+
return json({ ok: false, error: { message } }, 500);
|
|
2289
|
+
}
|
|
2279
2290
|
});
|
|
2280
2291
|
|
|
2281
2292
|
registerRoute(ROUTES.docfile, (request) => {
|
package/lib/config.js
CHANGED
|
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
export const PLUGIN_DIR = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* 资产根:源码树根 `<repo>/plugin/deck-studio/` 的上一级。
|
|
22
22
|
*
|
|
23
23
|
* npm 安装时插件在 `<profile>/node_modules/dsh-plugin-deck-studio`,上两级是 node_modules,
|
|
24
24
|
* 那里没有 samples / docs;公开包把手册与示例 deck 一起打进包内 `seeds/`(prepack 生成),
|
|
@@ -82,7 +82,7 @@ function normalizeDeck(raw, env) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/** 展开 `~/` 前缀;其余原样。 */
|
|
85
|
-
/** 默认数据根:安装用户的可写位置(不落进插件包 / node_modules
|
|
85
|
+
/** 默认数据根:安装用户的可写位置(不落进插件包 / node_modules)。源码调试时可在 profile 里显式配 dataRoot。 */
|
|
86
86
|
export function defaultDataRoot(env = process.env) {
|
|
87
87
|
return join(expandHome(env.DSH_HOME || "~/.dsh", env), "deckline");
|
|
88
88
|
}
|
|
@@ -179,6 +179,17 @@ export function mergeConfig(raw, options = {}) {
|
|
|
179
179
|
* 开发/其他 profile 的 DSH 界面不受影响。
|
|
180
180
|
*/
|
|
181
181
|
hideShellSidebar: input.hideShellSidebar === true,
|
|
182
|
+
/**
|
|
183
|
+
* F-164:工作台自动新建的会话落在哪个 DSH 工作区(目录)。
|
|
184
|
+
* 留空 = 自动:先找 deck 源目录所在工作区,再找宿主 cwd 工作区,再退回 DSH 默认工作区;
|
|
185
|
+
* 填了就把这个目录登记/复用一个工作区,会话固定落在那里(不再出现「未分组」)。
|
|
186
|
+
*/
|
|
187
|
+
workspacePath: expandHome(
|
|
188
|
+
typeof input.workspacePath === "string" && input.workspacePath.trim()
|
|
189
|
+
? input.workspacePath.trim()
|
|
190
|
+
: (env.DSH_DECK_WORKSPACE_PATH ?? ""),
|
|
191
|
+
env,
|
|
192
|
+
),
|
|
182
193
|
/**
|
|
183
194
|
* 新建 Deck 弹窗默认预选的登记风格(F-112):填 design 缩写(如 GRD)。
|
|
184
195
|
* 留空 = 不预选。只在 UI 预选,不改变任何已有 deck 的关联风格。
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-deck-studio",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "Deckline:DSH image deck
|
|
5
|
+
"description": "Deckline:DSH 的 image deck / PPT 工作台插件——原生 Chat + 整页图片 deck 工作区 + 设计风格库;用对话做 PPT、按页出图、版本可追踪、导出 PDF/PPTX。",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./index.js",
|
|
@@ -19,7 +19,14 @@
|
|
|
19
19
|
"image-deck",
|
|
20
20
|
"presentation",
|
|
21
21
|
"slides",
|
|
22
|
-
"
|
|
22
|
+
"ppt",
|
|
23
|
+
"pptx",
|
|
24
|
+
"powerpoint",
|
|
25
|
+
"ai-ppt",
|
|
26
|
+
"slide-deck",
|
|
27
|
+
"deckline",
|
|
28
|
+
"幻灯片",
|
|
29
|
+
"演示文稿"
|
|
23
30
|
],
|
|
24
31
|
"engines": {
|
|
25
32
|
"node": "^22.19.0 || >=24.0.0",
|
package/seeds/docs/compat.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# DSH 兼容矩阵(定期回归记录)
|
|
2
2
|
|
|
3
3
|
> 口径:DSH 处于 prerelease 阶段,没有 stable 线;本项目**定期批次**回归,不逐 rc 跟。
|
|
4
|
-
>
|
|
4
|
+
> 每个批次记录:证实的版本、验证范围、断点、复现方式。已知限制见 [install.md](install.md) 的故障排查部分。
|
|
5
5
|
|
|
6
6
|
## 当前支持声明
|
|
7
7
|
|
|
@@ -79,42 +79,35 @@ agent 工具链路与真出图待下批补验。支持声明暂不收紧到 0.2.
|
|
|
79
79
|
挂好(脚本直接 `goto` 会误判「工作台没加载」);等插件 patch 应用完成或先 warm-up 一次再跑自动化。
|
|
80
80
|
`/plugins/events` 仍是导航时 `ERR_ABORTED`,功能正常。
|
|
81
81
|
|
|
82
|
-
##
|
|
82
|
+
## 升级记录(0.2.1-rc.2,验证机)
|
|
83
83
|
|
|
84
|
-
**升级前**:全局 `@deepseek-ai/dsh@0.1.7-rc.2`(
|
|
85
|
-
运行中的服务靠 `~/.local/bin/dsh` 启动壳固定 Node `v24.21.0`。
|
|
86
|
-
**升级后**:`0.2.1-rc.2`(npm `latest`),启动壳不变(仍固定 Node 24)。
|
|
84
|
+
**升级前**:全局 `@deepseek-ai/dsh@0.1.7-rc.2`;**升级后**:`0.2.1-rc.2`(npm `latest`)。
|
|
87
85
|
|
|
88
86
|
```bash
|
|
89
|
-
#
|
|
90
|
-
~/dsh-
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
# 重启
|
|
94
|
-
|
|
87
|
+
# 建议先备份 profile(仓库外)
|
|
88
|
+
tar -czf ~/dsh-profiles-backup.tgz -C ~/.dsh profiles
|
|
89
|
+
# 升级全局包(Node 需 ^22.19 || >=24)
|
|
90
|
+
npm i -g @deepseek-ai/dsh@0.2.1-rc.2
|
|
91
|
+
# 重启 profile
|
|
92
|
+
dsh --profile deckline --no-open
|
|
95
93
|
```
|
|
96
94
|
|
|
97
95
|
**回归结果**:
|
|
98
96
|
|
|
99
97
|
| 检查项 | 结果 |
|
|
100
98
|
|---|---|
|
|
101
|
-
| `dsh --version
|
|
102
|
-
| `--dump-config
|
|
103
|
-
|
|
|
104
|
-
| 既有数据 | ✅
|
|
105
|
-
| Deckline 应用 | ✅ 首页 / Design 列表 / Deck
|
|
106
|
-
| 出图节点 | ✅
|
|
107
|
-
| 页面异常 | ✅ 0 pageerror
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
本机以 `set -a; . ~/.agents/skills/gpt-image/relays.secret.env; set +a; export WOCHENG_API_KEY="$RELAY_B_WOCHENG_KEY"` 后再启动。
|
|
114
|
-
- 开发用 `web`(3080)进程仍是升级前启动的旧版,下次重启自动用 0.2.1。
|
|
115
|
-
|
|
116
|
-
**回滚**:`~/.nvm/versions/node/v24.21.0/bin/npm i -g --prefix /opt/homebrew @deepseek-ai/dsh@0.1.7-rc.2`
|
|
117
|
-
(或从备份目录恢复包)→ 解压 `profiles.tar.gz` 覆盖 `~/.dsh/profiles` → 重启。
|
|
99
|
+
| `dsh --version` | ✅ `0.2.1-rc.2` |
|
|
100
|
+
| `--dump-config` | ✅ 0 stderr,插件 / 预设 / tools 条目不丢 |
|
|
101
|
+
| 启动 13090 | ✅ 正常打印 token,无迁移报错 |
|
|
102
|
+
| 既有数据 | ✅ 升级后 decks / designs / 资源库 / Conversations / 治理数据都在 |
|
|
103
|
+
| Deckline 应用 | ✅ 首页 / Design 列表 / Deck 工作台(意图 / 脚本 / Design / 日志… + image storyboard) |
|
|
104
|
+
| 出图节点 | ✅ 节点 `/v1/models` 探活 200 |
|
|
105
|
+
| 页面异常 | ✅ 0 pageerror |
|
|
106
|
+
|
|
107
|
+
**运行要求**:LLM 与出图节点的 key 由 profile / 环境变量提供(出图节点见 `apiKeyEnv`)。不带 key 时服务能起,
|
|
108
|
+
但 chat / 出图会失败;fake 模式下不配 key 也能走完整流程。
|
|
109
|
+
|
|
110
|
+
**回滚**:`npm i -g @deepseek-ai/dsh@0.1.7-rc.2` → 解压备份的 `profiles` 覆盖 `~/.dsh/profiles` → 重启。
|
|
118
111
|
|
|
119
112
|
## 已知安装失败:缺 DSH 内置包
|
|
120
113
|
|
package/seeds/docs/install.en.md
CHANGED
|
@@ -15,7 +15,7 @@ assets / conversations on the right. It is not a standalone app — it runs on a
|
|
|
15
15
|
| Node.js | `^22.19` or `≥ 24` | Same as the DSH runtime; Node 23 is not supported |
|
|
16
16
|
| pnpm | 10+ | DSH uses pnpm to manage profile dependencies |
|
|
17
17
|
| DSH | `≥ 0.1.7 < 0.3` | `npm i -g @deepseek-ai/dsh`; verified on 0.1.7-rc.2 / 0.2.1-rc.2, see [compat.md](compat.md) |
|
|
18
|
-
| An LLM provider key | — | e.g. `
|
|
18
|
+
| An LLM provider key | — | e.g. `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` (configured in the profile, see DSH docs) |
|
|
19
19
|
| An image node key | — | For real renders; the fake mode lets you try the flow first (section 4) |
|
|
20
20
|
|
|
21
21
|
```sh
|
|
@@ -105,8 +105,7 @@ Drop a `deck.rules.md` at the root of a deck project to describe the creative co
|
|
|
105
105
|
- It **cannot override** the product's hard rules (read-only source decks / QA gates / no fabrication /
|
|
106
106
|
user confirmation). In a conflict the hard rules win and the agent says so.
|
|
107
107
|
- Max 8 KB; it is never written back and never triggers any automatic action.
|
|
108
|
-
- Do not name it `AGENTS.md`: that belongs to DSH workspace instructions and Deckline does not depend on it
|
|
109
|
-
(see `docs/adr/0020` in the development repo).
|
|
108
|
+
- Do not name it `AGENTS.md`: that belongs to DSH workspace instructions and Deckline does not depend on it.
|
|
110
109
|
|
|
111
110
|
## 2.5 Where the data lives: source deck / documents / images / exports
|
|
112
111
|
|
|
@@ -132,8 +131,8 @@ creating a deck, check this table first.
|
|
|
132
131
|
```sh
|
|
133
132
|
tar czf deckline-backup-$(date +%Y%m%d).tgz "$DSH_HOME/deckline" "<your projectsRoot>"
|
|
134
133
|
```
|
|
135
|
-
- The workbench **Settings** panel shows the current data directory;
|
|
136
|
-
|
|
134
|
+
- The workbench **Settings** panel shows the current data directory; a dev profile may point `dataRoot` at
|
|
135
|
+
a directory inside the source tree (e.g. `.deck/`) explicitly.
|
|
137
136
|
|
|
138
137
|
## 3. Start
|
|
139
138
|
|
package/seeds/docs/install.md
CHANGED
|
@@ -16,7 +16,7 @@ Deckline(`dsh-plugin-deck-studio`)是 DeepSeek Harness(DSH)的 **image d
|
|
|
16
16
|
| Node.js | `^22.19` 或 `≥ 24` | 与 DSH 运行时一致;Node 23 不在支持范围 |
|
|
17
17
|
| pnpm | 10+ | DSH 用 pnpm 管理 profile 依赖 |
|
|
18
18
|
| DSH | `≥ 0.1.7 < 0.3`(0.1.5 不支持) | **用 npm 全局安装的发行版**:`npm i -g @deepseek-ai/dsh@0.2.1-rc.2`;已验证 0.2.1-rc.2,见 [compat.md](compat.md)。从源码自编译的 dsh 若缺内置包(如 `@deepseek-ai/dsh-agent-preset`)会启动失败,见 4.3 |
|
|
19
|
-
| 一个 LLM provider key | — | 例:`
|
|
19
|
+
| 一个 LLM provider key | — | 例:`DEEPSEEK_API_KEY` / `OPENAI_API_KEY`(profile 里配置,见 DSH 文档) |
|
|
20
20
|
| 一个 image 节点 key | — | 生成整页图用;可先用 fake 模式体验(见第 4 节) |
|
|
21
21
|
|
|
22
22
|
```sh
|
|
@@ -27,14 +27,48 @@ node -v && pnpm -v && dsh --version
|
|
|
27
27
|
|
|
28
28
|
### A. 从 npm 安装(推荐,所有人同一条命令)
|
|
29
29
|
|
|
30
|
+
> 在 [npmjs.com](https://www.npmjs.com/) 搜 `deckline` 或 `ppt dsh` 也能找到本包(关键词已含 PPT / PPTX / PowerPoint / 幻灯片)。
|
|
31
|
+
|
|
30
32
|
```sh
|
|
31
33
|
# 1) 建一个 deckline profile(基于 DSH 自带 web 模板)
|
|
32
34
|
dsh --profile deckline --from-default-profile web
|
|
33
35
|
|
|
34
36
|
# 2) 装插件;dsh plugin add 会自动把组合包加入 profile 并默认启用
|
|
35
|
-
dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.
|
|
37
|
+
dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.4
|
|
36
38
|
```
|
|
37
39
|
|
|
40
|
+
> 国内网络直连 `registry.npmjs.org` 可能很慢(20 MB 包实测约 19 KB/s,要十几分钟)。换镜像会快很多:
|
|
41
|
+
> ```sh
|
|
42
|
+
> npm_config_registry=https://registry.npmmirror.com dsh plugin --profile deckline add dsh-plugin-deck-studio@0.2.4
|
|
43
|
+
> ```
|
|
44
|
+
> 或一次性 `npm config set registry https://registry.npmmirror.com`。
|
|
45
|
+
|
|
46
|
+
### A2. 从 DSH 插件市场安装(图形化,等价于 A)
|
|
47
|
+
|
|
48
|
+
DSH 目前没有官方插件市场,市场是「索引源」模式:添加下面这个索引,就能在「插件 → 发现」里搜到 Deckline 并安装。
|
|
49
|
+
|
|
50
|
+
1. 侧栏 **插件** → 「实验性插件」旁 **更多** → 启用 **自定义插件市场**;重新打开插件页,出现 **发现** 标签。
|
|
51
|
+
2. **发现** → **添加索引源**,填:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
https://cdn.jsdelivr.net/gh/yuna78/dsh-deck-studio@main/market-index.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
国内网络用上面这条;能直连 GitHub 也可以用:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
https://raw.githubusercontent.com/yuna78/dsh-deck-studio/main/market-index.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
3. 搜索 `Deckline` 或 `PPT` → **安装**,会安装 `dsh-plugin-deck-studio@0.2.4`(索引里声明了国内镜像 registry)。
|
|
64
|
+
4. 之后同 A 的第 3 步启动。
|
|
65
|
+
|
|
66
|
+
索引文件就是仓库根目录的 [`market-index.json`](../market-index.json)(格式 1),新版本发布后更新版本号即可。
|
|
67
|
+
|
|
68
|
+
> 注意:DSH 的实验性插件市场是可选组件,版本需与当前 DSH 配套。若「发现」页提示市场组件不兼容(或插件被自动禁用),
|
|
69
|
+
> 直接用上面的 A(npm 命令)安装,效果相同。
|
|
70
|
+
|
|
71
|
+
|
|
38
72
|
### B. 从 Git 仓库安装(可选;仓库未公开时需要 GitHub 权限)
|
|
39
73
|
|
|
40
74
|
```sh
|
|
@@ -103,7 +137,7 @@ Deck Agent 每次 `deck_source_overview` 时会读到并遵守:
|
|
|
103
137
|
- 只约束**这一份 deck**,不影响其他 deck;不写就等于没有规则(可选能力)。
|
|
104
138
|
- 它**不能覆盖**产品硬纪律(客户源只读 / QA 门禁 / 不编造 / 需用户确认)——冲突时以硬纪律为准,Agent 会说明。
|
|
105
139
|
- 上限 8 KB;读取时不会回写,也不会因为文件内容自动执行任何操作。
|
|
106
|
-
- 不叫 `AGENTS.md`:`AGENTS.md` 属于 DSH 的工作区指令,Deckline
|
|
140
|
+
- 不叫 `AGENTS.md`:`AGENTS.md` 属于 DSH 的工作区指令,Deckline 不依赖它。
|
|
107
141
|
|
|
108
142
|
## 2.5 数据在哪里:源 deck / 文档 / 图片 / 导出
|
|
109
143
|
|
|
@@ -160,7 +194,7 @@ Deckline 支持:
|
|
|
160
194
|
3. 填:**模式**(fake / real)、**生图节点 endpoint**、**模型**、**key 环境变量名**(或 **key 文件路径**)、**出图尺寸**;
|
|
161
195
|
4. 点 **保存并生效**。
|
|
162
196
|
|
|
163
|
-
保存后写入本机数据目录 `~/.dsh/deckline/render-settings.json
|
|
197
|
+
保存后写入本机数据目录 `~/.dsh/deckline/render-settings.json`(开发调试时也可用 profile 显式配 `dataRoot`,例如仓库内 `.deck/`;
|
|
164
198
|
已 gitignore):节点可直存 API key(0600,界面只显示「已保存」,payload 不回显),也可改用变量名 / key 文件;
|
|
165
199
|
下次启动自动生效,并覆盖 profile 里的 render 配置。
|
|
166
200
|
|
package/ui/workbench.html
CHANGED
|
@@ -1411,14 +1411,30 @@ async function fetchDoc(docId) {
|
|
|
1411
1411
|
if (STATE.docCache[docId]) return STATE.docCache[docId];
|
|
1412
1412
|
if (STATE.docLoading === docId) return null;
|
|
1413
1413
|
STATE.docLoading = docId;
|
|
1414
|
+
const deckId = STATE.data?.active?.deckId ?? "";
|
|
1415
|
+
// F-163:加载失败必须可见。以前只 throw,中间的 spinner 永远不停,用户不知道是 404、500 还是超时。
|
|
1416
|
+
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
1417
|
+
const timer = controller ? setTimeout(() => controller.abort(), 15000) : null;
|
|
1414
1418
|
try {
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1419
|
+
const res = await fetch(
|
|
1420
|
+
`/api/deck-studio.doc?` + new URLSearchParams({ deckId, docId }),
|
|
1421
|
+
controller ? { signal: controller.signal } : undefined,
|
|
1422
|
+
);
|
|
1423
|
+
const text = await res.text();
|
|
1424
|
+
let doc = null;
|
|
1425
|
+
try { doc = JSON.parse(text); } catch { doc = null; }
|
|
1426
|
+
if (!res.ok || !doc || doc.ok === false) {
|
|
1427
|
+
const detail = doc?.error?.message || text.replace(/\s+/g, " ").trim().slice(0, 200) || res.statusText;
|
|
1428
|
+
throw new Error(`${res.status} · ${detail}`);
|
|
1429
|
+
}
|
|
1430
|
+
STATE.docCache[docId] = doc;
|
|
1420
1431
|
return doc;
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
const message = error?.name === "AbortError" ? "请求超时(15 秒没有响应)" : String(error?.message ?? error);
|
|
1434
|
+
console.warn(`[deck-studio] 读取文档失败 docId=${docId} deckId=${deckId}:${message}`);
|
|
1435
|
+
return { ok: false, docId, error: { message } };
|
|
1421
1436
|
} finally {
|
|
1437
|
+
if (timer) clearTimeout(timer);
|
|
1422
1438
|
STATE.docLoading = null;
|
|
1423
1439
|
}
|
|
1424
1440
|
}
|
|
@@ -1430,6 +1446,18 @@ async function renderDocTab(docId) {
|
|
|
1430
1446
|
if (!cached) view.innerHTML = `<div class="observe-body"><div class="doc-loading">读取 ${esc(index?.label ?? docId)} …</div></div>`;
|
|
1431
1447
|
const doc = cached ?? (await fetchDoc(docId));
|
|
1432
1448
|
if (!doc || STATE.tab !== docId) return; // 加载期间用户已切走
|
|
1449
|
+
if (doc.ok === false) {
|
|
1450
|
+
view.innerHTML = `<div class="observe-body">
|
|
1451
|
+
<div class="doc-loading">读取「${esc(index?.label ?? docId)}」失败:${esc(doc.error?.message || "未知错误")}</div>
|
|
1452
|
+
<p style="text-align:center;margin-top:10px"><button type="button" class="btn" data-doc-retry="${esc(docId)}">重试</button></p>
|
|
1453
|
+
<p class="small" style="text-align:center">先在终端看服务端日志;若一直失败,把上面这行错误发我。</p>
|
|
1454
|
+
</div>`;
|
|
1455
|
+
view.querySelectorAll("[data-doc-retry]").forEach((btn) => btn.addEventListener("click", () => {
|
|
1456
|
+
STATE.docCache[docId] = null;
|
|
1457
|
+
renderDocTab(docId);
|
|
1458
|
+
}));
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1433
1461
|
view.innerHTML = `<div class="observe-body">${renderDoc(doc, index)}</div>`;
|
|
1434
1462
|
bindDocActions(view);
|
|
1435
1463
|
}
|
|
@@ -2780,15 +2808,19 @@ async function ensurePromptConversation() {
|
|
|
2780
2808
|
if (!target) return;
|
|
2781
2809
|
const bound = chatSessionsForTarget(target);
|
|
2782
2810
|
if (bound.length) {
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2811
|
+
// 发送操作也要核对 preset,不能只因绑定存在就向标准模式发制作指令。
|
|
2812
|
+
const result = target.kind === "deck"
|
|
2813
|
+
? await ensureDeckConversation(target.id)
|
|
2814
|
+
: await openDesignProject(target.id);
|
|
2815
|
+
if (!result?.ok) throw new Error(result?.reason || "无法打开 Deckline 制作会话");
|
|
2786
2816
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
2787
2817
|
return;
|
|
2788
2818
|
}
|
|
2789
2819
|
const active = STATE.data?.active;
|
|
2790
|
-
|
|
2791
|
-
|
|
2820
|
+
const result = active?.kind === "design-canvas"
|
|
2821
|
+
? await openDesignProject(active.designId)
|
|
2822
|
+
: active?.deckId ? await ensureDeckConversation(active.deckId) : null;
|
|
2823
|
+
if (!result?.ok) throw new Error(result?.reason || "无法打开 Deckline 制作会话");
|
|
2792
2824
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
2793
2825
|
}
|
|
2794
2826
|
|
|
@@ -2809,6 +2841,8 @@ async function sendAgentPrompt() {
|
|
|
2809
2841
|
}
|
|
2810
2842
|
state.textContent = "已发送到左侧对话 ✓";
|
|
2811
2843
|
setTimeout(closeAgentPrompt, 900);
|
|
2844
|
+
} catch (error) {
|
|
2845
|
+
state.textContent = `未能发送:${error?.message || error}`;
|
|
2812
2846
|
} finally {
|
|
2813
2847
|
button.disabled = false;
|
|
2814
2848
|
}
|
|
@@ -3788,7 +3822,14 @@ function requestConversation(payload) {
|
|
|
3788
3822
|
resolve({ ok: Boolean(event.data.ok), sessionId: event.data.sessionId || "", reason: event.data.reason || "" });
|
|
3789
3823
|
};
|
|
3790
3824
|
window.addEventListener("message", onAck);
|
|
3791
|
-
target.postMessage({
|
|
3825
|
+
target.postMessage({
|
|
3826
|
+
type: "dsh-deck-studio:conversation",
|
|
3827
|
+
requestId,
|
|
3828
|
+
cwd: STATE.data?.cwd || "",
|
|
3829
|
+
workspacePath: STATE.data?.workspacePath || "",
|
|
3830
|
+
sourceRoot: STATE.data?.active?.sourceRoot || "",
|
|
3831
|
+
...payload,
|
|
3832
|
+
}, "*");
|
|
3792
3833
|
setTimeout(() => {
|
|
3793
3834
|
if (settled) return;
|
|
3794
3835
|
cleanup();
|
|
@@ -3850,14 +3891,17 @@ async function ensureDeckConversation(deckId) {
|
|
|
3850
3891
|
const chosen = bound[0] || null;
|
|
3851
3892
|
const binding = chosen ? { sessionId: chosen.sessionId } : null;
|
|
3852
3893
|
const result = await requestConversation({
|
|
3853
|
-
action:
|
|
3894
|
+
action: "ensure",
|
|
3854
3895
|
sessionId: chosen?.sessionId || "",
|
|
3855
3896
|
kind: "deck",
|
|
3856
3897
|
id: deckId,
|
|
3857
3898
|
title: label,
|
|
3858
|
-
prompt:
|
|
3899
|
+
prompt: `本对话绑定 deck ${deckId}(${label})。后续所有修改都在这个 deck 上进行;先读 intent 与 script 概要,等我的指令,不要直接出图。`,
|
|
3859
3900
|
});
|
|
3860
|
-
if (!result.ok)
|
|
3901
|
+
if (!result.ok) {
|
|
3902
|
+
getEl("chatBindChip").textContent = `会话打开失败:${result.reason || "未知原因"}`;
|
|
3903
|
+
return result;
|
|
3904
|
+
}
|
|
3861
3905
|
if (!binding || binding.sessionId !== result.sessionId) {
|
|
3862
3906
|
await bindConversation({ sessionId: result.sessionId, kind: "deck", id: deckId, title: label });
|
|
3863
3907
|
}
|
|
@@ -3872,25 +3916,28 @@ async function openDesignProject(designId) {
|
|
|
3872
3916
|
const project = (STATE.data?.designProjects || []).find((item) => item.designId === designId);
|
|
3873
3917
|
if (!project) return;
|
|
3874
3918
|
showDeck(project.canvasDeckId);
|
|
3875
|
-
const
|
|
3919
|
+
const target = chatTargetForBinding({ kind: "design", id: designId });
|
|
3920
|
+
const binding = chatSessionsForTarget(target)[0] || null;
|
|
3876
3921
|
const title = `风格 · ${project.name}`;
|
|
3877
3922
|
const result = await requestConversation({
|
|
3878
|
-
action:
|
|
3923
|
+
action: "ensure",
|
|
3879
3924
|
sessionId: binding?.sessionId || "",
|
|
3880
3925
|
kind: "design",
|
|
3881
3926
|
id: designId,
|
|
3882
3927
|
title,
|
|
3883
|
-
prompt:
|
|
3928
|
+
prompt: buildDesignPrompt(project, project.canvasDeckId),
|
|
3884
3929
|
});
|
|
3885
3930
|
if (result.ok && (!binding || binding.sessionId !== result.sessionId)) {
|
|
3886
3931
|
await bindConversation({ sessionId: result.sessionId, kind: "design", id: designId, title });
|
|
3887
3932
|
} else if (!result.ok) {
|
|
3888
3933
|
STATE.lastConvError = result.reason || "未知原因";
|
|
3934
|
+
getEl("chatBindChip").textContent = `会话打开失败:${STATE.lastConvError}`;
|
|
3889
3935
|
}
|
|
3890
3936
|
if (result.ok) {
|
|
3891
3937
|
STATE.chatSessionId = result.sessionId;
|
|
3892
3938
|
renderChatHeader();
|
|
3893
3939
|
}
|
|
3940
|
+
return result;
|
|
3894
3941
|
}
|
|
3895
3942
|
|
|
3896
3943
|
/** Conversations 页点会话:切过去,并把工作台切到它绑定的 deck / design 画布。 */
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# Productization plan (Deckline · DSH image-deck plugin)
|
|
2
|
-
|
|
3
|
-
简体中文 · [English](productization.en.md)
|
|
4
|
-
|
|
5
|
-
> Goal: turn "a DSH plugin that works on Yuna's machine" into a product that **anyone can install in
|
|
6
|
-
> 5 minutes, configure with their own decks and image key, and start using**.
|
|
7
|
-
>
|
|
8
|
-
> This document is the full plan + current-state inventory + phased acceptance. Execution is tracked in
|
|
9
|
-
> `docs/feedback.md` (F-54…F-57) and `tasks.md` (P27).
|
|
10
|
-
|
|
11
|
-
## 1. Product shape and positioning
|
|
12
|
-
|
|
13
|
-
- **Shape**: a DSH (DeepSeek Harness) **bundle plugin** + a dedicated profile (`deckline`).
|
|
14
|
-
Not a standalone app; DSH owns chat / models / permissions, Deckline owns the "full-page image deck"
|
|
15
|
-
workbench.
|
|
16
|
-
- **One-liner**: turn "who is this for, what is the point, how do slides get made" into a traceable
|
|
17
|
-
pipeline — discuss in chat → script / intent / governance documents → per-page image prompt →
|
|
18
|
-
one-page renders + revisions → export / handoff.
|
|
19
|
-
- **Non-goals**: no HTML deck renderer (separate project); no default batch rendering of a whole deck;
|
|
20
|
-
never modify the user's deck source directory.
|
|
21
|
-
- **Target users**: consultants / trainers / internal communicators / content creators who already use
|
|
22
|
-
DSH or are willing to install it.
|
|
23
|
-
|
|
24
|
-
## 2. Current state (available / gaps)
|
|
25
|
-
|
|
26
|
-
| Capability | Current state | Productization gap |
|
|
27
|
-
|---|---|---|
|
|
28
|
-
| DSH plugin form | ✅ bundle + preset + client panel | Package metadata is clean (MIT / direct name / no personal paths, see §10) |
|
|
29
|
-
| Installation | ✅ three ways: npm direct name / git subdirectory / local clone (`docs/install.md`) | npm not published yet (owner action) |
|
|
30
|
-
| Configuration | ✅ profile patch + `cordis.example.yml` + env conventions (personal paths all moved to the local patch) | Team-shared config templates (Phase 3) |
|
|
31
|
-
| Image key | ✅ `apiKeyEnv` / `apiKeyFile`; UI only shows whether a key is configured | Docs + clearer guidance in the Settings panel |
|
|
32
|
-
| Styles | ✅ add style (paste/upload/drag) + 8 presets | Presets ship with the plugin + one-click import (done) |
|
|
33
|
-
| Data | ✅ `.deck/` fully gitignored, source decks read-only | "where is data / how to back up / how to migrate" docs |
|
|
34
|
-
| Verification | ✅ smoke 46 + harness gate + 16-scenario agent eval + real-machine Playwright checks | CI / public fixtures (Phase 1) |
|
|
35
|
-
| Docs | ✅ README (zh/en) + install / styles / sample-deck docs (zh/en) + compatibility matrix | productization English, contributing guide, screenshots (Phase 2) |
|
|
36
|
-
|
|
37
|
-
## 3. Phased plan (recommended cadence)
|
|
38
|
-
|
|
39
|
-
### Phase 0 · Installable (v0.1, 1–2 days)
|
|
40
|
-
|
|
41
|
-
- [x] Plugin package metadata: `repository` / `homepage` / `keywords` / `peerDependencies`;
|
|
42
|
-
- [x] Default config template: `plugin/deck-studio/cordis.example.yml` (decks / projectsRoot / render nodes);
|
|
43
|
-
- [x] Install docs `docs/install.md` (npm / git / local, key config, troubleshooting);
|
|
44
|
-
- [x] README made public-facing (intro / 5-minute install / config / adding a design / styles);
|
|
45
|
-
- [x] Preset styles ship with the plugin: `seeds/styles/*.json` + `seeds/samples/*.jpg` + "+ Import preset styles";
|
|
46
|
-
- [x] Adding a design supports paste / drag / file picker; existing styles can take more samples;
|
|
47
|
-
- [x] Image key: Settings panel shows nodes + "key configured" + missing-key hints;
|
|
48
|
-
- [x] Moved personal config from the **plugin default patch** into the profile patch (distributable defaults) — migrated and verified via `--dump-config`.
|
|
49
|
-
|
|
50
|
-
**Acceptance**: a clean machine (macOS/Linux, Node `^22.19` or `≥24` + pnpm + DSH) following the README
|
|
51
|
-
can: start → create a deck → create a style (paste an image) → see key status in Settings → run one render
|
|
52
|
-
in fake mode.
|
|
53
|
-
|
|
54
|
-
### Phase 1 · Distributable (v0.1.0, ready; release awaits the owner)
|
|
55
|
-
|
|
56
|
-
- [x] **License: MIT** (owner decision, 2026-10-21) — `LICENSE` in the repo, package metadata aligned;
|
|
57
|
-
- [x] **Direct package name**: `dsh-plugin-deck-studio`, no org / scope; `private: false`;
|
|
58
|
-
- [x] **Public scope** (owner decision): plugin + public docs + preset designs (seeds) + 1 sample deck + 1 sample logo;
|
|
59
|
-
→ `scripts/build-public-bundle.mjs` produces a **whitelisted clean directory** (no internal ledgers / client
|
|
60
|
-
names / personal paths / git history);
|
|
61
|
-
- [x] Sample logo: `assets/logo/deckline-logo.svg|png` + mark; referenced at the top of README;
|
|
62
|
-
- [x] Sample deck: `samples/deckline-intro/` (10-page Deckline product handbook source + paste-ready
|
|
63
|
-
walkthrough in `docs/sample-deck.md`);
|
|
64
|
-
- [x] Privacy scan: plugin code sanitized (all client names / personal paths / key names replaced), the
|
|
65
|
-
bundler has a built-in forbidden-word scan;
|
|
66
|
-
- [x] `CHANGELOG.md` + v0.1.0;
|
|
67
|
-
- [ ] **Publish the repository**: `gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push`
|
|
68
|
-
(after owner confirmation; do not push the development repo — its history contains internal process docs and client information);
|
|
69
|
-
- [ ] `npm publish dsh-plugin-deck-studio` (npm account + 2FA required);
|
|
70
|
-
- [ ] Add 3 real screenshots/GIF at the top of README (before going public);
|
|
71
|
-
- [ ] GitHub Actions:
|
|
72
|
-
- `ci.yml`: `node --check` + smoke (`plugin/deck-studio/verify/smoke.mjs`) + commit message lint;
|
|
73
|
-
- `release.yml`: `npm publish` on tag (via `NPM_TOKEN` secret);
|
|
74
|
-
- [x] Version compatibility: `peerDependencies` / `engines.dsh` declare `0.1.7–0.2.x`; `docs/compat.md`
|
|
75
|
-
records two regression batches (0.1.7-rc.2 / 0.2.1-rc.2).
|
|
76
|
-
|
|
77
|
-
### Phase 2 · Easy onboarding (v0.3, 1 week)
|
|
78
|
-
|
|
79
|
-
- [ ] First-run wizard: when there are no decks, guide through "new deck / import preset styles / configure key";
|
|
80
|
-
- [ ] "New Deck" form can pick a preset style (currently free-text only);
|
|
81
|
-
- [ ] Style library page: preview / search / tags (business, fancy, Japanese…), apply to a new deck in one click;
|
|
82
|
-
- [ ] Data management: visualize the `.deck` location, backup/export button (zip);
|
|
83
|
-
- [ ] i18n: Chinese + English (DSH itself supports multiple languages) — public docs are bilingual; UI strings pending;
|
|
84
|
-
- [ ] Error reporting: actionable render failure reasons (key / quota / model name / size).
|
|
85
|
-
|
|
86
|
-
### Phase 3 · Collaborative (v0.4+)
|
|
87
|
-
|
|
88
|
-
- [ ] Multi-user / team-shared profile config templates (keys never shared);
|
|
89
|
-
- [ ] Style-pack marketplace (community contributes `seeds/styles`, PRs validated for schema + samples);
|
|
90
|
-
- [ ] Render cost estimation / usage stats (per node);
|
|
91
|
-
- [ ] Deck source adapters for more directory conventions (currently scans `outputs/full/final`, `outputs/final`, `pages`, `images`, `exports`).
|
|
92
|
-
|
|
93
|
-
### Phase 4 · Optional (on demand)
|
|
94
|
-
|
|
95
|
-
- Desktop build (DSH desktop already exists), Docker image, SaaS hosting;
|
|
96
|
-
- Object storage / image CDN integration (renders go straight to a CDN);
|
|
97
|
-
- HTML deck renderer (separate project, not in this repository).
|
|
98
|
-
|
|
99
|
-
## 4. How the Git repo goes public (concrete steps)
|
|
100
|
-
|
|
101
|
-
> Owner decision: **public scope = plugin + public docs + preset designs + 1 sample deck + 1 sample logo**.
|
|
102
|
-
> The current development repo's git history contains internal ledgers (feedback / tasks / client names /
|
|
103
|
-
> personal paths) and **must not be pushed directly**. Generate a clean whitelisted directory, then create
|
|
104
|
-
> a new public repository from it.
|
|
105
|
-
|
|
106
|
-
```sh
|
|
107
|
-
# 1) Generate the clean directory (whitelist + privacy scan; output in dist/public, gitignored)
|
|
108
|
-
node scripts/build-public-bundle.mjs
|
|
109
|
-
# ✔ whitelist + privacy scan pass (see script output), no /Users/ paths, no client names, no keys
|
|
110
|
-
|
|
111
|
-
# 2) Create the public repo (after owner confirmation; account yuna78)
|
|
112
|
-
gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push
|
|
113
|
-
|
|
114
|
-
# 3) Fill in repo metadata: About = "DSH image deck workbench plugin",
|
|
115
|
-
# Topics = deepseek-harness, dsh-plugin, image-deck, presentation, deck
|
|
116
|
-
# 4) Tag / release
|
|
117
|
-
cd dist/public && git tag v0.1.0 && git push origin v0.1.0
|
|
118
|
-
gh release create v0.1.0 --notes "First public release: install via dsh plugin add dsh-plugin-deck-studio"
|
|
119
|
-
|
|
120
|
-
# 5) npm publish (Phase 1)
|
|
121
|
-
cd plugin/deck-studio
|
|
122
|
-
npm publish --access public # npm account + 2FA required
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Public bundle contents (whitelist)**:
|
|
126
|
-
|
|
127
|
-
- `README.md` / `LICENSE` / `CHANGELOG.md`;
|
|
128
|
-
- `docs/`: `productization.md` + `productization.en.md`, `install.md` + `install.en.md`, `styles.md` + `styles.en.md`,
|
|
129
|
-
`sample-deck.md` + `sample-deck.en.md`, `compat.md`;
|
|
130
|
-
- `plugin/deck-studio/`: the plugin itself (host + client + ui + lib + seeds preset styles/samples + verify smoke);
|
|
131
|
-
- `samples/deckline-intro/`: sample deck source (README / intent / script);
|
|
132
|
-
- `assets/logo/`: sample logo (svg + png).
|
|
133
|
-
|
|
134
|
-
**Not public**: `CURRENT.md` / `HANDOVER.md` / `tasks.md` / `docs/feedback.md` / `docs/architecture.md` /
|
|
135
|
-
`docs/adr/` / `docs/research/` / `fixtures/` (contain local references to client material) / `.deck/` (runtime
|
|
136
|
-
data) / git history.
|
|
137
|
-
|
|
138
|
-
**Cleanup already done**:
|
|
139
|
-
|
|
140
|
-
1. Personal paths in `cordis.patch.yml` → profile patch; the repo carries a generic bundle patch + `cordis.example.yml`;
|
|
141
|
-
2. Plugin `package.json`: `private: false`, MIT, direct package name, repository / homepage / peerDependencies;
|
|
142
|
-
3. Plugin code sanitized: client names, personal paths and env var names in `client.js` / `lib/config.js` /
|
|
143
|
-
`lib/deck-workspace.js` / `lib/render-nodes.js` / `lib/source-adapter.js` / `lib/tools.js` / `verify/smoke.mjs`
|
|
144
|
-
replaced with generic examples;
|
|
145
|
-
4. The bundler has a built-in forbidden-word scan (client names / `/Users/` / `sk-` / private key names); a hit
|
|
146
|
-
aborts the build.
|
|
147
|
-
|
|
148
|
-
## 5. Install and distribution matrix
|
|
149
|
-
|
|
150
|
-
| Method | Command | For |
|
|
151
|
-
|---|---|---|
|
|
152
|
-
| npm | `dsh plugin --profile deckline add dsh-plugin-deck-studio` | Regular users (after publish) |
|
|
153
|
-
| Git | `dsh plugin --profile deckline add "github:yuna78/dsh-deck-studio#path:/plugin/deck-studio"` | People tracking main |
|
|
154
|
-
| Local clone | `dsh plugin --profile deckline add "$PWD/plugin/deck-studio"` | Developers / customization |
|
|
155
|
-
|
|
156
|
-
All three land in `~/.dsh/profiles/deckline/`; after installing, users only edit the **profile patch**
|
|
157
|
-
(decks / projectsRoot / render nodes) and never the plugin package — that boundary is what makes
|
|
158
|
-
"anyone can install it" true.
|
|
159
|
-
|
|
160
|
-
## 6. Configuration conventions (image key)
|
|
161
|
-
|
|
162
|
-
- Configuration exists in exactly two places: the **profile patch** (structure) and **env vars / key files** (credentials);
|
|
163
|
-
- The structure only holds `apiKeyEnv` (variable name) or `apiKeyFile` (local path), **never a plaintext key**;
|
|
164
|
-
- The workbench Settings panel only shows the `keyConfigured` boolean + source name;
|
|
165
|
-
- Multiple nodes supported (primary / backup / different models), each with its own key;
|
|
166
|
-
- Missing key: fake mode still runs the full flow; real mode reports where to configure it.
|
|
167
|
-
|
|
168
|
-
## 7. Style ecosystem (the productization differentiator)
|
|
169
|
-
|
|
170
|
-
- **Preset bundle ships with the plugin**: `plugin/deck-studio/seeds/styles/<ABBR>.json` (definitions with
|
|
171
|
-
`styleBrief` + `baselineBriefs`) + `seeds/samples/<ABBR>-0{1,2,3}.jpg` (3 baseline samples);
|
|
172
|
-
- **One-click import**: Designs page "+ Import preset styles", existing abbreviations are skipped (idempotent);
|
|
173
|
-
- **Add your own**: paste/upload/drag in the UI; or drop images into `designs/<ABBR>/`;
|
|
174
|
-
- **Contribute**: a PR adds one JSON + 3 baseline samples (schema: abbr / name / description / constraints /
|
|
175
|
-
reference / styleBrief / baselineBriefs);
|
|
176
|
-
- Known limitation: samples illustrate a style, not guarantee verbatim reproduction of their text (a general
|
|
177
|
-
image-model limitation); for exact text, overlay HTML/Mermaid text afterwards.
|
|
178
|
-
|
|
179
|
-
## 8. Risks and boundaries
|
|
180
|
-
|
|
181
|
-
| Risk | Description | Mitigation |
|
|
182
|
-
|---|---|---|
|
|
183
|
-
| DSH plugin API changes | The plugin depends on the DSH version | Declared peer range + CI smoke + compatibility docs |
|
|
184
|
-
| Credential leakage | Users paste a key into the patch | Docs stress apiKeyEnv / apiKeyFile; Settings never echoes keys |
|
|
185
|
-
| Runaway render cost | Users batch-render everything | Named batches / whole-deck renders are allowed (per-page revisions, single-page retries); no one-click button, to avoid accidents |
|
|
186
|
-
| Client data | Deck sources may be sensitive | Sources read-only, output local under `.deck`, repo carries no data; export packages sanitized |
|
|
187
|
-
| Model quality | Garbled text, broken layouts | Style prompt constraints + "little text, English" advice + post-edit overlays |
|
|
188
|
-
|
|
189
|
-
## 9. Milestones (suggested)
|
|
190
|
-
|
|
191
|
-
- **M1 (this week)**: Phase 0 complete + 8 preset styles imported and usable locally;
|
|
192
|
-
- **M2 (next week)**: repository public + v0.1.0 release + 2 external users install successfully;
|
|
193
|
-
- **M3 (within two weeks)**: npm publish + CI + first-run wizard;
|
|
194
|
-
- **M4 (one month)**: style library page + bilingual UI + community style packs.
|
|
195
|
-
|
|
196
|
-
## 10. Decisions made (2026-10-21)
|
|
197
|
-
|
|
198
|
-
| # | Decision | Outcome | Implementation |
|
|
199
|
-
|---|---|---|---|
|
|
200
|
-
| 1 | License | **MIT** | `LICENSE` + package.json `license: MIT` |
|
|
201
|
-
| 2 | Package name | **Direct, no org**: `dsh-plugin-deck-studio` | package.json `name` / `private: false` |
|
|
202
|
-
| 3 | Public scope | Plugin + docs + preset designs + **1 sample deck** + **1 sample logo** | `scripts/build-public-bundle.mjs` whitelist; no internal ledgers or history |
|
|
203
|
-
| 4 | Brand | **Deckline** confirmed (repo name stays `dsh-deck-studio`) | README / UI / logo |
|
|
204
|
-
| 5 | Public account | GitHub `yuna78`; repo `yuna78/dsh-deck-studio` (public) | `gh repo create` + push after owner confirmation |
|
|
205
|
-
|
|
206
|
-
**Two actions still waiting on the owner**:
|
|
207
|
-
|
|
208
|
-
1. Confirm going public: run `node scripts/build-public-bundle.mjs`, then
|
|
209
|
-
`gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push`;
|
|
210
|
-
2. npm publish: `cd plugin/deck-studio && npm publish --access public` (needs npm login + 2FA).
|
|
211
|
-
|
|
212
|
-
CI / release / screenshots come in the iterations after this round.
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# 产品化计划(Deckline · DSH image deck 插件)
|
|
2
|
-
|
|
3
|
-
简体中文 · [English](productization.en.md)
|
|
4
|
-
|
|
5
|
-
> 目标:把现在这个「在 Yuna 机器上跑通的 DSH 插件」变成**任何人在自己机器上 5 分钟内装好、
|
|
6
|
-
> 配好自己的 deck 和生图 key、开始用**的产品。
|
|
7
|
-
>
|
|
8
|
-
> 本文是完整计划 + 现状盘点 + 分阶段验收。执行登记见 `docs/feedback.md` F-54…F-57、`tasks.md` P27。
|
|
9
|
-
|
|
10
|
-
## 1. 产品形态与定位
|
|
11
|
-
|
|
12
|
-
- **形态**:DSH(DeepSeek Harness)的 **bundle 插件** + 一个专用 profile(`deckline`)。
|
|
13
|
-
不是独立 App;DSH 负责 chat / 模型 / 权限,Deckline 负责「整页图片 deck」的工作台。
|
|
14
|
-
- **一句话**:把「讲给谁、讲什么、怎么出图」变成一条可追踪的产线——
|
|
15
|
-
Chat 里讨论 → script / intent / 治理文档 → 逐页 image prompt → 单页出图 + 版本 → 导出 / 交接。
|
|
16
|
-
- **不做什么**:不做 HTML deck 渲染器(另立项目);不默认批量出整套图;不改用户 deck 源目录。
|
|
17
|
-
- **目标用户**:咨询 / 培训 / 内部汇报 / 内容创作者,已经有 DSH 或愿意装 DSH 的人。
|
|
18
|
-
|
|
19
|
-
## 2. 现状盘点(已具备 / 缺口)
|
|
20
|
-
|
|
21
|
-
| 能力 | 现状 | 产品化缺口 |
|
|
22
|
-
|---|---|---|
|
|
23
|
-
| DSH 插件形态 | ✅ bundle + preset + 客户端面板 | 包元数据已整理(MIT / 直发名 / 无个人路径,见 §10) |
|
|
24
|
-
| 安装 | ✅ npm 直发包名 / git 子目录 / 本地 clone 三种方式(`docs/install.md`) | npm 尚未发布(负责人执行) |
|
|
25
|
-
| 配置 | ✅ profile patch + `cordis.example.yml` + env 口径(个人路径已全部下沉到本机 patch) | 团队共享配置模板(Phase 3) |
|
|
26
|
-
| 生图 key | ✅ `apiKeyEnv` / `apiKeyFile`,界面只显示是否配置 | 缺文档 + 设置面板更明确的引导 |
|
|
27
|
-
| 风格 | ✅ 添加风格(贴图/上传/拖拽/粘贴)+ 8 个预设 | 预设需要随插件分发 + 一键导入(已做) |
|
|
28
|
-
| 数据 | ✅ `.deck/` 全部 gitignore,源 deck 只读 | 缺「数据在哪 / 怎么备份 / 怎么迁移」说明 |
|
|
29
|
-
| 验证 | ✅ smoke 46 项 + harness 门禁 + Agent 评估 16 场景 + 真机 Playwright 验证 | CI / 公开 fixture(Phase 1) |
|
|
30
|
-
| 文档 | ✅ README(中/英)+ 安装 / 风格目录 / 示例 deck 文档(中/英)+ 兼容矩阵 | productization 英译、贡献指南、截图(Phase 2) |
|
|
31
|
-
|
|
32
|
-
## 3. 分阶段计划(建议按此节奏)
|
|
33
|
-
|
|
34
|
-
### Phase 0 · 可安装(v0.1,1–2 天)
|
|
35
|
-
|
|
36
|
-
- [x] 插件包元数据:加 `repository` / `homepage` / `keywords` / `peerDependencies`(`private: true` + `UNLICENSED` 暂留防误发,license 拍板后改);
|
|
37
|
-
- [x] 默认配置模板:`plugin/deck-studio/cordis.example.yml`(decks / projectsRoot / render 节点);
|
|
38
|
-
- [x] 安装文档 `docs/install.md`(npm / git / 本地 三种方式 + key 配置 + 排障);
|
|
39
|
-
- [x] README 对外化(产品介绍 / 5 分钟安装 / 配置 / 加 design / 风格);
|
|
40
|
-
- [x] 预设风格随插件分发:`seeds/styles/*.json` + `seeds/samples/*.jpg` + 「+ 导入预设风格」;
|
|
41
|
-
- [x] 添加 design 支持贴图 / 拖拽 / 选文件;已有风格可继续加样张;
|
|
42
|
-
- [x] 生图 key:工作台设置面板显示节点 + 「key 是否配置」+ 缺 key 提示;
|
|
43
|
-
- [x] 把当前机器上的个人配置从**插件默认 patch** 挪到 profile patch(保证默认包可分发)——已迁移并用 `--dump-config` 验证。
|
|
44
|
-
|
|
45
|
-
**验收**:一台干净机器(macOS/Linux,Node `^22.19` 或 `≥24` + pnpm + DSH)按 README 走完能:启动 → 建 deck →
|
|
46
|
-
建风格(贴图)→ 设置里看到 key 状态 →(fake 模式)走完一次出图。
|
|
47
|
-
|
|
48
|
-
### Phase 1 · 可分发(v0.1.0,已就绪,待负责人执行发布)
|
|
49
|
-
|
|
50
|
-
- [x] **许可证:MIT**(负责人 2026-10-21 拍板)——`LICENSE` 已落仓库,包元数据同步;
|
|
51
|
-
- [x] **包名直发**:`dsh-plugin-deck-studio`,无 org / scope;`private: false`;
|
|
52
|
-
- [x] **公开范围**(负责人拍板):插件 + 对外 docs + 预设 design(seeds)+ 1 个 sample deck + 1 个 sample logo;
|
|
53
|
-
→ 用 `scripts/build-public-bundle.mjs` 生成**白名单干净目录**(不含内部台账 / 客户名 / 个人路径 / git 历史);
|
|
54
|
-
- [x] sample logo:`assets/logo/deckline-logo.svg|png` + mark;README 顶部已引用;
|
|
55
|
-
- [x] sample deck:`samples/deckline-intro/`(Deckline 产品说明手册 10 页源 + 可粘贴跑通流程文案 `docs/sample-deck.md`);
|
|
56
|
-
- [x] 隐私扫描:插件代码已脱敏(客户名 / 个人路径 / key 名全部换掉),打包脚本内置禁用词扫描;
|
|
57
|
-
- [x] `CHANGELOG.md` + v0.1.0;
|
|
58
|
-
- [ ] **仓库公开**:`gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push`
|
|
59
|
-
(需负责人确认后执行;不要推当前开发仓库——它的 git 历史含内部过程文档与客户信息);
|
|
60
|
-
- [ ] `npm publish dsh-plugin-deck-studio`(需 npm 账号 + 2FA);
|
|
61
|
-
- [ ] README 顶部补 3 张真机截图/GIF(公开前);
|
|
62
|
-
- [ ] GitHub Actions:
|
|
63
|
-
- `ci.yml`:`node --check` + smoke(`plugin/deck-studio/verify/smoke.mjs`)+ 提交信息规范;
|
|
64
|
-
- `release.yml`:tag 时 `npm publish`(用 `NPM_TOKEN` secret);
|
|
65
|
-
- [x] 版本兼容:`peerDependencies` / `engines.dsh` 声明 `0.1.7–0.2.x`;`docs/compat.md` 记录 0.1.7-rc.2 / 0.2.1-rc.2 两批回归。
|
|
66
|
-
|
|
67
|
-
### Phase 2 · 好上手(v0.3,1 周)
|
|
68
|
-
|
|
69
|
-
- [ ] 首次启动向导:无 deck 时引导「新建 deck / 导入预设风格 / 配 key」三步;
|
|
70
|
-
- [ ] 「新建 Deck」表单支持从预设风格里选(现在只能手填风格);
|
|
71
|
-
- [ ] 风格库页面:预览 / 搜索 / 标签(商务、fancy、日系…),一键套用到新 deck;
|
|
72
|
-
- [ ] 数据管理:`.deck` 位置可视化、备份/导出按钮(zip);
|
|
73
|
-
- [ ] i18n:中文 + English(DSH 本身支持多语言)——公开文档已中英双语,UI 文案待做;
|
|
74
|
-
- [ ] 错误上报:出图失败给出可操作原因(key / 配额 / 模型名 / 尺寸)。
|
|
75
|
-
|
|
76
|
-
### Phase 3 · 可协作(v0.4+)
|
|
77
|
-
|
|
78
|
-
- [ ] 多用户 / 团队共享 profile 配置模板(不共享 key);
|
|
79
|
-
- [ ] 风格包 marketplace(社区贡献 `seeds/styles`,PR 校验 schema + 样张);
|
|
80
|
-
- [ ] 出图成本估算 / 用量统计(按节点);
|
|
81
|
-
- [ ] Deck 源适配更多目录约定(现在扫描 `outputs/full/final`、`outputs/final`、`pages`、`images`、`exports`)。
|
|
82
|
-
|
|
83
|
-
### Phase 4 · 可选(视需求)
|
|
84
|
-
|
|
85
|
-
- 桌面版(DSH desktop 已有)、Docker 镜像、SaaS 托管;
|
|
86
|
-
- 与对象存储 / 图床集成(出图直接进 CDN);
|
|
87
|
-
- HTML deck 渲染器(独立项目,不在本仓库)。
|
|
88
|
-
|
|
89
|
-
## 4. Git 如何公开(具体步骤)
|
|
90
|
-
|
|
91
|
-
> 负责人已拍板:**公开范围 = 插件 + 对外 docs + 预设 design + 1 个 sample deck + 1 个 sample logo**。
|
|
92
|
-
> 当前开发仓库的 git 历史含内部台账(feedback / tasks / 客户名 / 个人路径),**不能直接 push**。
|
|
93
|
-
> 用白名单脚本生成干净目录,再创建一个新的公开仓库。
|
|
94
|
-
|
|
95
|
-
```sh
|
|
96
|
-
# 1) 生成干净目录(白名单 + 隐私扫描;产物在 dist/public,已 gitignore)
|
|
97
|
-
node scripts/build-public-bundle.mjs
|
|
98
|
-
# ✔ 白名单 + 隐私扫描通过(以脚本输出为准),无 /Users/ 个人路径、无客户名、无 key
|
|
99
|
-
|
|
100
|
-
# 2) 建公开仓库(负责人确认后执行;账号 yuna78)
|
|
101
|
-
gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push
|
|
102
|
-
|
|
103
|
-
# 3) 补仓库元信息:About = "DSH image deck workbench plugin",
|
|
104
|
-
# Topics = deepseek-harness, dsh-plugin, image-deck, presentation, deck
|
|
105
|
-
# 4) 打 tag / 发 Release
|
|
106
|
-
cd dist/public && git tag v0.1.0 && git push origin v0.1.0
|
|
107
|
-
gh release create v0.1.0 --notes "First public release: install via dsh plugin add dsh-plugin-deck-studio"
|
|
108
|
-
|
|
109
|
-
# 5) npm 发布(Phase 1)
|
|
110
|
-
cd plugin/deck-studio
|
|
111
|
-
npm publish --access public # 需要 npm 账号 + 2FA
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**公开包内容(白名单)**:
|
|
115
|
-
|
|
116
|
-
- `README.md` / `LICENSE` / `CHANGELOG.md`;
|
|
117
|
-
- `docs/`:`productization.md`、`install.md` + `install.en.md`、`styles.md` + `styles.en.md`、`sample-deck.md` + `sample-deck.en.md`、`compat.md`;
|
|
118
|
-
- `plugin/deck-studio/`:插件本体(宿主 + 客户端 + ui + lib + seeds 预设风格/样张 + verify smoke);
|
|
119
|
-
- `samples/deckline-intro/`:示例 deck 源(README / intent / script);
|
|
120
|
-
- `assets/logo/`:sample logo(svg + png)。
|
|
121
|
-
|
|
122
|
-
**不公开**:`CURRENT.md` / `HANDOVER.md` / `tasks.md` / `docs/feedback.md` / `docs/architecture.md` /
|
|
123
|
-
`docs/adr/` / `docs/research/` / `fixtures/`(含客户材料的本地源引用)/ `.deck/`(运行数据)/ git 历史。
|
|
124
|
-
|
|
125
|
-
**已完成的清理**:
|
|
126
|
-
|
|
127
|
-
1. `cordis.patch.yml` 个人路径 → profile patch;仓库里是通用 bundle patch + `cordis.example.yml`;
|
|
128
|
-
2. 插件 `package.json`:`private: false`、MIT、直发包名、repository / homepage / peerDependencies;
|
|
129
|
-
3. 插件代码脱敏:`client.js` / `lib/config.js` / `lib/deck-workspace.js` / `lib/render-nodes.js` /
|
|
130
|
-
`lib/source-adapter.js` / `lib/tools.js` / `verify/smoke.mjs` 里的客户名、个人路径、环境变量名全部替换为通用示例;
|
|
131
|
-
4. 白名单脚本内置禁用词扫描(客户名 / `/Users/` / `sk-` / 私有 key 名),命中即中止打包。
|
|
132
|
-
|
|
133
|
-
## 5. 安装与分发矩阵
|
|
134
|
-
|
|
135
|
-
| 方式 | 命令 | 适合 |
|
|
136
|
-
|---|---|---|
|
|
137
|
-
| npm | `dsh plugin --profile deckline add dsh-plugin-deck-studio` | 普通用户(发布后) |
|
|
138
|
-
| Git | `dsh plugin --profile deckline add "github:yuna78/dsh-deck-studio#path:/plugin/deck-studio"` | 想跟 main 的人 |
|
|
139
|
-
| 本地 clone | `dsh plugin --profile deckline add "$PWD/plugin/deck-studio"` | 开发者 / 定制 |
|
|
140
|
-
|
|
141
|
-
三种方式都落在 `~/.dsh/profiles/deckline/`;安装后用户只需要改 **profile patch**(decks / projectsRoot / render 节点),
|
|
142
|
-
不需要动插件包本身——这是「任何人可安装」的关键边界。
|
|
143
|
-
|
|
144
|
-
## 6. 配置口径(生图 key)
|
|
145
|
-
|
|
146
|
-
- 配置只存在于两处:**profile patch**(结构)与 **环境变量 / key 文件**(凭证);
|
|
147
|
-
- 结构里只写 `apiKeyEnv`(变量名)或 `apiKeyFile`(本机路径),**永远不写 key 明文**;
|
|
148
|
-
- 工作台设置面板只显示 `keyConfigured` 布尔 + 来源名;
|
|
149
|
-
- 支持多节点(主 / 备用 / 不同模型),每节点独立 key;
|
|
150
|
-
- 缺 key 时:fake 模式仍可走通流程;real 模式报错信息直接给出配置位置。
|
|
151
|
-
|
|
152
|
-
## 7. 风格生态(本产品化差异点)
|
|
153
|
-
|
|
154
|
-
- **预设包随插件分发**:`plugin/deck-studio/seeds/styles/<ABBR>.json`(定义,含 `styleBrief` + `baselineBriefs`)
|
|
155
|
-
+ `seeds/samples/<ABBR>-0{1,2,3}.jpg`(3 张基线样张);
|
|
156
|
-
- **一键导入**:Designs 页「+ 导入预设风格」,已存在的缩写自动跳过(幂等);
|
|
157
|
-
- **自己加**:界面里贴图/上传/拖拽;或把图片放到 `designs/<ABBR>/`;
|
|
158
|
-
- **贡献**:PR 新增一个 JSON + 3 张基线样张即可(schema:abbr / name / description / constraints / reference / styleBrief / baselineBriefs);
|
|
159
|
-
- 已知限制:样张是风格示意,不保证能逐字复刻其中的文字(图像模型通病);要精确文字用 HTML/Mermaid 叠字。
|
|
160
|
-
|
|
161
|
-
## 8. 风险与边界
|
|
162
|
-
|
|
163
|
-
| 风险 | 说明 | 对策 |
|
|
164
|
-
|---|---|---|
|
|
165
|
-
| DSH 插件 API 变动 | 插件依赖 DSH 版本 | 声明 peer 范围 + CI 跑 smoke + 版本豁免文档 |
|
|
166
|
-
| 凭证泄漏 | 用户误把 key 写进 patch | 文档强调 apiKeyEnv / apiKeyFile;设置面板不回显 |
|
|
167
|
-
| 出图成本失控 | 用户批量出图 | 允许点名批量 / 整套(逐页留痕、失败单页重出);不做一键按钮,避免误触 |
|
|
168
|
-
| 客户数据 | deck 源可能含敏感内容 | 源只读、产物本地 `.deck`、仓库不含数据;导出交接包脱敏 |
|
|
169
|
-
| 模型质量 | 文字糊、版式跑 | 风格 prompt 约束 + 「文字少而英文」建议 + 后期叠字 |
|
|
170
|
-
|
|
171
|
-
## 9. 里程碑(建议时间)
|
|
172
|
-
|
|
173
|
-
- **M1(本周)**:Phase 0 完成 + 本机导入 8 个预设风格可用;
|
|
174
|
-
- **M2(下周)**:仓库公开 + v0.1.0 Release + 2 位外部用户装通;
|
|
175
|
-
- **M3(两周内)**:npm 发布 + CI + 首次启动向导;
|
|
176
|
-
- **M4(一个月)**:风格库页面 + UI 双语 + 社区风格包。
|
|
177
|
-
|
|
178
|
-
## 10. 已拍板(2026-10-21)
|
|
179
|
-
|
|
180
|
-
| # | 决策 | 结论 | 落地 |
|
|
181
|
-
|---|---|---|---|
|
|
182
|
-
| 1 | License | **MIT** | `LICENSE` + package.json `license: MIT` |
|
|
183
|
-
| 2 | 包名 | **直发,无 org**:`dsh-plugin-deck-studio` | package.json `name` / `private: false` |
|
|
184
|
-
| 3 | 公开范围 | 插件 + docs + 预设 design + **1 个 sample deck** + **1 个 sample logo** | `scripts/build-public-bundle.mjs` 白名单;不含内部台账与历史 |
|
|
185
|
-
| 4 | 品牌 | **Deckline** 最终确认(仓库名仍 `dsh-deck-studio`) | README / UI / logo |
|
|
186
|
-
| 5 | 公开账号 | GitHub `yuna78`;仓库 `yuna78/dsh-deck-studio`(public) | 待负责人确认后 `gh repo create` + push |
|
|
187
|
-
|
|
188
|
-
**还等负责人执行的 2 个动作**:
|
|
189
|
-
|
|
190
|
-
1. 确认公开:跑 `node scripts/build-public-bundle.mjs` 后 `gh repo create yuna78/dsh-deck-studio --public --source dist/public --remote origin --push`;
|
|
191
|
-
2. npm 发布:`cd plugin/deck-studio && npm publish --access public`(需登录 npm + 2FA)。
|
|
192
|
-
|
|
193
|
-
之后 CI / Release / 截图由本轮之后的迭代补。
|