@xia-sc/dsh-cc-studio 0.3.2 → 0.3.4
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 +14 -12
- package/lib/agent.js +614 -614
- package/lib/client.js +1 -3
- package/package.json +1 -2
- package/presets/cc/agent.cordis.yml +20 -0
- package/presets/cc/preset.yml +2 -2
- package/prototypes/index.html +0 -11
- package/prototypes/prototype-a.html +0 -246
- package/prototypes/prototype-b.html +0 -175
package/lib/client.js
CHANGED
|
@@ -59,7 +59,6 @@ window.__ModuleLoader__.load({
|
|
|
59
59
|
"settings.status.suffix":"个",
|
|
60
60
|
"settings.status.path":"(落盘 ~/.dsh/cc-library)",
|
|
61
61
|
"settings.status.hint":"CC 模式:切换到 CC 预设后,输入框上方出现胶囊,点开即是融合工坊(五维/角色/世界书/导出 + 已存侧栏 + 实时预览)。",
|
|
62
|
-
"settings.status.prototype":"原型对比:",
|
|
63
62
|
"settings.status.spec":"规范:",
|
|
64
63
|
"settings.status.impExp":"导入/导出:JSON / PNG(tEXt ccv3) / CHARX(ZIP card.json),侧边栏 ★ 保存/载入/导出/删除。",
|
|
65
64
|
"settings.status.refresh":"刷新已存列表",
|
|
@@ -265,7 +264,6 @@ window.__ModuleLoader__.load({
|
|
|
265
264
|
"settings.status.suffix":"",
|
|
266
265
|
"settings.status.path":" (stored in ~/.dsh/cc-library)",
|
|
267
266
|
"settings.status.hint":"CC Mode: switch to CC preset, capsule appears above composer; open to get fusion workshop (5D/Character/Lorebook/Export + sidebar + preview).",
|
|
268
|
-
"settings.status.prototype":"Prototypes: ",
|
|
269
267
|
"settings.status.spec":"Spec: ",
|
|
270
268
|
"settings.status.impExp":"Import/Export: JSON / PNG(tEXt ccv3) / CHARX(ZIP card.json), sidebar ★ Save/Load/Export/Delete.",
|
|
271
269
|
"settings.status.refresh":"Refresh saved list",
|
|
@@ -1402,7 +1400,7 @@ window.__ModuleLoader__.load({
|
|
|
1402
1400
|
h("h3",{style:{margin:"0 0 8px", fontSize:13}}, t("settings.status.title")),
|
|
1403
1401
|
h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-primary)"}}, t("settings.status.count"), libCount===null ? "—" : String(libCount)+t("settings.status.suffix"), libCount!==null ? h("span",{style:{color:"var(--dsw-alias-label-secondary)", marginLeft:8}}, t("settings.status.path")) : null),
|
|
1404
1402
|
h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-secondary)", marginTop:8}}, t("settings.status.hint")),
|
|
1405
|
-
h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-secondary)", marginTop:6}}, t("settings.status.
|
|
1403
|
+
h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-secondary)", marginTop:6}}, t("settings.status.spec"), h("a",{href:"https://github.com/kwaroran/character-card-spec-v3/blob/main/SPEC_V3.md", target:"_blank", style:{color:"#7c5cff"}}, "CCv3 SPEC")),
|
|
1406
1404
|
h("div",{style:{fontSize:12, color:"var(--dsw-alias-label-secondary)", marginTop:6}}, t("settings.status.impExp")),
|
|
1407
1405
|
store && store.refreshLibrary ? h("button",{style:Object.assign({}, S.btn, {marginTop:10, padding:"6px 10px", fontSize:12}), onClick:function(){ store.refreshLibrary(); }}, t("settings.status.refresh")) : null
|
|
1408
1406
|
),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xia-sc/dsh-cc-studio",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "CCv3 角色卡工坊:融合工坊 + CC 模式 + JSON/PNG/CHARX + 中英双语(跟随全局)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"files": [
|
|
33
33
|
"lib",
|
|
34
34
|
"cordis.patch.yml",
|
|
35
|
-
"prototypes",
|
|
36
35
|
"presets"
|
|
37
36
|
],
|
|
38
37
|
"scripts": {
|
|
@@ -197,11 +197,16 @@
|
|
|
197
197
|
- id: tool-subagent-list-agents
|
|
198
198
|
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
199
199
|
|
|
200
|
+
# 0.3.4 对齐 dsh 0.1.6-alpha.2 内置 standard:spawn 行此前漏抄 modelSelectionSettings。
|
|
201
|
+
# 该键在 dsh-tool-subagent 里是 `z.boolean().default(false)`,漏掉既不报错也不改 schema,
|
|
202
|
+
# 只是子代理的「指定模型」入口被静默关掉 —— 最难发现的那类漂移。
|
|
203
|
+
# fork 行故意不加:provider/model 必须等于父代理,继承的历史才能继续命中 KV cache。
|
|
200
204
|
- id: tool-subagent
|
|
201
205
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
202
206
|
config:
|
|
203
207
|
provider: spawn
|
|
204
208
|
toolName: subagent
|
|
209
|
+
modelSelectionSettings: true
|
|
205
210
|
backgroundMode: continuable
|
|
206
211
|
|
|
207
212
|
- id: tool-subagent-fork
|
|
@@ -271,6 +276,21 @@
|
|
|
271
276
|
fetch: false
|
|
272
277
|
searchTimeoutMs: 60000
|
|
273
278
|
|
|
279
|
+
# 0.3.4 补回:`present` 是 standard 一直有的行,本预设此前整行漏抄。
|
|
280
|
+
# 症状很隐蔽 —— CC 模式下模型没有「把成品登记为交付物」的工具,不报任何错,只是少了能力。
|
|
281
|
+
# 它和下面那行都必须与 dsh 内置 standard 逐行对得上(升级清单见 AGENTS.md §4)。
|
|
282
|
+
- id: present
|
|
283
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
284
|
+
|
|
285
|
+
# dsh 0.1.6-alpha.2 新增行:standard 里就是 disabled(默认关,需要的人自己放开)。
|
|
286
|
+
# 抄过来是为了让「本预设 vs 内置 standard」的行差以后只剩有意为之的那几处,
|
|
287
|
+
# 下次升级 diff 时不必再逐个判断「这行是新加的、还是我们漏抄了」。
|
|
288
|
+
# 保留 disabled 也安全:健康检查 unresolvableRows 会跳过 disabled 行
|
|
289
|
+
# (dsh-agent-presets lib/index.js:292 `if (Boolean(row.disabled)) continue;`)。
|
|
290
|
+
- id: tool-plugin-manager
|
|
291
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
292
|
+
disabled: true
|
|
293
|
+
|
|
274
294
|
# ── CC 角色卡工坊(CC 模式专用) ───────────────────────────────────
|
|
275
295
|
- id: cc-agent
|
|
276
296
|
name: '@xia-sc/dsh-cc-studio/agent'
|
package/presets/cc/preset.yml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
name: CC 模式
|
|
2
|
-
description: CCv3 角色卡工坊 — LLM 通过 6 个 Tool 引导填表(cc_get_card / cc_patch_character / cc_patch_world / cc_add_lorebook_entries / cc_patch_greetings / cc_validate),胶囊实时同步,工坊全屏编辑,导出 chara_card_v3。
|
|
1
|
+
name: CC 模式
|
|
2
|
+
description: CCv3 角色卡工坊 — LLM 通过 6 个 Tool 引导填表(cc_get_card / cc_patch_character / cc_patch_world / cc_add_lorebook_entries / cc_patch_greetings / cc_validate),胶囊实时同步,工坊全屏编辑,导出 chara_card_v3。
|
package/prototypes/index.html
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="zh-CN"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>dsh-cc-studio 原型总览</title>
|
|
3
|
-
<style>body{margin:0;font:14px/1.6 system-ui;background:#0b0d11;color:#e6e8ee} a{color:#7c5cff} .wrap{max-width:900px;margin:40px auto;padding:0 16px} .card{background:#171a21;border:1px solid #262c3a;border-radius:14px;padding:16px;margin-bottom:16px} .btn{display:inline-block;padding:10px 14px;border-radius:10px;background:#7c5cff;color:white;text-decoration:none;font-weight:700;margin-right:8px} .btn.ghost{background:transparent;border:1px solid #262c3a;color:#e6e8ee} code{background:#0f1218;border:1px solid #262c3a;padding:2px 6px;border-radius:6px}</style></head>
|
|
4
|
-
<body><div class="wrap">
|
|
5
|
-
<h1>🎭 dsh-cc-studio · 原型对比</h1>
|
|
6
|
-
<p style="color:#9aa3b8">Q1 的 A/B 两个可交互 H5 已生成,直接点开看效果(本地文件即可,或用 <code>file://</code> 打开)。Q2=B(五维深挖)、Q3=dsh-cc-studio、Q4=首版仅 JSON 已锁定。</p>
|
|
7
|
-
<div class="card"><h3 style="margin:0 0 8px">原型 A — 轻量浮层</h3><p style="color:#9aa3b8">平时只占输入框上方一行胶囊,点开全屏工坊。适合沉浸式一次性做完。</p><a class="btn" href="./prototype-a.html" target="_blank">打开原型 A</a><span style="color:#9aa3b8">文件:<code>E:\dsh\plugin\dsh-cc-studio\prototypes\prototype-a.html</code></span></div>
|
|
8
|
-
<div class="card"><h3 style="margin:0 0 8px">原型 B — 侧边常驻 + 浮层双形态</h3><p style="color:#9aa3b8">左侧 320px 常驻工坊(进度/5维快览/世界书快切/校验),中间照常聊天,右侧实时 JSON + 小助手。浮层仍保留用于沉浸式编辑。适合边聊边改。</p><a class="btn" href="./prototype-b.html" target="_blank">打开原型 B</a><span style="color:#9aa3b8">文件:<code>E:\dsh\plugin\dsh-cc-studio\prototypes\prototype-b.html</code></span></div>
|
|
9
|
-
<div class="card"><h3>下一步</h3><ol><li>你在这两个页面里点一点,感受一下 A 的“干净” vs B 的“边聊边改”。</li><li>回复一句 <b>“定 A”</b> 或 <b>“定 B”</b> 或 <b>“都要 / 融合”</b>,我立刻按定版开写 <code>dsh-cc-studio</code> 真插件(<code>package.json/cordis.patch.yml/lib/index.js/lib/client.js</code>),首版先跑通 JSON 导出。</li></ol></div>
|
|
10
|
-
<div class="card" style="background:#0f1218"><h4 style="margin:0 0 8px">已锁定的实现基线</h4><ul style="margin:0;color:#cbd5f0"><li>5维问卷:年表 / 势力 / 地理 / 力量体系 / 日常细节 → 一键生成带 <code>@@position/@@depth/@@activate</code> 的 Lorebook</li><li>CCv3 全字段表单 + Lorebook Decorator 可视化 + CBS 自动补全 + 实时校验</li><li>Host: <code>ctx.llm.stream</code> 透传 + <code>fs</code> 校验;Client: <code>shell.overlay + conversation.input.dock + settings.section</code></li><li>首版导出:<code>card.json</code>(<code>spec: chara_card_v3</code>),CHARX/PNG 二期</li></ul></div>
|
|
11
|
-
</div></body></html>
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8"/>
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
6
|
-
<title>原型A · 轻量浮层工坊 — dsh-cc-studio</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root{--bg:#0f1115;--panel:#171a21;--panel2:#1e232e;--line:#262c3a;--text:#e6e8ee;--muted:#9aa3b8;--acc:#7c5cff;--acc2:#2ee6a6;--warn:#ff6b6b;--radius:14px}
|
|
9
|
-
*{box-sizing:border-box} html,body{height:100%} body{margin:0;background:#0b0d11;color:var(--text);font:14px/1.6 ui-sans,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial}
|
|
10
|
-
a{color:var(--acc)}
|
|
11
|
-
.topbar{height:44px;display:flex;align-items:center;justify-content:space-between;padding:0 14px;border-bottom:1px solid var(--line);background:#0f1218;position:sticky;top:0;z-index:5}
|
|
12
|
-
.topbar .brand{display:flex;gap:10px;align-items:center;font-weight:700;letter-spacing:.2px}
|
|
13
|
-
.pill{padding:4px 8px;border-radius:999px;background:#1a1f2e;border:1px solid #2a3350;color:#aeb8d4;font-size:12px}
|
|
14
|
-
.shell{display:grid;grid-template-columns:280px 1fr;min-height:calc(100vh - 44px)}
|
|
15
|
-
.sidebar{border-right:1px solid var(--line);background:#0f1218;padding:14px;display:flex;flex-direction:column;gap:14px}
|
|
16
|
-
.side-card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px}
|
|
17
|
-
.side-card h4{margin:0 0 8px;font-size:12px;letter-spacing:.08em;color:var(--muted);text-transform:uppercase}
|
|
18
|
-
.conv{padding:16px;display:flex;flex-direction:column;gap:16px;background: radial-gradient(1200px 600px at 30% -10%, #1a1f3a 0, transparent 60%), linear-gradient(180deg,#0d0f14,#0b0d11)}
|
|
19
|
-
.msg{max-width:760px;background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
|
|
20
|
-
.msg.user{margin-left:auto;background:#1a1d27;border-color:#2a3147}
|
|
21
|
-
.msg .role{font-size:11px;color:var(--muted);margin-bottom:6px;letter-spacing:.06em}
|
|
22
|
-
.composer{position:sticky;bottom:0;background:linear-gradient(180deg, transparent, #0b0d11 28%);padding:12px 0 16px}
|
|
23
|
-
.dock{max-width:760px;margin:0 auto 8px;display:flex;gap:8px;align-items:center}
|
|
24
|
-
.capsule{appearance:none;border:1px solid #2d3655;background:#171c2e;color:#c8d1ec;border-radius:999px;padding:6px 12px;font-size:12px;display:inline-flex;gap:8px;align-items:center;cursor:pointer;transition:.15s}
|
|
25
|
-
.capsule:hover{border-color:#3a4670;background:#1c2240;transform:translateY(-1px)}
|
|
26
|
-
.capsule .dot{width:7px;height:7px;border-radius:50%;background:var(--acc2);box-shadow:0 0 10px var(--acc2)}
|
|
27
|
-
.inputbar{max-width:760px;margin:0 auto;background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:10px;display:flex;gap:10px;align-items:flex-end}
|
|
28
|
-
.inputbar textarea{flex:1;min-height:44px;max-height:120px;resize:none;background:#0f1218;border:1px solid #242b3d;border-radius:10px;padding:10px 12px;color:var(--text);outline:none}
|
|
29
|
-
.btn{appearance:none;border:1px solid transparent;background:var(--acc);color:white;border-radius:10px;padding:8px 14px;font-weight:600;cursor:pointer}
|
|
30
|
-
.btn.ghost{background:transparent;border-color:var(--line);color:var(--text)}
|
|
31
|
-
.btn.small{padding:6px 10px;font-size:12px;border-radius:8px}
|
|
32
|
-
/* overlay */
|
|
33
|
-
.overlay{position:fixed;inset:0;background:rgba(3,6,12,.62);backdrop-filter: blur(8px);display:none;align-items:center;justify-content:center;padding:18px;z-index:30}
|
|
34
|
-
.overlay.open{display:flex}
|
|
35
|
-
.workshop{width:min(1280px, 96vw);height:min(860px, 92vh);background:linear-gradient(180deg,#171c2a,#11141d);border:1px solid #2a324b;border-radius:18px;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 20px 80px rgba(0,0,0,.55)}
|
|
36
|
-
.ws-head{height:56px;display:flex;align-items:center;justify-content:space-between;padding:0 16px;border-bottom:1px solid #242d47;background:#131827}
|
|
37
|
-
.steps{display:flex;gap:6px;align-items:center}
|
|
38
|
-
.step{padding:6px 10px;border-radius:999px;font-size:12px;border:1px solid #2a334e;color:var(--muted);background:#0f1422}
|
|
39
|
-
.step.active{background:var(--acc);border-color:var(--acc);color:white;box-shadow:0 6px 16px rgba(124,92,255,.35)}
|
|
40
|
-
.ws-body{flex:1;display:grid;grid-template-columns:220px 1fr 360px;min-height:0}
|
|
41
|
-
.ws-nav{border-right:1px solid #242d47;background:#0f1320;padding:12px;display:flex;flex-direction:column;gap:10px}
|
|
42
|
-
.ws-nav button{width:100%;text-align:left;padding:10px 12px;border-radius:10px;border:1px solid transparent;background:transparent;color:#cbd5f0;cursor:pointer}
|
|
43
|
-
.ws-nav button.active{background:#1a2140;border-color:#2d3a66;color:white}
|
|
44
|
-
.ws-nav button small{display:block;color:var(--muted);font-size:11px;margin-top:2px}
|
|
45
|
-
.ws-main{padding:16px;overflow:auto}
|
|
46
|
-
.ws-side{border-left:1px solid #242d47;background:#0e1220;padding:12px;overflow:auto}
|
|
47
|
-
.card{background:#0f1422;border:1px solid #242d47;border-radius:12px;padding:12px;margin-bottom:12px}
|
|
48
|
-
.card h3{margin:0 0 8px;font-size:13px}
|
|
49
|
-
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
|
|
50
|
-
.field label{font-size:12px;color:var(--muted)}
|
|
51
|
-
.field input,.field textarea,.field select{width:100%;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px 10px;outline:none}
|
|
52
|
-
.field textarea{min-height:84px;resize:vertical}
|
|
53
|
-
.chips{display:flex;flex-wrap:wrap;gap:6px}
|
|
54
|
-
.chip{font-size:11px;padding:4px 8px;border-radius:999px;background:#12182a;border:1px solid #28365a;color:#aebde0;cursor:pointer}
|
|
55
|
-
.chip.on{background:var(--acc);border-color:var(--acc);color:white}
|
|
56
|
-
.lore-row{display:grid;grid-template-columns:1fr auto;gap:8px;align-items:center;background:#0b0f1b;border:1px solid #26314d;border-radius:10px;padding:10px;margin-bottom:8px}
|
|
57
|
-
.mono{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
|
|
58
|
-
.json{white-space:pre-wrap;word-break:break-all;background:#0b0f1b;border:1px solid #26314d;border-radius:10px;padding:10px;max-height:420px;overflow:auto}
|
|
59
|
-
.badge{font-size:11px;padding:2px 6px;border-radius:999px;border:1px solid #2a3350;background:#11182e;color:#9fb0d8}
|
|
60
|
-
.dim-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
|
61
|
-
.dim{border:1px dashed #2a3552;border-radius:12px;padding:10px;background:#0c1020}
|
|
62
|
-
.dim h4{margin:0 0 6px;font-size:12px;color:#cbd5f0}
|
|
63
|
-
.kpi{display:flex;gap:8px;flex-wrap:wrap}
|
|
64
|
-
@media (max-width:1100px){.ws-body{grid-template-columns:1fr}.ws-side{border-left:none;border-top:1px solid #242d47}.shell{grid-template-columns:1fr}.sidebar{display:none}}
|
|
65
|
-
</style>
|
|
66
|
-
</head>
|
|
67
|
-
<body>
|
|
68
|
-
<div class="topbar">
|
|
69
|
-
<div class="brand">⬢ DSH <span style="color:var(--muted);font-weight:400">· web — 会话内角色卡工坊</span></div>
|
|
70
|
-
<div style="display:flex;gap:8px;align-items:center"><span class="pill">CCv3 Living Standard</span><span class="pill" style="background:#1a2a1f;border-color:#2a4a3a;color:#a6e8c8">● 模型就绪</span></div>
|
|
71
|
-
</div>
|
|
72
|
-
<div class="shell">
|
|
73
|
-
<div class="sidebar">
|
|
74
|
-
<div class="side-card"><h4>工作区</h4><div class="mono" style="color:#cbd5f0">E:\demo\my-cards</div><div style="color:var(--muted);font-size:12px;margin-top:6px">会话 cwd 实时跟随(dsh-git 同款 store)</div></div>
|
|
75
|
-
<div class="side-card"><h4>最近草稿</h4><div style="display:flex;flex-direction:column;gap:8px"><div style="padding:8px 10px;border:1px solid #26314d;border-radius:10px;background:#0b0f1b"><div style="font-weight:600">星港遗民 · 琉璃</div><div style="color:var(--muted);font-size:12px">未导出 · 5 lorebook</div></div><div style="padding:8px 10px;border:1px dashed #26314d;border-radius:10px;color:var(--muted)">+ 新建角色卡</div></div></div>
|
|
76
|
-
<div class="side-card"><h4>原型说明</h4><div style="color:var(--muted);font-size:12px">A = 轻量浮层:平时只占输入框上方一行胶囊,点开全屏工坊。适合“写完再导”的沉浸式创作。</div></div>
|
|
77
|
-
</div>
|
|
78
|
-
<div class="conv">
|
|
79
|
-
<div class="msg"><div class="role">ASSISTANT</div>试着丢一句点子给我,比如“废土上开面馆的仿生人奶奶,会修收音机”。我帮你把世界观补齐到可导出的 CCv3。</div>
|
|
80
|
-
<div class="msg user"><div class="role">YOU</div>我想做一个雨城里的记忆典当行老板娘,能把记忆做成香薰蜡烛,客人闻到就能重温那段记忆。但我世界观很薄…</div>
|
|
81
|
-
<div class="composer">
|
|
82
|
-
<div class="dock">
|
|
83
|
-
<button class="capsule" id="openA">🎭 角色卡工坊 <span class="dot"></span> 未导出 · 点此展开</button>
|
|
84
|
-
<span class="badge">CCv3 校验:<span style="color:var(--warn)">缺 nickname / group_only_greetings</span></span>
|
|
85
|
-
<span class="badge">草稿自动保存 ✓</span>
|
|
86
|
-
</div>
|
|
87
|
-
<div class="inputbar">
|
|
88
|
-
<textarea placeholder="继续聊点子,或直接在工坊里编辑… Shift+Enter 换行"></textarea>
|
|
89
|
-
<button class="btn">发送</button>
|
|
90
|
-
</div>
|
|
91
|
-
<div style="max-width:760px;margin:8px auto 0;color:var(--muted);font-size:12px">按回车发送 · 工坊里每个 “AI 扩写” 都会走 DSH 的 llm.stream,失败可重试</div>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<div class="overlay" id="overlayA">
|
|
97
|
-
<div class="workshop" role="dialog" aria-modal="true">
|
|
98
|
-
<div class="ws-head">
|
|
99
|
-
<div style="display:flex;gap:12px;align-items:center"><strong>🎭 角色卡工坊</strong><span class="badge">dsh-cc-studio · A 浮层</span>
|
|
100
|
-
<div class="steps" id="stepsA"></div>
|
|
101
|
-
</div>
|
|
102
|
-
<div style="display:flex;gap:8px"><button class="btn ghost small" onclick="closeA()">收起</button><button class="btn small" onclick="exportJSON()">导出 JSON</button></div>
|
|
103
|
-
</div>
|
|
104
|
-
<div class="ws-body">
|
|
105
|
-
<div class="ws-nav" id="navA"></div>
|
|
106
|
-
<div class="ws-main" id="mainA"></div>
|
|
107
|
-
<div class="ws-side">
|
|
108
|
-
<div class="card"><h3>实时 CCv3 预览</h3><div class="kpi" style="margin-bottom:8px"><span class="badge">spec: chara_card_v3</span><span class="badge">校验: <span id="validA" style="color:var(--acc2)">通过</span></span></div><pre class="json mono" id="jsonA"></pre></div>
|
|
109
|
-
<div class="card"><h3>CBS 速查</h3><div class="chips"><span class="chip">{{char}}</span><span class="chip">{{user}}</span><span class="chip">{{random:A,B}}</span><span class="chip">{{roll:d6}}</span><span class="chip">{{// 注释}}</span></div><div style="color:var(--muted);font-size:12px;margin-top:8px">输入时自动补全,导出前会做大小写不敏感校验。</div></div>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
<script>
|
|
116
|
-
const steps = ["点子","世界观 5维","角色细化","世界书","校验导出"];
|
|
117
|
-
let cur = 0;
|
|
118
|
-
const draft = {
|
|
119
|
-
spec:"chara_card_v3", spec_version:"3.0",
|
|
120
|
-
data:{
|
|
121
|
-
name:"雨城烛坊 · 岚烟", nickname:"岚烟", tags:["记忆","雨城","香薰","典当"], creator:"you", character_version:"0.1",
|
|
122
|
-
description:"雨城常年细雨,霓虹在水膜上化开。岚烟经营一家无招牌的记忆典当行,将客人的记忆抽丝封进蜡烛。",
|
|
123
|
-
personality:"外柔内韧,嗅觉共情,话少但记性极好;对“自愿”有执念,拒绝强行抽取。",
|
|
124
|
-
scenario:"你推开吱呀的木门,铃铛轻响,雨气混着蜡与旧纸味扑面而来。岚烟在柜台后抬头,手中烛芯正袅袅成线。",
|
|
125
|
-
system_prompt:"You are 岚烟, keeper of memory candles. Reply in Chinese, sensory, restrained romance.",
|
|
126
|
-
post_history_instructions:"保持蜡烛=记忆的隐喻一致性,不主动破坏客人自愿原则。",
|
|
127
|
-
first_mes:"“雨又大了。先烤烤手吧——想典当哪一段?” 她把一盏未点的乳白蜡烛推到你面前,烛体里有细细的银线游动。",
|
|
128
|
-
alternate_greetings:["“今晚的雨适合想起旧人。你带记忆来了吗?”","“这盏是上周一位航行员留下的,里面有海风。”"],
|
|
129
|
-
group_only_greetings:["(群聊限定)“诸位若同时点烛,记忆会串味——我得一盏一盏来。”"],
|
|
130
|
-
mes_example:"{{user}}: 能把我5岁那年的夏夜还给我吗?\n{{char}}: 可以,但那段有蝉鸣和西瓜味,烧起来会很吵。你确定要现在闻吗?",
|
|
131
|
-
creator_notes:"雨城世界观薄弱用户的示例草稿", creator_notes_multilingual:{en:"Example draft for thin-worldbuilding user"}, source:[], assets:[{type:"icon",uri:"ccdefault:",name:"main",ext:"png"}], creation_date: Math.floor(Date.now()/1000), modification_date: Math.floor(Date.now()/1000),
|
|
132
|
-
character_book:{ name:"雨城烛坊 lorebook", description:"随卡私有世界书", scan_depth:4, token_budget:1200, recursive_scanning:false, extensions:{}, entries:[
|
|
133
|
-
{enabled:true, insertion_order:0, use_regex:false, constant:false, name:"雨城常雨", keys:["雨城","霓虹","潮气"], content:"@@position after_desc\n雨城年均降水 1800mm,排水系统以“雨梯”著称,霓虹倒影是导航。", priority:10},
|
|
134
|
-
{enabled:true, insertion_order:1, use_regex:false, constant:true, name:"蜡烛=记忆", keys:[], content:"@@activate\n蜡烛即记忆容器,银线密度=情绪强度。闻香即重温,不可食用。", priority:9},
|
|
135
|
-
]}
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
let idea="雨城里的记忆典当行老板娘,能把记忆做成香薰蜡烛";
|
|
139
|
-
let world={timeline:"2127 雨城独立后,记忆被列为可交易软资产", factions:"烛坊/雨梯管理局/盗忆人", geo:"环形雨城,内环干燥外环常雨", power:"抽忆术需自愿+嗅觉天赋", daily:"雨声电台、蜡烛夜市、记忆税"};
|
|
140
|
-
|
|
141
|
-
function renderSteps(){
|
|
142
|
-
const el=document.getElementById('stepsA');
|
|
143
|
-
el.innerHTML=steps.map((s,i)=>`<span class="step ${i===cur?'active':''}" onclick="cur=${i};renderA()">${i+1}. ${s}</span>`).join("");
|
|
144
|
-
}
|
|
145
|
-
function renderNav(){
|
|
146
|
-
const nav=document.getElementById('navA');
|
|
147
|
-
const items=[
|
|
148
|
-
{t:"点子投喂",d:"一句话点子 + 风格"},
|
|
149
|
-
{t:"世界观 5维",d:"年表/势力/地理/力量/日常"},
|
|
150
|
-
{t:"角色细化",d:"六件套 + 开场"},
|
|
151
|
-
{t:"世界书",d:"Lorebook + Decorator"},
|
|
152
|
-
{t:"校验导出",d:"JSON 预览与下载"},
|
|
153
|
-
];
|
|
154
|
-
nav.innerHTML=items.map((x,i)=>`<button class="${i===cur?'active':''}" onclick="cur=${i};renderA()">${x.t}<small>${x.d}</small></button>`).join("") +
|
|
155
|
-
`<div style="margin-top:auto;padding-top:12px;border-top:1px solid #242d47;display:flex;gap:8px"><button class="btn ghost small" style="flex:1" onclick="autoFill()">AI 扩写</button><button class="btn small" style="flex:1" onclick="validateA()">校验</button></div>`;
|
|
156
|
-
}
|
|
157
|
-
function renderA(){
|
|
158
|
-
renderSteps(); renderNav();
|
|
159
|
-
const m=document.getElementById('mainA');
|
|
160
|
-
if(cur===0){
|
|
161
|
-
m.innerHTML=`
|
|
162
|
-
<div class="card"><h3>点子投喂(薄世界观友好)</h3>
|
|
163
|
-
<div class="field"><label>一句话点子</label><textarea id="idea">${idea}</textarea></div>
|
|
164
|
-
<div class="field"><label>风格标签</label><div class="chips" id="tags"><span class="chip on">雨城</span><span class="chip on">感官系</span><span class="chip">赛博</span><span class="chip">治愈</span><span class="chip">微克苏鲁</span></div></div>
|
|
165
|
-
<div style="display:flex;gap:8px"><button class="btn small" onclick="expandIdea()">✦ AI 扩写成设定</button><button class="btn ghost small" onclick="cur=1;renderA()">跳过→世界观</button></div>
|
|
166
|
-
<div style="margin-top:10px;color:var(--muted);font-size:12px">会生成 description/personality/scenario 初稿 + 5维大纲草案,可一键采纳。</div>
|
|
167
|
-
</div>
|
|
168
|
-
<div class="card"><h3>AI 初稿(可直接采纳)</h3>
|
|
169
|
-
<div class="field"><label>description</label><textarea>${draft.data.description}</textarea></div>
|
|
170
|
-
<div class="field"><label>personality</label><textarea>${draft.data.personality}</textarea></div>
|
|
171
|
-
<div class="field"><label>scenario</label><textarea>${draft.data.scenario}</textarea></div>
|
|
172
|
-
</div>`;
|
|
173
|
-
} else if(cur===1){
|
|
174
|
-
m.innerHTML=`
|
|
175
|
-
<div class="card"><h3>世界观 5维(Q2-B 深度问卷)</h3><div style="color:var(--muted);font-size:12px;margin-bottom:10px">每维填 1-2 句即可,AI 会据此生成 Lorebook 条目(带 @@position/@@depth)。</div>
|
|
176
|
-
<div class="dim-grid">
|
|
177
|
-
<div class="dim"><h4>① 年表 Timeline</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">${world.timeline}</textarea></div>
|
|
178
|
-
<div class="dim"><h4>② 势力 Factions</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">${world.factions}</textarea></div>
|
|
179
|
-
<div class="dim"><h4>③ 地理 Geography</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">${world.geo}</textarea></div>
|
|
180
|
-
<div class="dim"><h4>④ 力量 Power</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">${world.power}</textarea></div>
|
|
181
|
-
<div class="dim" style="grid-column:span 2"><h4>⑤ 日常 Daily</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">${world.daily}</textarea></div>
|
|
182
|
-
</div>
|
|
183
|
-
<div style="display:flex;gap:8px;margin-top:12px"><button class="btn small" onclick="genLore()">✦ 5维→生成 6 条 Lorebook</button><button class="btn ghost small" onclick="cur=2;renderA()">下一步</button></div>
|
|
184
|
-
</div>`;
|
|
185
|
-
} else if(cur===2){
|
|
186
|
-
m.innerHTML=`
|
|
187
|
-
<div class="card"><h3>角色细化 — 六件套</h3>
|
|
188
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
|
|
189
|
-
<div class="field"><label>name</label><input value="${draft.data.name}"/></div>
|
|
190
|
-
<div class="field"><label>nickname ({{char}} 用)</label><input value="${draft.data.nickname}"/></div>
|
|
191
|
-
</div>
|
|
192
|
-
<div class="field"><label>tags</label><input value="${draft.data.tags.join(', ')}"/></div>
|
|
193
|
-
<div class="field"><label>description</label><textarea>${draft.data.description}</textarea></div>
|
|
194
|
-
<div class="field"><label>personality</label><textarea>${draft.data.personality}</textarea></div>
|
|
195
|
-
<div class="field"><label>scenario</label><textarea>${draft.data.scenario}</textarea></div>
|
|
196
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
|
|
197
|
-
<div class="field"><label>system_prompt</label><textarea>${draft.data.system_prompt}</textarea></div>
|
|
198
|
-
<div class="field"><label>post_history_instructions</label><textarea>${draft.data.post_history_instructions}</textarea></div>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
201
|
-
<div class="card"><h3>开场与示例</h3>
|
|
202
|
-
<div class="field"><label>first_mes</label><textarea>${draft.data.first_mes}</textarea></div>
|
|
203
|
-
<div class="field"><label>alternate_greetings (每行一条)</label><textarea>${draft.data.alternate_greetings.join('\n')}</textarea></div>
|
|
204
|
-
<div class="field"><label>group_only_greetings *必填</label><textarea>${draft.data.group_only_greetings.join('\n')}</textarea></div>
|
|
205
|
-
<div class="field"><label>mes_example ({{user}}/{{char}})</label><textarea style="min-height:110px">${draft.data.mes_example}</textarea></div>
|
|
206
|
-
</div>`;
|
|
207
|
-
} else if(cur===3){
|
|
208
|
-
const entries=draft.data.character_book.entries.map((e,i)=>`
|
|
209
|
-
<div class="lore-row">
|
|
210
|
-
<div><div style="font-weight:600">${e.name} <span class="badge">${e.constant?'constant':e.keys.join(', ')||'—'}</span> <span class="badge">order ${e.insertion_order}</span></div>
|
|
211
|
-
<div class="mono" style="color:#aebde0;margin-top:4px;white-space:pre-wrap">${e.content}</div>
|
|
212
|
-
<div class="chips" style="margin-top:6px"><span class="chip">@@depth 2</span><span class="chip">@@role system</span><span class="chip">@@position after_desc</span></div></div>
|
|
213
|
-
<div style="display:flex;flex-direction:column;gap:6px"><label style="font-size:12px"><input type="checkbox" ${e.enabled?'checked':''}/> enabled</label><button class="btn ghost small">编辑</button></div>
|
|
214
|
-
</div>`).join("");
|
|
215
|
-
m.innerHTML=`
|
|
216
|
-
<div class="card"><h3>世界书 Lorebook</h3><div style="display:flex;gap:8px;margin-bottom:8px"><span class="badge">scan_depth: ${draft.data.character_book.scan_depth}</span><span class="badge">token_budget: ${draft.data.character_book.token_budget}</span><span class="badge">entries: ${draft.data.character_book.entries.length}</span><button class="btn small" style="margin-left:auto" onclick="addEntry()">+ 新增条目</button></div>${entries}
|
|
217
|
-
<div style="margin-top:10px;display:flex;gap:8px"><button class="btn small" onclick="genLore()">✦ 用 5维再生成一批</button><button class="btn ghost small">导入 Lorebook JSON</button></div>
|
|
218
|
-
</div>`;
|
|
219
|
-
} else {
|
|
220
|
-
m.innerHTML=`
|
|
221
|
-
<div class="card"><h3>校验与导出 — 首版仅 JSON(最稳)</h3>
|
|
222
|
-
<div class="kpi"><span class="badge" style="border-color:#2a5a3a;color:#a6e8c8">✓ spec/spec_version 正确</span><span class="badge">group_only_greetings: 1 条</span><span class="badge">assets: 1</span></div>
|
|
223
|
-
<div style="display:flex;gap:8px;margin:12px 0"><button class="btn small" onclick="exportJSON()">⬇ 导出 card.json</button><button class="btn ghost small" onclick="validateA()">重新校验</button><span style="color:var(--muted);font-size:12px;align-self:center">CHARX/PNG 二期再上</span></div>
|
|
224
|
-
<div class="field"><label>creator_notes / 多语言</label><textarea>${draft.data.creator_notes}</textarea></div>
|
|
225
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px"><div class="field"><label>creation_date</label><input value="${draft.data.creation_date}" readonly/></div><div class="field"><label>modification_date (导出自动刷新)</label><input value="${draft.data.modification_date}" readonly/></div></div>
|
|
226
|
-
</div>`;
|
|
227
|
-
}
|
|
228
|
-
document.getElementById('jsonA').textContent = JSON.stringify(draft, null, 2);
|
|
229
|
-
}
|
|
230
|
-
function expandIdea(){ idea=document.getElementById('idea')?.value||idea; draft.data.description="雨城的雨是活的,会在玻璃上写字。岚烟的烛坊藏在雨梯夹层,蜡烛银线随记忆温度变色。"; renderA(); }
|
|
231
|
-
function genLore(){ draft.data.character_book.entries.push({enabled:true, insertion_order: draft.data.character_book.entries.length, use_regex:false, constant:false, name:"雨梯管理局", keys:["雨梯","管理局"], content:"@@depth 4\n雨梯管理局负责分配干燥配额,烛坊因“记忆税”与其周旋。", priority:8}); renderA(); }
|
|
232
|
-
function addEntry(){ draft.data.character_book.entries.push({enabled:true, insertion_order: draft.data.character_book.entries.length, use_regex:false, constant:false, name:"新条目", keys:["关键词"], content:"@@position personality\n新 lore 内容…", priority:5}); renderA(); }
|
|
233
|
-
function validateA(){ document.getElementById('validA').textContent="通过"; document.getElementById('validA').style.color="var(--acc2)"; }
|
|
234
|
-
function autoFill(){ expandIdea(); }
|
|
235
|
-
function exportJSON(){
|
|
236
|
-
const blob=new Blob([JSON.stringify(draft,null,2)],{type:"application/json"});
|
|
237
|
-
const a=document.createElement('a'); a.href=URL.createObjectURL(blob); a.download="card.json"; a.click(); URL.revokeObjectURL(a.href);
|
|
238
|
-
}
|
|
239
|
-
function openA(){ document.getElementById('overlayA').classList.add('open'); renderA(); }
|
|
240
|
-
function closeA(){ document.getElementById('overlayA').classList.remove('open'); }
|
|
241
|
-
document.getElementById('openA').addEventListener('click', openA);
|
|
242
|
-
document.getElementById('overlayA').addEventListener('click', (e)=>{ if(e.target.id==='overlayA') closeA(); });
|
|
243
|
-
renderA();
|
|
244
|
-
</script>
|
|
245
|
-
</body>
|
|
246
|
-
</html>
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8"/>
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
6
|
-
<title>原型B · 侧边常驻 + 浮层双形态 — dsh-cc-studio</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root{--bg:#0f1115;--panel:#171a21;--line:#262c3a;--text:#e6e8ee;--muted:#9aa3b8;--acc:#7c5cff;--acc2:#2ee6a6;--warn:#ffb020;--radius:14px}
|
|
9
|
-
*{box-sizing:border-box} html,body{height:100%} body{margin:0;background:#0b0d11;color:var(--text);font:14px/1.6 ui-sans,system-ui,Segoe UI,Roboto,Helvetica,Arial}
|
|
10
|
-
.topbar{height:44px;display:flex;align-items:center;justify-content:space-between;padding:0 14px;border-bottom:1px solid var(--line);background:#0f1218;position:sticky;top:0;z-index:5}
|
|
11
|
-
.pill{padding:4px 8px;border-radius:999px;background:#1a1f2e;border:1px solid #2a3350;color:#aeb8d4;font-size:12px}
|
|
12
|
-
.layout{display:grid;grid-template-columns:320px 1fr 340px;min-height:calc(100vh - 44px)}
|
|
13
|
-
.left{border-right:1px solid var(--line);background:#0f1218;display:flex;flex-direction:column;min-height:0}
|
|
14
|
-
.left-head{padding:12px 12px 10px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}
|
|
15
|
-
.left-body{flex:1;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:12px}
|
|
16
|
-
.card{background:#171a21;border:1px solid var(--line);border-radius:12px;padding:12px}
|
|
17
|
-
.card h4{margin:0 0 8px;font-size:12px;letter-spacing:.08em;color:var(--muted);text-transform:uppercase}
|
|
18
|
-
.progress{height:6px;background:#0b0f1b;border:1px solid #26314d;border-radius:999px;overflow:hidden}
|
|
19
|
-
.progress i{display:block;height:100%;width:62%;background:linear-gradient(90deg,var(--acc),#4ea1ff)}
|
|
20
|
-
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
|
|
21
|
-
.field label{font-size:12px;color:var(--muted)}
|
|
22
|
-
.field textarea,.field input{width:100%;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px 10px}
|
|
23
|
-
.chips{display:flex;flex-wrap:wrap;gap:6px}
|
|
24
|
-
.chip{font-size:11px;padding:4px 8px;border-radius:999px;background:#12182a;border:1px solid #28365a;color:#aebde0}
|
|
25
|
-
.chip.on{background:var(--acc);border-color:var(--acc);color:white}
|
|
26
|
-
.center{background: radial-gradient(900px 500px at 20% -10%, #1a1f3a 0, transparent 60%), linear-gradient(180deg,#0d0f14,#0b0d11);padding:16px;display:flex;flex-direction:column;gap:14px;min-height:0;overflow:auto}
|
|
27
|
-
.msg{max-width:760px;background:#171a21;border:1px solid var(--line);border-radius:14px;padding:14px 16px}
|
|
28
|
-
.msg.user{margin-left:auto;background:#1a1d27;border-color:#2a3147}
|
|
29
|
-
.composer{position:sticky;bottom:0;background:linear-gradient(180deg, transparent, #0b0d11 30%);padding:10px 0 14px}
|
|
30
|
-
.dock{max-width:760px;margin:0 auto 8px;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
31
|
-
.capsule{appearance:none;border:1px solid #2d3655;background:#171c2e;color:#c8d1ec;border-radius:999px;padding:6px 12px;font-size:12px;display:inline-flex;gap:8px;align-items:center;cursor:pointer}
|
|
32
|
-
.capsule.active{border-color:var(--acc);background:#1c2240}
|
|
33
|
-
.inputbar{max-width:760px;margin:0 auto;background:#171a21;border:1px solid var(--line);border-radius:16px;padding:10px;display:flex;gap:10px;align-items:flex-end}
|
|
34
|
-
.inputbar textarea{flex:1;min-height:44px;background:#0f1218;border:1px solid #242b3d;border-radius:10px;padding:10px 12px;color:var(--text);resize:none}
|
|
35
|
-
.btn{appearance:none;border:1px solid transparent;background:var(--acc);color:white;border-radius:10px;padding:8px 14px;font-weight:600;cursor:pointer}
|
|
36
|
-
.btn.ghost{background:transparent;border-color:var(--line);color:var(--text)}
|
|
37
|
-
.btn.small{padding:6px 10px;font-size:12px;border-radius:8px}
|
|
38
|
-
.right{border-left:1px solid var(--line);background:#0e1220;display:flex;flex-direction:column;min-height:0}
|
|
39
|
-
.right-head{padding:12px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between}
|
|
40
|
-
.right-body{flex:1;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:12px}
|
|
41
|
-
.mini-chat{border:1px solid #26314d;border-radius:12px;background:#0b0f1b;padding:10px;display:flex;flex-direction:column;gap:8px}
|
|
42
|
-
.mini-chat .bubble{background:#171a21;border:1px solid #2a3350;border-radius:10px;padding:8px 10px;font-size:12px}
|
|
43
|
-
.overlay{position:fixed;inset:0;background:rgba(3,6,12,.62);backdrop-filter:blur(8px);display:none;align-items:center;justify-content:center;padding:18px;z-index:30}
|
|
44
|
-
.overlay.open{display:flex}
|
|
45
|
-
.workshop{width:min(1180px,96vw);height:min(860px,92vh);background:linear-gradient(180deg,#171c2a,#11141d);border:1px solid #2a324b;border-radius:18px;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 20px 80px rgba(0,0,0,.55)}
|
|
46
|
-
.ws-head{height:56px;display:flex;align-items:center;justify-content:space-between;padding:0 16px;border-bottom:1px solid #242d47;background:#131827}
|
|
47
|
-
.steps{display:flex;gap:6px}
|
|
48
|
-
.step{padding:6px 10px;border-radius:999px;font-size:12px;border:1px solid #2a334e;color:var(--muted);background:#0f1422}
|
|
49
|
-
.step.active{background:var(--acc);border-color:var(--acc);color:white}
|
|
50
|
-
.ws-body{display:grid;grid-template-columns:220px 1fr 360px;flex:1;min-height:0}
|
|
51
|
-
.ws-nav{border-right:1px solid #242d47;background:#0f1320;padding:12px;display:flex;flex-direction:column;gap:10px}
|
|
52
|
-
.ws-nav button{width:100%;text-align:left;padding:10px 12px;border-radius:10px;border:1px solid transparent;background:transparent;color:#cbd5f0;cursor:pointer}
|
|
53
|
-
.ws-nav button.active{background:#1a2140;border-color:#2d3a66;color:white}
|
|
54
|
-
.mono{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
|
|
55
|
-
.json{white-space:pre-wrap;word-break:break-all;background:#0b0f1b;border:1px solid #26314d;border-radius:10px;padding:10px;max-height:360px;overflow:auto}
|
|
56
|
-
.badge{font-size:11px;padding:2px 6px;border-radius:999px;border:1px solid #2a3350;background:#11182e;color:#9fb0d8}
|
|
57
|
-
.dim-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
|
58
|
-
.dim{border:1px dashed #2a3552;border-radius:12px;padding:10px;background:#0c1020}
|
|
59
|
-
@media (max-width:1180px){.layout{grid-template-columns:1fr}.left,.right{display:none}}
|
|
60
|
-
</style>
|
|
61
|
-
</head>
|
|
62
|
-
<body>
|
|
63
|
-
<div class="topbar">
|
|
64
|
-
<div style="display:flex;gap:10px;align-items:center;font-weight:700">⬢ DSH <span style="color:var(--muted);font-weight:400">· 侧边常驻 + 浮层双形态</span></div>
|
|
65
|
-
<div style="display:flex;gap:8px"><span class="pill">CCv3</span><span class="pill" style="background:#1a2a1f;border-color:#2a4a3a;color:#a6e8c8">● 模型就绪</span></div>
|
|
66
|
-
</div>
|
|
67
|
-
|
|
68
|
-
<div class="layout">
|
|
69
|
-
<!-- LEFT persistent studio -->
|
|
70
|
-
<div class="left">
|
|
71
|
-
<div class="left-head"><strong style="font-size:13px">🎭 角色卡工坊</strong><span class="badge">常驻侧边</span></div>
|
|
72
|
-
<div class="left-body">
|
|
73
|
-
<div class="card"><h4>草稿进度</h4>
|
|
74
|
-
<div style="display:flex;justify-content:space-between;font-size:12px;margin-bottom:6px"><span>雨城烛坊 · 岚烟</span><span style="color:var(--acc2)">62%</span></div>
|
|
75
|
-
<div class="progress"><i></i></div>
|
|
76
|
-
<div style="display:flex;gap:6px;margin-top:10px;flex-wrap:wrap"><span class="badge" style="background:#1a2a1f;border-color:#2a4a3a;color:#a6e8c8">✓ 点子</span><span class="badge" style="background:#1a2a1f;border-color:#2a4a3a;color:#a6e8c8">✓ 5维</span><span class="badge">角色细化</span><span class="badge">世界书 5条</span></div>
|
|
77
|
-
<div style="display:flex;gap:8px;margin-top:10px"><button class="btn small" style="flex:1" onclick="openB()">全屏工坊</button><button class="btn ghost small" style="flex:1" onclick="exportJSON()">导出 JSON</button></div>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="card"><h4>5维快览</h4>
|
|
80
|
-
<div style="display:flex;flex-direction:column;gap:8px;font-size:12px">
|
|
81
|
-
<div>① 年表:2127 记忆软资产化</div><div>② 势力:烛坊 / 雨梯局 / 盗忆人</div><div>③ 地理:环形雨城</div><div>④ 力量:自愿+嗅觉</div><div>⑤ 日常:蜡烛夜市</div>
|
|
82
|
-
</div>
|
|
83
|
-
<button class="btn ghost small" style="width:100%;margin-top:10px" onclick="openB();cur=1;renderB()">✎ 去 5维编辑</button>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="card"><h4>世界书快切</h4>
|
|
86
|
-
<div style="display:flex;flex-direction:column;gap:8px">
|
|
87
|
-
<div style="padding:8px;border:1px solid #26314d;border-radius:10px;background:#0b0f1b;font-size:12px"><b>雨城常雨</b> <span class="badge">@@position after_desc</span><div class="mono" style="color:var(--muted)">雨梯…霓虹倒影导航</div></div>
|
|
88
|
-
<div style="padding:8px;border:1px solid #26314d;border-radius:10px;background:#0b0f1b;font-size:12px"><b>蜡烛=记忆</b> <span class="badge">@@activate</span><div class="mono" style="color:var(--muted)">银线密度=情绪强度</div></div>
|
|
89
|
-
</div>
|
|
90
|
-
<button class="btn ghost small" style="width:100%;margin-top:10px" onclick="openB();cur=3;renderB()">打开世界书</button>
|
|
91
|
-
</div>
|
|
92
|
-
<div class="card"><h4>校验</h4><div style="font-size:12px;color:var(--muted)">group_only_greetings: <span style="color:var(--acc2)">1 条 ✓</span> · assets 主图标唯一 ✓ · 正则 0 错误</div></div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<!-- CENTER conversation -->
|
|
97
|
-
<div class="center">
|
|
98
|
-
<div class="msg"><div style="font-size:11px;color:var(--muted);letter-spacing:.06em">ASSISTANT</div>我在侧边常驻着,你边聊边改,不用来回开关。试试:“把蜡烛夜市写得更拥挤一点”</div>
|
|
99
|
-
<div class="msg user"><div style="font-size:11px;color:var(--muted)">YOU</div>我想让盗忆人更可怜一点,不是纯反派</div>
|
|
100
|
-
<div class="msg"><div style="font-size:11px;color:var(--muted)">ASSISTANT · 已联动工坊</div>已把 “盗忆人:被雨梯局吊销嗅觉执照的前调香师,只能靠盗忆续命” 写进 5维→势力,并生成一条 lorebook:<span class="mono" style="background:#0b0f1b;border:1px solid #26314d;padding:2px 6px;border-radius:6px">@@depth 4 盗忆人银线发黑…</span></div>
|
|
101
|
-
<div class="composer">
|
|
102
|
-
<div class="dock">
|
|
103
|
-
<button class="capsule active" onclick="openB()">🎭 全屏工坊</button>
|
|
104
|
-
<button class="capsule" onclick="document.querySelector('.left').scrollIntoView({behavior:'smooth'})">⬅ 查看侧边</button>
|
|
105
|
-
<span class="badge">边聊边改 · 侧边实时同步</span>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="inputbar"><textarea placeholder="直接聊,侧边会自动同步到草稿…"></textarea><button class="btn">发送</button></div>
|
|
108
|
-
<div style="max-width:760px;margin:8px auto 0;color:var(--muted);font-size:12px">B 形态的核心差异:<b style="color:#cbd5f0">侧边常驻</b>,不需要打开浮层也能看到进度/快改;浮层仍保留用于沉浸式编辑。</div>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
<!-- RIGHT mini preview + CBS chat -->
|
|
113
|
-
<div class="right">
|
|
114
|
-
<div class="right-head"><strong style="font-size:13px">实时预览</strong><span class="badge">JSON</span></div>
|
|
115
|
-
<div class="right-body">
|
|
116
|
-
<div class="card" style="margin:0"><h4>CCv3 JSON</h4><pre class="json mono" id="jsonB"></pre><button class="btn ghost small" style="width:100%;margin-top:8px" onclick="exportJSON()">⬇ 导出 card.json</button></div>
|
|
117
|
-
<div class="mini-chat"><h4 style="margin:0;font-size:12px;color:var(--muted)">工坊小助手(走 DSH llm)</h4>
|
|
118
|
-
<div class="bubble">“把雨梯管理局写得官僚一点?”</div>
|
|
119
|
-
<div class="bubble" style="background:#1a2140;border-color:#2d3a66">已改:增加“干燥配额抽签 + 记忆税滞纳金”两条 lorebook。</div>
|
|
120
|
-
<div style="display:flex;gap:6px"><input placeholder="对工坊说句话…" style="flex:1;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;padding:6px 8px;color:var(--text)"/><button class="btn small">发送</button></div>
|
|
121
|
-
</div>
|
|
122
|
-
<div class="card" style="margin:0"><h4>CBS 提示</h4><div class="chips"><span class="chip">{{char}}</span><span class="chip">{{user}}</span><span class="chip">{{random:A,B}}</span></div></div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
|
|
127
|
-
<!-- Overlay workshop (same core as A, but triggered from B) -->
|
|
128
|
-
<div class="overlay" id="overlayB">
|
|
129
|
-
<div class="workshop" role="dialog" aria-modal="true">
|
|
130
|
-
<div class="ws-head"><div style="display:flex;gap:12px;align-items:center"><strong>🎭 角色卡工坊 · 全屏</strong><span class="badge">B 的浮层 = A 的工坊</span><div class="steps" id="stepsB"></div></div><div style="display:flex;gap:8px"><button class="btn ghost small" onclick="closeB()">返回侧边</button><button class="btn small" onclick="exportJSON()">导出 JSON</button></div></div>
|
|
131
|
-
<div class="ws-body">
|
|
132
|
-
<div class="ws-nav" id="navB"></div>
|
|
133
|
-
<div style="padding:16px;overflow:auto" id="mainB"></div>
|
|
134
|
-
<div style="border-left:1px solid #242d47;background:#0e1220;padding:12px;overflow:auto"><div class="card"><h4>预览</h4><pre class="json mono" id="jsonB2"></pre></div></div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<script>
|
|
140
|
-
const steps=["点子","5维","角色","世界书","导出"];
|
|
141
|
-
let cur=0;
|
|
142
|
-
const draft={spec:"chara_card_v3",spec_version:"3.0",data:{name:"雨城烛坊 · 岚烟",nickname:"岚烟",tags:["记忆","雨城","香薰"],creator:"you",character_version:"0.1",description:"雨城常雨,岚烟的烛坊以蜡封记忆。",personality:"外柔内韧,嗅觉共情",scenario:"你推开木门,雨气与蜡味扑面",system_prompt:"You are 岚烟",post_history_instructions:"保持蜡烛隐喻",first_mes:"“想典当哪一段?”",alternate_greetings:["“今晚适合想起旧人。”"],group_only_greetings:["(群聊)“一盏一盏来。”"],mes_example:"{{user}}: 还我夏夜\n{{char}}: 会很吵,你确定吗?",creator_notes:"B 原型",assets:[{type:"icon",uri:"ccdefault:",name:"main",ext:"png"}],creation_date:Math.floor(Date.now()/1000),modification_date:Math.floor(Date.now()/1000),character_book:{name:"雨城 lorebook",scan_depth:4,token_budget:1200,recursive_scanning:false,extensions:{},entries:[
|
|
143
|
-
{enabled:true,insertion_order:0,use_regex:false,constant:false,name:"雨城常雨",keys:["雨城"],content:"@@position after_desc\n雨梯与霓虹",priority:10},
|
|
144
|
-
{enabled:true,insertion_order:1,use_regex:false,constant:true,name:"蜡烛=记忆",keys:[],content:"@@activate\n蜡烛即记忆",priority:9},
|
|
145
|
-
{enabled:true,insertion_order:2,use_regex:false,constant:false,name:"盗忆人",keys:["盗忆人"],content:"@@depth 4\n前调香师,被吊销执照,银线发黑",priority:8},
|
|
146
|
-
]}}};
|
|
147
|
-
function renderSteps(){
|
|
148
|
-
const el=document.getElementById('stepsB');
|
|
149
|
-
el.innerHTML=steps.map((s,i)=>`<span class="step ${i===cur?'active':''}" onclick="cur=${i};renderB()">${i+1}. ${s}</span>`).join("");
|
|
150
|
-
}
|
|
151
|
-
function renderNav(){
|
|
152
|
-
const nav=document.getElementById('navB');
|
|
153
|
-
const items=[{t:"点子投喂",d:"一句话+风格"},{t:"世界观 5维",d:"年表/势力/地理/力量/日常"},{t:"角色细化",d:"六件套"},{t:"世界书",d:"Lorebook"},{t:"校验导出",d:"JSON"}];
|
|
154
|
-
nav.innerHTML=items.map((x,i)=>`<button class="${i===cur?'active':''}" onclick="cur=${i};renderB()">${x.t}<div style="font-size:11px;color:var(--muted)">${x.d}</div></button>`).join("") + `<div style="margin-top:auto;padding-top:12px;border-top:1px solid #242d47;display:flex;gap:8px"><button class="btn ghost small" style="flex:1">AI 扩写</button><button class="btn small" style="flex:1">校验</button></div>`;
|
|
155
|
-
}
|
|
156
|
-
function renderB(){
|
|
157
|
-
renderSteps(); renderNav();
|
|
158
|
-
const m=document.getElementById('mainB');
|
|
159
|
-
if(cur===0) m.innerHTML=`<div class="card"><h4>点子投喂</h4><div class="field"><label>一句话点子</label><textarea>雨城记忆典当行老板娘,蜡烛封记忆</textarea></div><div class="chips"><span class="chip on">雨城</span><span class="chip on">感官系</span><span class="chip">治愈</span></div><div style="margin-top:10px"><button class="btn small">✦ AI 扩写</button></div></div><div class="card"><h4>AI 初稿</h4><div class="field"><label>description</label><textarea>${draft.data.description}</textarea></div></div>`;
|
|
160
|
-
else if(cur===1) m.innerHTML=`<div class="card"><h4>世界观 5维(B 的侧边会同步)</h4><div class="dim-grid"><div class="dim"><h4>① 年表</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">2127 雨城独立,记忆软资产化</textarea></div><div class="dim"><h4>② 势力</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">烛坊 / 雨梯局 / 盗忆人(前调香师)</textarea></div><div class="dim"><h4>③ 地理</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">环形雨城,内干外雨</textarea></div><div class="dim"><h4>④ 力量</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">自愿+嗅觉,银线=情绪强度</textarea></div><div class="dim" style="grid-column:span 2"><h4>⑤ 日常</h4><textarea style="width:100%;min-height:70px;background:#0b0f1b;border:1px solid #26314d;border-radius:8px;color:var(--text);padding:8px">蜡烛夜市、雨声电台、记忆税</textarea></div></div><button class="btn small" style="margin-top:12px">✦ 5维→生成 Lorebook</button></div>`;
|
|
161
|
-
else if(cur===2) m.innerHTML=`<div class="card"><h4>角色细化</h4><div style="display:grid;grid-template-columns:1fr 1fr;gap:10px"><div class="field"><label>name</label><input value="${draft.data.name}"/></div><div class="field"><label>nickname</label><input value="${draft.data.nickname}"/></div></div><div class="field"><label>description</label><textarea>${draft.data.description}</textarea></div></div>`;
|
|
162
|
-
else if(cur===3) m.innerHTML=`<div class="card"><h4>世界书</h4>${draft.data.character_book.entries.map(e=>`<div style="padding:10px;border:1px solid #26314d;border-radius:10px;background:#0b0f1b;margin-bottom:8px"><b>${e.name}</b> <span class="badge">${e.keys.join(',')||'constant'}</span><div class="mono" style="color:var(--muted)">${e.content}</div></div>`).join("")}<button class="btn small">+ 新增条目</button></div>`;
|
|
163
|
-
else m.innerHTML=`<div class="card"><h4>校验与导出(仅 JSON)</h4><div style="display:flex;gap:8px;margin:10px 0"><button class="btn small" onclick="exportJSON()">⬇ 导出 card.json</button><button class="btn ghost small">校验</button></div><div style="color:var(--muted);font-size:12px">CHARX/PNG 二期</div></div>`;
|
|
164
|
-
document.getElementById('jsonB2').textContent=JSON.stringify(draft,null,2);
|
|
165
|
-
document.getElementById('jsonB').textContent=JSON.stringify(draft,null,2);
|
|
166
|
-
}
|
|
167
|
-
function openB(){document.getElementById('overlayB').classList.add('open'); renderB();}
|
|
168
|
-
function closeB(){document.getElementById('overlayB').classList.remove('open');}
|
|
169
|
-
function exportJSON(){const b=new Blob([JSON.stringify(draft,null,2)],{type:"application/json"});const a=document.createElement('a');a.href=URL.createObjectURL(b);a.download="card.json";a.click(); setTimeout(()=>URL.revokeObjectURL(a.href),1000);}
|
|
170
|
-
document.getElementById('overlayB').addEventListener('click',e=>{if(e.target.id==='overlayB')closeB();});
|
|
171
|
-
renderB();
|
|
172
|
-
document.getElementById('jsonB').textContent=JSON.stringify(draft,null,2);
|
|
173
|
-
</script>
|
|
174
|
-
</body>
|
|
175
|
-
</html>
|