dsh-her 0.0.1 → 0.1.0

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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/cordis.patch.yml +5 -0
  4. package/examples/workshop/README.md +20 -0
  5. package/lib/client.js +1602 -0
  6. package/lib/core/character.js +50 -0
  7. package/lib/core/cues.js +62 -0
  8. package/lib/core/echo.js +27 -0
  9. package/lib/core/index.js +10 -0
  10. package/lib/core/paths.js +14 -0
  11. package/lib/core/prompt.js +63 -0
  12. package/lib/core/sentences.js +115 -0
  13. package/lib/core/speakable.js +26 -0
  14. package/lib/core/voices.js +43 -0
  15. package/lib/core/wav.js +71 -0
  16. package/lib/core/workshop.js +330 -0
  17. package/lib/dsh/api.js +156 -0
  18. package/lib/dsh/body-install.js +137 -0
  19. package/lib/dsh/body-launcher.js +48 -0
  20. package/lib/dsh/config.js +27 -0
  21. package/lib/dsh/her-session.js +282 -0
  22. package/lib/dsh/import-status.js +6 -0
  23. package/lib/dsh/plugin.js +1010 -0
  24. package/lib/dsh/workshop.js +222 -0
  25. package/lib/index.js +11 -0
  26. package/lib/memory/digest.js +119 -0
  27. package/lib/memory/search.js +52 -0
  28. package/lib/memory/store.js +267 -0
  29. package/lib/protocol/index.js +39 -0
  30. package/lib/server/brain-server.js +219 -0
  31. package/lib/types/core/character.d.ts +32 -0
  32. package/lib/types/core/cues.d.ts +20 -0
  33. package/lib/types/core/echo.d.ts +10 -0
  34. package/lib/types/core/index.d.ts +10 -0
  35. package/lib/types/core/paths.d.ts +11 -0
  36. package/lib/types/core/prompt.d.ts +39 -0
  37. package/lib/types/core/sentences.d.ts +40 -0
  38. package/lib/types/core/speakable.d.ts +6 -0
  39. package/lib/types/core/voices.d.ts +23 -0
  40. package/lib/types/core/wav.d.ts +9 -0
  41. package/lib/types/core/workshop.d.ts +156 -0
  42. package/lib/types/dsh/api.d.ts +163 -0
  43. package/lib/types/dsh/body-install.d.ts +58 -0
  44. package/lib/types/dsh/body-launcher.d.ts +28 -0
  45. package/lib/types/dsh/config.d.ts +34 -0
  46. package/lib/types/dsh/her-session.d.ts +215 -0
  47. package/lib/types/dsh/import-status.d.ts +17 -0
  48. package/lib/types/dsh/plugin.d.ts +34 -0
  49. package/lib/types/dsh/workshop.d.ts +94 -0
  50. package/lib/types/index.d.ts +11 -0
  51. package/lib/types/memory/digest.d.ts +33 -0
  52. package/lib/types/memory/search.d.ts +17 -0
  53. package/lib/types/memory/store.d.ts +132 -0
  54. package/lib/types/protocol/index.d.ts +434 -0
  55. package/lib/types/server/brain-server.d.ts +71 -0
  56. package/lib/types/voice/cloud-key.d.ts +10 -0
  57. package/lib/types/voice/cosyvoice.d.ts +47 -0
  58. package/lib/types/voice/edge.d.ts +52 -0
  59. package/lib/types/voice/engine.d.ts +30 -0
  60. package/lib/types/voice/models.d.ts +21 -0
  61. package/lib/types/voice/speech-queue.d.ts +55 -0
  62. package/lib/types/voice/voice.d.ts +75 -0
  63. package/lib/types/voice/worker.d.ts +37 -0
  64. package/lib/voice/cloud-key.js +23 -0
  65. package/lib/voice/cosyvoice.js +194 -0
  66. package/lib/voice/edge.js +199 -0
  67. package/lib/voice/engine.js +101 -0
  68. package/lib/voice/models.js +69 -0
  69. package/lib/voice/speech-queue.js +118 -0
  70. package/lib/voice/voice.js +171 -0
  71. package/lib/voice/worker.js +47 -0
  72. package/package.json +61 -4
package/lib/client.js ADDED
@@ -0,0 +1,1602 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-her",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ "use strict";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // src/client/index.tsx
26
+ var index_exports = {};
27
+ __export(index_exports, {
28
+ PACKAGE_NAME: () => PACKAGE_NAME,
29
+ PANEL_ID: () => PANEL_ID,
30
+ apply: () => apply,
31
+ inject: () => inject
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/client/Page.tsx
36
+ var import_react3 = require("react");
37
+
38
+ // src/client/api.ts
39
+ async function post(path, body = {}) {
40
+ try {
41
+ const res = await fetch(new URL(`/api/dsh-her${path}`, document.baseURI), {
42
+ method: "POST",
43
+ credentials: "same-origin",
44
+ headers: { "content-type": "application/json" },
45
+ body: JSON.stringify(body)
46
+ });
47
+ const value = await res.json().catch(() => null);
48
+ if (value === null) return { ok: false, message: `HTTP ${res.status}` };
49
+ return value;
50
+ } catch (error) {
51
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
52
+ }
53
+ }
54
+ var herApi = {
55
+ state: () => post("/state"),
56
+ saveCharacter: (patch) => post("/character", patch),
57
+ expandPersona: (input) => post("/persona/expand", input),
58
+ tryPersona: (input) => post("/persona/try", input),
59
+ previewVoice: (voice, speed, text) => post("/voice/preview", { voice, speed, ...text === void 0 ? {} : { text } }),
60
+ startBody: () => post("/body/start"),
61
+ installBody: () => post("/body/install"),
62
+ restartBody: () => post("/body/restart"),
63
+ setMode: (mode) => post("/body/mode", { mode }),
64
+ pickPack: () => post("/pack/pick"),
65
+ usePack: (packId) => post("/pack/use", { packId }),
66
+ workshopList: () => post("/workshop/list"),
67
+ workshopNew: (name) => post("/workshop/new", { name }),
68
+ workshopSave: (project) => post("/workshop/save", { project }),
69
+ workshopExport: (id, media) => post("/workshop/export", { id, media }),
70
+ workshopImportProject: () => post("/workshop/import-project"),
71
+ workshopExamples: () => post("/workshop/examples"),
72
+ workshopOpen: (id) => post("/workshop/open", { id }),
73
+ workshopFrame: (id, scene) => post("/workshop/frame", { id, scene }),
74
+ workshopImport: (id) => post("/workshop/import", { id }),
75
+ setLook: (change) => post("/look/set", change),
76
+ memoryRemove: (ids) => post("/memory/remove", { ids }),
77
+ memoryConfirm: (ids) => post("/memory/confirm", { ids }),
78
+ digest: () => post("/memory/digest"),
79
+ exportMemory: () => post("/memory/export")
80
+ };
81
+ async function modelCatalog() {
82
+ try {
83
+ const res = await fetch(new URL("/api/session/modelCatalog", document.baseURI), {
84
+ method: "POST",
85
+ credentials: "same-origin",
86
+ headers: { "content-type": "application/json" },
87
+ body: JSON.stringify({ type: "client-request", rpcId: crypto.randomUUID(), method: "session/modelCatalog", payload: { args: {} } })
88
+ });
89
+ const reply = await res.json();
90
+ const value = reply.result?.value;
91
+ if (reply.result?.ok !== true || value === void 0) return { ok: false, message: reply.result?.error?.message ?? `HTTP ${res.status}` };
92
+ const routable = value.routableProviders;
93
+ const groups = (value.groups ?? []).filter((g) => routable === void 0 || routable.includes(g.id));
94
+ return { ok: true, groups, ...value.default === void 0 ? {} : { default: value.default } };
95
+ } catch (error) {
96
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
97
+ }
98
+ }
99
+ function playAudio(base64, format) {
100
+ void new Audio(`data:audio/${format === "mp3" ? "mpeg" : "wav"};base64,${base64}`).play();
101
+ }
102
+
103
+ // src/core/voices.ts
104
+ var VOICES = [
105
+ { id: "zh-CN-XiaoxiaoNeural", name: "\u6653\u6653", desc: "\u6E29\u6696\uFF0C\u81EA\u7136", service: "edge" },
106
+ { id: "zh-CN-XiaoyiNeural", name: "\u6653\u4F0A", desc: "\u6D3B\u6CFC\uFF0C\u53EF\u7231", service: "edge" },
107
+ { id: "zh-TW-HsiaoChenNeural", name: "\u66C9\u81FB", desc: "\u6E29\u67D4\uFF0C\u53F0\u6E7E\u8154", service: "edge" },
108
+ { id: "zh-TW-HsiaoYuNeural", name: "\u66C9\u96E8", desc: "\u6E05\u4EAE\uFF0C\u53F0\u6E7E\u8154", service: "edge" },
109
+ { id: "zh-CN-liaoning-XiaobeiNeural", name: "\u6653\u5317", desc: "\u723D\u6717\uFF0C\u4E1C\u5317\u8BDD", service: "edge" },
110
+ { id: "zh-CN-shaanxi-XiaoniNeural", name: "\u6653\u59AE", desc: "\u9655\u897F\u8BDD", service: "edge" },
111
+ { id: "zh-HK-HiuMaanNeural", name: "\u66C9\u66FC", desc: "\u7CA4\u8BED", service: "edge" },
112
+ { id: "longanrou_v3", name: "\u9F99\u5B89\u67D4", desc: "\u6E29\u67D4\uFF0C\u50CF\u95FA\u871C", service: "cosyvoice" },
113
+ { id: "longxing_v3", name: "\u9F99\u661F", desc: "\u6E29\u5A49\uFF0C\u90BB\u5BB6\u5973\u5B69", service: "cosyvoice" },
114
+ { id: "longanhuan_v3", name: "\u9F99\u5B89\u6B22", desc: "\u6B22\u8131\u5143\u6C14", service: "cosyvoice" },
115
+ { id: "longanqin_v3", name: "\u9F99\u5B89\u4EB2", desc: "\u4EB2\u548C\uFF0C\u5F00\u6717", service: "cosyvoice" },
116
+ { id: "longanling_v3", name: "\u9F99\u5B89\u7075", desc: "\u673A\u7075\uFF0C\u6D3B\u6CFC", service: "cosyvoice" },
117
+ { id: "longhua_v3", name: "\u9F99\u534E", desc: "\u5143\u6C14\uFF0C\u751C\u7F8E", service: "cosyvoice" },
118
+ { id: "longfeifei_v3", name: "\u9F99\u83F2\u83F2", desc: "\u751C\u7F8E\uFF0C\u7231\u6492\u5A07", service: "cosyvoice" },
119
+ { id: "longantai_v3", name: "\u9F99\u5B89\u53F0", desc: "\u55F2\u751C\uFF0C\u53F0\u6E7E\u8154", service: "cosyvoice" },
120
+ { id: "longwan_v3", name: "\u9F99\u5A49", desc: "\u7EC6\u817B\uFF0C\u67D4\u58F0", service: "cosyvoice" },
121
+ { id: "longyingling_v3", name: "\u9F99\u5E94\u8046", desc: "\u6E29\u548C\uFF0C\u4F1A\u5171\u60C5", service: "cosyvoice" },
122
+ { id: "longanwen_v3", name: "\u9F99\u5B89\u6E29", desc: "\u4F18\u96C5\uFF0C\u77E5\u6027", service: "cosyvoice" },
123
+ { id: "longxiaochun_v3", name: "\u9F99\u5C0F\u6DF3", desc: "\u77E5\u6027\uFF0C\u79EF\u6781", service: "cosyvoice" }
124
+ ];
125
+ function voiceName(id) {
126
+ return VOICES.find((v2) => v2.id === id)?.name ?? id;
127
+ }
128
+ var SERVICE_NAMES = {
129
+ edge: "\u5FAE\u8F6F Edge\uFF08\u514D\u8D39\uFF0C\u8981\u8054\u7F51\uFF09",
130
+ cosyvoice: "\u963F\u91CC\u4E91\u767E\u70BC CosyVoice\uFF08\u8981 key\uFF0C\u6309\u5B57\u6570\u4ED8\u8D39\uFF09"
131
+ };
132
+
133
+ // src/client/Workshop.tsx
134
+ var import_react = require("react");
135
+
136
+ // src/core/workshop.ts
137
+ var DESK_DIR = "\u684C\u5BA0";
138
+ var sceneDir = (scene) => `\u573A\u666F-${safeName(scene.name)}`;
139
+ function safeName(text) {
140
+ return text.trim().replace(/[\\/:*?"<>|]+/g, "-").replace(/^\.+/, "") || "\u672A\u547D\u540D";
141
+ }
142
+ var CARD_CHOICES = {
143
+ gender: ["\u5973", "\u7537"],
144
+ age: ["\u4E8C\u5341\u51FA\u5934", "\u4E8C\u5341\u591A\u5C81", "\u4E09\u5341\u5C81\u5DE6\u53F3", "\u4E09\u5341\u591A\u5C81"],
145
+ skin: ["\u767D\u7699", "\u81EA\u7136\u80A4\u8272", "\u5C0F\u9EA6\u8272", "\u6DF1\u8272"],
146
+ hairStyle: ["\u957F\u76F4\u53D1", "\u957F\u5377\u53D1", "\u4E2D\u957F\u53D1", "\u77ED\u53D1", "\u9A6C\u5C3E", "\u4E38\u5B50\u5934"],
147
+ hairColor: ["\u9ED1\u8272", "\u6DF1\u68D5\u8272", "\u6817\u8272", "\u91D1\u8272", "\u94F6\u767D\u8272"],
148
+ build: ["\u7EA4\u7EC6", "\u5300\u79F0", "\u5065\u7F8E", "\u4E30\u6EE1"],
149
+ outfit: ["\u5BB6\u5C45\u670D", "\u886C\u886B\u8FDE\u8863\u88D9", "\u536B\u8863\u548C\u725B\u4ED4\u88E4", "\u540A\u5E26\u548C\u77ED\u88D9", "\u897F\u88C5\u5957\u88C5", "\u9488\u7EC7\u886B\u548C\u957F\u88D9", "\u6C49\u670D"],
150
+ accessories: ["\u65E0", "\u7EC6\u6846\u773C\u955C", "\u53D1\u5361", "\u8033\u73AF", "\u9879\u94FE"],
151
+ style: ["\u5199\u5B9E\u7167\u7247", "\u65E5\u7CFB\u52A8\u6F2B", "3D \u5361\u901A"]
152
+ };
153
+ var CARD_LABELS = {
154
+ gender: "\u6027\u522B",
155
+ age: "\u5E74\u9F84",
156
+ skin: "\u80A4\u8272",
157
+ hairStyle: "\u53D1\u578B",
158
+ hairColor: "\u53D1\u8272",
159
+ build: "\u8EAB\u6750",
160
+ outfit: "\u8863\u670D",
161
+ accessories: "\u914D\u9970",
162
+ style: "\u753B\u98CE",
163
+ extra: "\u5176\u4ED6\u7279\u5F81",
164
+ sentence: "\u89D2\u8272\u63CF\u8FF0"
165
+ };
166
+ var subject = (card) => card.gender === "\u7537" ? "\u6210\u5E74\u7537\u6027" : card.gender === "\u5973" ? "\u6210\u5E74\u5973\u6027" : `\u6210\u5E74\u4EBA\uFF08${card.gender}\uFF09`;
167
+ function describe(card) {
168
+ if (card.sentence.trim() !== "") return card.sentence.trim();
169
+ const parts = [
170
+ `${card.age}\u7684${subject(card)}`,
171
+ `${card.skin}\u76AE\u80A4`,
172
+ `${card.hairColor}${card.hairStyle}`,
173
+ ...card.accessories === "\u65E0" || card.accessories.trim() === "" ? [] : [`\u6234${card.accessories}`],
174
+ `\u7A7F${card.outfit}`,
175
+ `\u8EAB\u6750${card.build}`,
176
+ ...card.extra.trim() === "" ? [] : [card.extra.trim()]
177
+ ];
178
+ return parts.join("\uFF0C");
179
+ }
180
+ var styleWords = (card) => card.style === "\u5199\u5B9E\u7167\u7247" ? "\u5199\u5B9E\u6444\u5F71\u98CE\u683C\uFF0C\u771F\u5B9E\u76AE\u80A4\u8D28\u611F\uFF0C\u81EA\u7136\u5149" : card.style === "\u65E5\u7CFB\u52A8\u6F2B" ? "\u65E5\u7CFB\u52A8\u6F2B\u753B\u98CE\uFF0C\u5E72\u51C0\u7EBF\u6761" : card.style === "3D \u5361\u901A" ? "3D \u5361\u901A\u6E32\u67D3\u98CE\u683C" : card.style;
181
+ function screenColor(card) {
182
+ return /绿/.test(`${card.outfit}${card.extra}${card.sentence}`) ? { name: "\u7EAF\u7070\u8272", hex: "#808080" } : { name: "\u7EAF\u7EFF\u8272", hex: "#00B140" };
183
+ }
184
+ var POSE_WORDS = { stand: "\u7AD9\u7740", sit: "\u5750\u7740", lie: "\u8EBA\u7740" };
185
+ var WEB_HOW = '\u4E00\u6BB5\u6BB5\u751F\u6210\uFF1A\u4E0A\u4F20\u9996\u5E27\uFF0C\u7C98\u8D34\u63D0\u793A\u8BCD\uFF08\u6709"\u8D1F\u9762\u63D0\u793A\u8BCD"\u6846\u5C31\u628A\u8D1F\u9762\u63D0\u793A\u8BCD\u4E5F\u7C98\u8FDB\u53BB\uFF0C\u6709"\u5C3E\u5E27"\u5C31\u4E5F\u653E\u9996\u5E27\u90A3\u5F20\u56FE\uFF09\uFF0C\u4E0B\u8F7D\u540E\u6309\u6587\u4EF6\u540D\u5B58\u8FDB\u5BF9\u5E94\u7684\u6587\u4EF6\u5939\u3002\u300C\u751F\u6210\u6E05\u5355.md\u300D\u91CC\u6BCF\u6BB5\u90FD\u5217\u597D\u4E86\u3002';
186
+ var GENERATORS = {
187
+ minimax: {
188
+ name: "MiniMax Code",
189
+ label: "MiniMax Code\uFF08\u7ED9\u5B83\u4EFB\u52A1\u6587\u4EF6\uFF0C\u81EA\u52A8\u751F\u6210\u4E0B\u8F7D\uFF09",
190
+ settings: "MiniMax-Hailuo-2.3 \u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C6 \u79D2\u3002",
191
+ how: '\u5728 MiniMax Code \u91CC\u8BF4\uFF1A"\u6309 <\u9879\u76EE\u6587\u4EF6\u5939>/MiniMax\u751F\u6210\u4EFB\u52A1.md \u751F\u6210"\uFF0C\u5B83\u4F1A\u4E00\u6BB5\u6BB5\u751F\u6210\u3001\u4E0B\u8F7D\u3001\u5B58\u597D\u3002'
192
+ },
193
+ comfyui: {
194
+ name: "ComfyUI",
195
+ label: "ComfyUI\uFF08\u672C\u5730\uFF0CWan 2.2 \u7B49\u56FE\u751F\u89C6\u9891\u5DE5\u4F5C\u6D41\uFF09",
196
+ settings: "\u56FE\u751F\u89C6\u9891\u5DE5\u4F5C\u6D41\uFF08\u6BD4\u5982 Wan 2.2 I2V\uFF09\uFF0C\u5C3A\u5BF8\u548C\u9996\u5E27\u540C\u6BD4\u4F8B\u3001\u77ED\u8FB9 720 \u4EE5\u4E0A\uFF0C\u7EA6 5 \u79D2\uFF0816 fps \u65F6 81 \u5E27\uFF09\uFF0C\u8D1F\u9762\u63D0\u793A\u8BCD\u653E\u8FDB\u8D1F\u9762\u8282\u70B9\uFF0C\u6BCF\u6BB5\u7528\u540C\u4E00\u5957\u53C2\u6570\u3002",
197
+ how: "\u7528\u4F60\u7684\u56FE\u751F\u89C6\u9891\u5DE5\u4F5C\u6D41\u4E00\u6BB5\u6BB5\u8DD1\uFF0C\u6216\u8005\u7528\u811A\u672C\u8BFB\u300CComfyUI\u751F\u6210\u6E05\u5355.json\u300D\uFF08\u6BCF\u6BB5\u7684\u9996\u5E27\u3001\u63D0\u793A\u8BCD\u3001\u8D1F\u9762\u63D0\u793A\u8BCD\u3001\u4FDD\u5B58\u8DEF\u5F84\uFF09\u6279\u91CF\u8DD1\u3002\u8F93\u51FA\u5B58\u6210\u6E05\u5355\u91CC\u7684\u6587\u4EF6\u540D\u3002"
198
+ },
199
+ jimeng: { name: "\u5373\u68A6", label: "\u5373\u68A6\uFF08\u7F51\u9875 / App\uFF09", settings: "\u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C5 \u79D2\u3002", how: `\u5728\u5373\u68A6\u91CC${WEB_HOW}` },
200
+ kling: { name: "\u53EF\u7075", label: "\u53EF\u7075\uFF08\u7F51\u9875 / App\uFF09", settings: "\u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C5 \u79D2\u3002", how: `\u5728\u53EF\u7075\u91CC${WEB_HOW}` },
201
+ hailuo: { name: "\u6D77\u87BA AI", label: "\u6D77\u87BA AI\uFF08MiniMax \u7684\u7F51\u9875\u7248\uFF09", settings: "\u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C6 \u79D2\u3002", how: `\u5728\u6D77\u87BA AI \u91CC${WEB_HOW}` },
202
+ wanx: { name: "\u901A\u4E49\u4E07\u76F8", label: "\u901A\u4E49\u4E07\u76F8\uFF08\u7F51\u9875 / \u963F\u91CC\u4E91\u767E\u70BC\uFF09", settings: "\u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C5 \u79D2\u3002", how: `\u5728\u901A\u4E49\u4E07\u76F8\u91CC${WEB_HOW}` },
203
+ vidu: { name: "Vidu", label: "Vidu\uFF08\u7F51\u9875\uFF09", settings: "\u56FE\u751F\u89C6\u9891\uFF0C1080P\uFF0C4\u20135 \u79D2\u3002", how: `\u5728 Vidu \u91CC${WEB_HOW}` }
204
+ };
205
+ var MANUAL_LABEL = "\u624B\u5DE5\u586B\u5199\uFF08\u522B\u7684\u5DE5\u5177\uFF0C\u81EA\u5DF1\u5199\u540D\u5B57\u548C\u8BBE\u7F6E\uFF09";
206
+ function generatorInfo(project) {
207
+ if (project.generator !== "manual" && project.generator in GENERATORS) return GENERATORS[project.generator];
208
+ const name = project.custom?.name.trim() || "\u4F60\u7528\u7684\u5DE5\u5177";
209
+ const settings = project.custom?.settings.trim() || "\u56FE\u751F\u89C6\u9891\uFF0C\u548C\u9996\u5E27\u540C\u6BD4\u4F8B\uFF0C1080P\uFF0C5\u20136 \u79D2\uFF0C\u6BCF\u6BB5\u7528\u540C\u4E00\u5957\u8BBE\u7F6E\u3002";
210
+ return { name, label: MANUAL_LABEL, settings, how: `\u5728${name}\u91CC${WEB_HOW}` };
211
+ }
212
+ var PRESETS = [
213
+ { role: "idle", n: 1, required: true, action: "\u5B89\u9759\u5730{pose}\uFF0C\u81EA\u7136\u547C\u5438\uFF0C\u5076\u5C14\u7728\u773C\uFF0C\u5934\u6781\u8F7B\u5FAE\u5730\u6643\u52A8\uFF0C\u5634\u5DF4\u95ED\u5408\uFF0C\u6D45\u6D45\u5730\u7B11" },
214
+ { role: "talk", n: 1, required: true, action: "\u770B\u7740\u955C\u5934\u6E29\u67D4\u5730\u8BF4\u8BDD\uFF0C\u5634\u5DF4\u81EA\u7136\u6301\u7EED\u5F00\u5408\uFF0C\u8F7B\u8F7B\u70B9\u5934\uFF0C\u624B\u6709\u5F88\u5C0F\u7684\u52A8\u4F5C\u3002\u6700\u540E\u4E00\u79D2\u95ED\u5634\u56DE\u5230\u539F\u59FF\u52BF" },
215
+ { role: "talk", n: 2, required: true, action: "\u770B\u7740\u955C\u5934\u8BF4\u8BDD\uFF0C\u6362\u4E00\u79CD\u8282\u594F\uFF0C\u7565\u5E26\u7B11\u610F\uFF0C\u5634\u5DF4\u81EA\u7136\u6301\u7EED\u5F00\u5408\uFF0C\u4E00\u53EA\u624B\u8F7B\u8F7B\u62AC\u8D77\u53C8\u653E\u4E0B\u3002\u6700\u540E\u4E00\u79D2\u95ED\u5634\u56DE\u5230\u539F\u59FF\u52BF" },
216
+ { role: "idle", n: 2, action: "\u5B89\u9759\u5730{pose}\uFF0C\u8F7B\u8F7B\u628A\u4E00\u7F15\u5934\u53D1\u62E8\u5230\u8033\u540E\uFF0C\u7136\u540E\u6062\u590D\u539F\u59FF\u52BF" },
217
+ { role: "listen", n: 1, action: "\u8BA4\u771F\u5730\u542C\uFF0C\u5634\u5DF4\u95ED\u7740\uFF0C\u8F7B\u8F7B\u70B9\u5934\u4E24\u6B21\uFF0C\u773C\u795E\u4E13\u6CE8\u770B\u7740\u955C\u5934" },
218
+ { role: "think", n: 1, action: "\u773C\u775B\u770B\u5411\u53F3\u4E0A\u65B9\u50CF\u5728\u601D\u8003\uFF0C\u98DF\u6307\u8F7B\u78B0\u4E0B\u5DF4\uFF0C\u7136\u540E\u653E\u4E0B\u624B\uFF0C\u89C6\u7EBF\u56DE\u5230\u955C\u5934" },
219
+ { role: "happy", n: 1, action: "\u5F00\u5FC3\u5730\u7B11\u51FA\u6765\uFF0C\u773C\u775B\u5F2F\u8D77\uFF0C\u80A9\u8180\u8F7B\u5FAE\u8038\u52A8\uFF0C\u7136\u540E\u6062\u590D\u6D45\u7B11" },
220
+ { role: "shy", n: 1, action: "\u5BB3\u7F9E\u5730\u4F4E\u5934\uFF0C\u624B\u6307\u5377\u4E00\u4E0B\u53D1\u68A2\uFF0C\u518D\u62AC\u773C\u770B\u955C\u5934\uFF0C\u6062\u590D\u539F\u59FF\u52BF" },
221
+ { role: "wave", n: 1, action: "\u770B\u5230\u4F60\u5F88\u5F00\u5FC3\uFF0C\u62AC\u8D77\u53F3\u624B\u671D\u955C\u5934\u6325\u624B\uFF0C\u7136\u540E\u653E\u4E0B\u624B\u6062\u590D\u539F\u59FF\u52BF" },
222
+ { role: "touched", n: 1, action: "\u50CF\u88AB\u8F7B\u8F7B\u6233\u4E86\u4E00\u4E0B\uFF0C\u5FAE\u5FAE\u4E00\u60CA\uFF0C\u7136\u540E\u770B\u7740\u955C\u5934\u7B11\uFF0C\u6062\u590D\u539F\u59FF\u52BF" },
223
+ { role: "sleepy", n: 1, action: "\u6253\u4E00\u4E2A\u5C0F\u54C8\u6B20\uFF0C\u7528\u624B\u80CC\u8F7B\u63C9\u773C\u775B\uFF0C\u7136\u540E\u5BF9\u955C\u5934\u7B11\u4E00\u4E0B\uFF0C\u6062\u590D\u539F\u59FF\u52BF" }
224
+ ];
225
+ function presetClips(pose) {
226
+ return PRESETS.map((p) => ({
227
+ file: `${pose}_${p.role}_${String(p.n).padStart(2, "0")}`,
228
+ action: p.action.replaceAll("{pose}", POSE_WORDS[pose]),
229
+ ...p.required === true ? { required: true } : { off: true }
230
+ }));
231
+ }
232
+ function customClip(pose, existing, action) {
233
+ let n = 1;
234
+ while (existing.some((c) => c.file === `${pose}_act${n}_01`)) n++;
235
+ return { file: `${pose}_act${n}_01`, action: action.trim() };
236
+ }
237
+ function newScene(id, name, pose = "stand") {
238
+ return { id, name, place: "", pose, clips: presetClips(pose) };
239
+ }
240
+ function rules(project, kind, scene) {
241
+ const common = [
242
+ "\u56FA\u5B9A\u673A\u4F4D\uFF0C\u5355\u4E00\u8FDE\u7EED\u955C\u5934\uFF0C\u4E0D\u63A8\u62C9\u6447\u79FB\uFF0C\u4E0D\u53D8\u7126\uFF0C\u4E0D\u5207\u955C",
243
+ "\u8138\u3001\u4E94\u5B98\u3001\u53D1\u578B\u3001\u5986\u5BB9\u3001\u670D\u88C5\u3001\u8EAB\u6750\u6BD4\u4F8B\u5168\u7A0B\u5B8C\u5168\u4E0D\u53D8",
244
+ "\u52A8\u4F5C\u81EA\u7136\u3001\u514B\u5236\u3001\u5E45\u5EA6\u5C0F\uFF0C\u771F\u5B9E\u7684\u547C\u5438\u548C\u7728\u773C",
245
+ "\u753B\u9762\u4E2D\u4E0D\u51FA\u73B0\u4EFB\u4F55\u6587\u5B57\u3001\u5B57\u5E55\u3001\u6C34\u5370\u3001Logo\uFF0C\u65E0\u80CC\u666F\u97F3\u4E50",
246
+ "\u89C6\u9891\u5F00\u59CB\u548C\u7ED3\u675F\u65F6\uFF0C\u4EBA\u7269\u59FF\u52BF\u4E0E\u53C2\u8003\u56FE\u5B8C\u5168\u4E00\u81F4"
247
+ ];
248
+ if (kind === "desk") {
249
+ const color = screenColor(project.card);
250
+ return [
251
+ `\u53C2\u8003\u56FE\u4E2D\u7684\u540C\u4E00\u4F4D${subject(project.card)}\uFF0C\u5168\u8EAB\u7AD9\u7ACB\uFF0C${color.name}\u80CC\u666F`,
252
+ ...common,
253
+ `\u80CC\u666F\u59CB\u7EC8\u662F\u5747\u5300\u7684${color.name}\uFF08${color.hex}\uFF09\uFF0C\u65E0\u5730\u9762\u3001\u65E0\u9634\u5F71\u3001\u65E0\u6E10\u53D8\u3001\u65E0\u4EFB\u4F55\u7269\u4F53\uFF0C\u5149\u7167\u4E0D\u53D8`,
254
+ "\u53CC\u811A\u59CB\u7EC8\u7AD9\u5728\u539F\u5730\uFF0C\u8EAB\u4F53\u59CB\u7EC8\u5B8C\u6574\u5730\u5728\u753B\u9762\u5185\uFF08\u5934\u9876\u548C\u811A\u90FD\u4E0D\u51FA\u753B\uFF09"
255
+ ].join("\u3002") + "\u3002";
256
+ }
257
+ return [
258
+ `\u53C2\u8003\u56FE\u4E2D\u7684\u540C\u4E00\u4F4D${subject(project.card)}\uFF0C\u5728${scene?.name ?? "\u573A\u666F"}\u91CC${POSE_WORDS[scene?.pose ?? "stand"]}`,
259
+ ...common,
260
+ "\u80CC\u666F\u4E0E\u53C2\u8003\u56FE\u5B8C\u5168\u4E00\u81F4\u4E14\u9759\u6B62\uFF0C\u5149\u7167\u4E0D\u53D8",
261
+ "\u4EBA\u7269\u4E0D\u79BB\u5F00\u539F\u4F4D\uFF0C\u8EAB\u4F53\u59CB\u7EC8\u5728\u753B\u9762\u5185"
262
+ ].join("\u3002") + "\u3002";
263
+ }
264
+ function clipPrompt(project, kind, clip, scene) {
265
+ const prefix = project.generator === "manual" ? project.custom?.prefix.trim() ?? "" : "";
266
+ return `${prefix === "" ? "" : `${prefix.replace(/[,。,.\s]+$/, "")}\u3002`}${rules(project, kind, scene)}\u5979\u662F\uFF1A${describe(project.card)}\u3002${clip.action}\u3002`;
267
+ }
268
+ function referencePrompt(project) {
269
+ const color = screenColor(project.card);
270
+ return [
271
+ `${describe(project.card)}`,
272
+ "\u5168\u8EAB\u6B63\u9762\u7AD9\u7ACB\uFF0C\u53CC\u624B\u81EA\u7136\u5782\u5728\u8EAB\u4FA7\uFF0C\u8868\u60C5\u6E29\u548C\uFF0C\u770B\u7740\u955C\u5934",
273
+ `${color.name}\u80CC\u666F\uFF08${color.hex}\uFF09\uFF0C\u80CC\u666F\u5B8C\u5168\u5747\u5300\uFF0C\u65E0\u5730\u9762\u3001\u65E0\u9634\u5F71\u3001\u65E0\u6E10\u53D8\u3001\u65E0\u4EFB\u4F55\u7269\u4F53`,
274
+ "\u4EBA\u7269\u5C45\u4E2D\uFF0C\u5934\u9876\u548C\u811A\u4E0B\u5404\u7559\u4E00\u70B9\u7A7A\uFF0C\u7AD6\u7248 9:16\uFF0C1080\xD71920",
275
+ styleWords(project.card)
276
+ ].join("\u3002") + "\u3002";
277
+ }
278
+ function scenePhotoPrompt(project, scene, screen) {
279
+ const size = screen === void 0 ? "\u6A2A\u7248 16:10\uFF0C2560 \u5BBD\u4EE5\u4E0A" : `\u6A2A\u7248\uFF0C\u548C\u5C4F\u5E55\u4E00\u6837\u7684\u6BD4\u4F8B\uFF0C${screen.width}\xD7${screen.height}`;
280
+ return [
281
+ `${scene.place.trim() === "" ? scene.name : scene.place.trim()}`,
282
+ `\u753B\u9762\u91CC\u6709\u4E00\u4F4D${describe(project.card)}\uFF0C${POSE_WORDS[scene.pose]}\uFF0C\u770B\u7740\u955C\u5934`,
283
+ "\u7528\u53C2\u8003\u56FE\u4FDD\u6301\u4EBA\u7269\u5B8C\u5168\u4E00\u81F4\uFF08\u540C\u4E00\u5F20\u8138\u3001\u540C\u6837\u7684\u53D1\u578B\u548C\u8863\u670D\uFF09",
284
+ "\u4EBA\u7269\u5B8C\u6574\u5730\u5728\u753B\u9762\u4E2D\u95F4\u504F\u4E00\u4FA7\uFF0C\u5934\u9876\u548C\u811A\u4E0B\u7559\u51FA\u7A7A\u95F4\uFF0C\u4EBA\u7269\u9AD8\u5EA6\u7EA6\u5360\u753B\u9762\u7684\u516D\u6210",
285
+ size,
286
+ styleWords(project.card)
287
+ ].join("\u3002") + "\u3002";
288
+ }
289
+ var EXAMPLE_SLOTS = {
290
+ look: [{ file: "look.png", label: "\u505A\u597D\u7684\u5979\uFF08\u89D2\u8272\u5361\u8FD9\u6837\u586B\u51FA\u6765\u7684\u6837\u5B50\uFF09" }],
291
+ reference: [{ file: "reference.png", label: "\u53C2\u8003\u56FE\uFF1A\u7EAF\u8272\u80CC\u666F\u3001\u5168\u8EAB\u3001\u6B63\u9762\u7AD9\u7ACB" }],
292
+ desk: [{ file: "desk.mp4", label: "\u684C\u5BA0\u7247\u6BB5\uFF08\u7EFF\u5E55\u539F\u7247\uFF09" }, { file: "desk-keyed.png", label: "\u62A0\u597D\u540E\u6D6E\u5728\u684C\u9762\u4E0A" }],
293
+ scene: [
294
+ { file: "scene-plate.png", label: "\u573A\u666F\u7684\u5168\u5C4F\u7167\u7247" },
295
+ { file: "scene-crop.png", label: "\u6846\u597D\u7684\u4EBA\u7269\u533A\uFF08crop.png\uFF09" },
296
+ { file: "scene.mp4", label: "\u573A\u666F\u7247\u6BB5" }
297
+ ],
298
+ import: [{ file: "result.png", label: "\u5BFC\u5165\u540E\u684C\u9762\u4E0A\u7684\u6837\u5B50" }]
299
+ };
300
+ var EXAMPLE_FILES = new Set(Object.values(EXAMPLE_SLOTS).flatMap((slots) => slots.map((s) => s.file)));
301
+
302
+ // src/client/Workshop.tsx
303
+ var import_jsx_runtime = require("react/jsx-runtime");
304
+ var OWN = "__own__";
305
+ var copy = (text) => void navigator.clipboard?.writeText(text);
306
+ function Workshop({ state, apply: apply2 }) {
307
+ const [items, setItems] = (0, import_react.useState)([]);
308
+ const [open, setOpen] = (0, import_react.useState)();
309
+ const [draft, setDraft] = (0, import_react.useState)();
310
+ const [dirty, setDirty] = (0, import_react.useState)(false);
311
+ const [newName, setNewName] = (0, import_react.useState)("");
312
+ const [note, setNote] = (0, import_react.useState)();
313
+ const reload = (0, import_react.useCallback)(async () => {
314
+ const result = await herApi.workshopList();
315
+ if (result.ok) setItems(result.projects);
316
+ }, []);
317
+ (0, import_react.useEffect)(() => {
318
+ void reload();
319
+ const timer = setInterval(() => void reload(), 4e3);
320
+ return () => clearInterval(timer);
321
+ }, [reload]);
322
+ const status = items.find((i) => i.project.id === open)?.status;
323
+ const choose = (item) => {
324
+ setOpen(item.project.id);
325
+ setDraft(structuredClone(item.project));
326
+ setDirty(false);
327
+ setNote(void 0);
328
+ };
329
+ const edit = (change) => {
330
+ setDraft((current) => {
331
+ if (current === void 0) return current;
332
+ const next = structuredClone(current);
333
+ change(next);
334
+ return next;
335
+ });
336
+ setDirty(true);
337
+ };
338
+ const report = (result, done) => {
339
+ setNote(result.ok ? { kind: "ok", text: done } : { kind: "error", text: result.message });
340
+ return result.ok;
341
+ };
342
+ const create = async () => {
343
+ const result = await herApi.workshopNew(newName.trim());
344
+ if (report(result, "\u5EFA\u597D\u4E86\uFF0C\u6587\u4EF6\u5939\u5728\u300C\u6587\u7A3F / dsh-her \u7D20\u6750\u300D\u91CC") && result.ok) {
345
+ setNewName("");
346
+ await reload();
347
+ choose(result);
348
+ }
349
+ };
350
+ const save = async () => {
351
+ if (draft === void 0) return false;
352
+ const result = await herApi.workshopSave(draft);
353
+ if (!report(result, "\u5DF2\u4FDD\u5B58\uFF1A\u63D0\u793A\u8BCD\u90FD\u5199\u8FDB\u6587\u4EF6\u5939\u4E86")) return false;
354
+ setDirty(false);
355
+ await reload();
356
+ return true;
357
+ };
358
+ const frame = async (scene) => {
359
+ if (dirty && !await save()) return;
360
+ report(await herApi.workshopFrame(draft.id, scene.id), '\u5DF2\u5728\u684C\u9762\u4E0A\u6253\u5F00"\u505A\u573A\u666F"\u7A97\u53E3\uFF1A\u6846\u597D\u5979\u7684\u6D3B\u52A8\u8303\u56F4\uFF0C\u70B9\u4FDD\u5B58');
361
+ };
362
+ const importLook = async () => {
363
+ if (dirty && !await save()) return;
364
+ if (await apply2(herApi.workshopImport(draft.id))) setNote({ kind: "ok", text: "\u5F00\u59CB\u5BFC\u5165\uFF0C\u8FDB\u5EA6\u5728\u4E0B\u9762" });
365
+ };
366
+ const [unfolded, setUnfolded] = (0, import_react.useState)(() => loadFolds());
367
+ const toggle = (card, on = !unfolded[card]) => setUnfolded((current) => {
368
+ const next = { ...current, [card]: on };
369
+ storeFolds(next);
370
+ return next;
371
+ });
372
+ const saveStep = async (card) => {
373
+ if (dirty && !await save()) return;
374
+ const at = CARDS.indexOf(card);
375
+ setUnfolded((current) => {
376
+ const next = { ...current, [card]: false, ...at + 1 < CARDS.length ? { [CARDS[at + 1]]: true } : {} };
377
+ storeFolds(next);
378
+ return next;
379
+ });
380
+ if (!dirty) setNote({ kind: "ok", text: "\u8FD9\u4E00\u6B65\u5DF2\u7ECF\u4FDD\u5B58\u8FC7\u4E86" });
381
+ };
382
+ const [examples, setExamples] = (0, import_react.useState)([]);
383
+ (0, import_react.useEffect)(() => {
384
+ void herApi.workshopExamples().then((r) => {
385
+ if (r.ok) setExamples(r.examples);
386
+ });
387
+ }, []);
388
+ const [withMedia, setWithMedia] = (0, import_react.useState)(false);
389
+ const exportProject = async () => {
390
+ if (dirty && !await save()) return;
391
+ setNote({ kind: "ok", text: "\u5728\u684C\u9762\u4E0A\u5F39\u51FA\u7684\u7A97\u53E3\u91CC\u9009\u4E00\u4E2A\u6587\u4EF6\u5939\u2026" });
392
+ const result = await herApi.workshopExport(draft.id, withMedia);
393
+ if (!result.ok) report(result, "");
394
+ else if ("cancelled" in result) setNote(void 0);
395
+ else setNote({ kind: "ok", text: `\u5DF2\u5BFC\u51FA ${result.files} \u4E2A\u6587\u4EF6\u5230 ${result.dir}` });
396
+ };
397
+ const importProject = async () => {
398
+ setNote({ kind: "ok", text: "\u5728\u684C\u9762\u4E0A\u5F39\u51FA\u7684\u7A97\u53E3\u91CC\u9009\u4E00\u4E2A\u5F62\u8C61\u9879\u76EE\u7684\u6587\u4EF6\u5939\uFF08\u6216\u91CC\u9762\u7684 \u5F62\u8C61.json\uFF09\u2026" });
399
+ const result = await herApi.workshopImportProject();
400
+ if (!result.ok) report(result, "");
401
+ else if ("cancelled" in result) setNote(void 0);
402
+ else {
403
+ await reload();
404
+ choose(result);
405
+ setNote({ kind: "ok", text: `\u5DF2\u5BFC\u5165\u300C${result.project.name}\u300D\uFF0C\u653E\u5728 ${result.status.dir}` });
406
+ }
407
+ };
408
+ const screen = state.body.screens?.[0];
409
+ const screenPx = screen === void 0 ? void 0 : { width: Math.round(screen.w * screen.scale), height: Math.round(screen.h * screen.scale) };
410
+ const desk = status?.parts.find((p) => p.folder === DESK_DIR);
411
+ const fold = (card, title, summary, done, body, saveLabel = "\u4FDD\u5B58\u8FD9\u4E00\u6B65") => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Fold, { card, title, summary, done, open: unfolded[card], onToggle: () => toggle(card), children: [
412
+ body,
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleStrip, { examples: examples.filter((e) => e.card === card) }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn primary", onClick: () => void saveStep(card), children: saveLabel }),
416
+ dirty && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-warn", children: "\u6709\u6539\u52A8\u8FD8\u6CA1\u4FDD\u5B58" })
417
+ ] })
418
+ ] });
419
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
420
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-card", children: [
421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: "\u5F62\u8C61\u5DE5\u574A" }),
422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "her-muted", children: "\u505A\u4E00\u4E2A\u4F60\u81EA\u5DF1\u7684\u5979\uFF1A\u9009\u597D\u5979\u7684\u6837\u5B50\u3001\u5979\u4F4F\u5728\u54EA\u4E9B\u573A\u666F\u91CC\uFF0C\u8FD9\u91CC\u4F1A\u628A\u6BCF\u4E00\u5F20\u56FE\u3001\u6BCF\u4E00\u6BB5\u89C6\u9891\u7684\u63D0\u793A\u8BCD\u90FD\u5199\u597D\uFF0C\u653E\u8FDB\u300C\u6587\u7A3F / dsh-her \u7D20\u6750\u300D\u91CC\u7684\u4E00\u4E2A\u6587\u4EF6\u5939\u3002\u4F60\u7528\u5373\u68A6\u3001\u53EF\u7075\u3001MiniMax\u3001ComfyUI \u8FD9\u4E9B\u5DE5\u5177\u751F\u6210\uFF0C\u653E\u56DE\u6587\u4EF6\u5939\uFF0C\u518D\u4E00\u952E\u5BFC\u5165\u3002" }),
423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "her-grid", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: `her-choice ${item.project.id === open ? "on" : ""}`, onClick: () => choose(item), children: [
424
+ item.project.name,
425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: progressText(item.status) })
426
+ ] }, item.project.id)) }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
428
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { flex: 1, minWidth: 160 }, value: newName, placeholder: "\u65B0\u5F62\u8C61\u7684\u540D\u5B57\uFF0C\u6BD4\u5982\uFF1A\u5C45\u5BB6\u773C\u955C", onChange: (e) => setNewName(e.target.value) }),
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", disabled: newName.trim() === "", onClick: () => void create(), children: "\u65B0\u5EFA" }),
430
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", onClick: () => void importProject(), children: "\u5BFC\u5165\u5DF2\u6709\u9879\u76EE\u2026" })
431
+ ] }),
432
+ draft !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "her-btn", onClick: () => void exportProject(), children: [
434
+ "\u628A\u300C",
435
+ draft.name,
436
+ "\u300D\u5BFC\u51FA\u5230\u6587\u4EF6\u5939\u2026"
437
+ ] }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: withMedia, onChange: (e) => setWithMedia(e.target.checked) }),
440
+ " \u8FDE\u540C\u56FE\u7247\u548C\u89C6\u9891"
441
+ ] }),
442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-muted", children: '\u4E0D\u52FE\u53EA\u5BFC\u51FA\u8BBE\u7F6E\uFF08\u5F62\u8C61.json \u548C\u6240\u6709\u63D0\u793A\u8BCD\uFF09\uFF0C\u5F88\u5C0F\uFF1B\u5BFC\u51FA\u7684\u6587\u4EF6\u5939\u5728\u522B\u7684\u7535\u8111\u4E0A\u7528"\u5BFC\u5165\u5DF2\u6709\u9879\u76EE"\u6253\u5F00\u3002' })
443
+ ] }),
444
+ draft === void 0 && note !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: note.kind === "error" ? "her-danger" : "her-ok", children: note.text })
445
+ ] }),
446
+ draft !== void 0 && status !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
447
+ fold("look", "1. \u5979\u957F\u4EC0\u4E48\u6837", describe(draft.card), true, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
449
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 160 }, children: [
450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u5F62\u8C61\u540D\u5B57" }),
451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: draft.name, onChange: (e) => edit((p) => {
452
+ p.name = e.target.value;
453
+ }) })
454
+ ] }),
455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 160 }, children: [
456
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u7528\u4EC0\u4E48\u751F\u6210\u89C6\u9891" }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "her-select", value: draft.generator, onChange: (e) => edit((p) => {
458
+ p.generator = e.target.value;
459
+ if (p.generator === "manual") p.custom ??= { name: "", settings: "", prefix: "" };
460
+ }), children: [
461
+ Object.keys(GENERATORS).map((id) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: id, children: GENERATORS[id].label }, id)),
462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "manual", children: MANUAL_LABEL })
463
+ ] })
464
+ ] })
465
+ ] }),
466
+ draft.generator === "manual" && draft.custom !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, minWidth: 140 }, children: [
468
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u5DE5\u5177\u540D\u5B57" }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: draft.custom.name, placeholder: "\u6BD4\u5982 Runway\u3001PixVerse\u3001\u81EA\u5DF1\u7684\u5DE5\u4F5C\u6D41", onChange: (e) => edit((p) => {
470
+ p.custom.name = e.target.value;
471
+ }) })
472
+ ] }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 2, minWidth: 220 }, children: [
474
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u7EDF\u4E00\u8BBE\u7F6E\uFF08\u5199\u8FDB\u6BCF\u4E2A\u63D0\u793A\u8BCD\u6587\u4EF6\u7684\u5F00\u5934\uFF09" }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: draft.custom.settings, placeholder: generatorInfo({ generator: "manual" }).settings, onChange: (e) => edit((p) => {
476
+ p.custom.settings = e.target.value;
477
+ }) })
478
+ ] }),
479
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: "1 1 100%" }, children: [
480
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u6BCF\u6BB5\u63D0\u793A\u8BCD\u524D\u9762\u52A0\uFF08\u53EF\u4EE5\u4E0D\u586B\uFF1B\u6BD4\u5982 LoRA \u89E6\u53D1\u8BCD\u3001\u753B\u98CE\u8BCD\uFF09" }),
481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: draft.custom.prefix, onChange: (e) => edit((p) => {
482
+ p.custom.prefix = e.target.value;
483
+ }) })
484
+ ] })
485
+ ] }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "her-muted", children: [
487
+ "\u7EDF\u4E00\u8BBE\u7F6E\uFF1A",
488
+ generatorInfo(draft).settings
489
+ ] }),
490
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardEditor, { card: draft.card, onChange: (card) => edit((p) => {
491
+ p.card = card;
492
+ }) }),
493
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u89D2\u8272\u63CF\u8FF0\uFF08\u6BCF\u5F20\u56FE\u3001\u6BCF\u6BB5\u89C6\u9891\u90FD\u7528\u8FD9\u4E00\u53E5\uFF0C\u5979\u624D\u662F\u540C\u4E00\u4E2A\u4EBA\uFF1B\u53EF\u4EE5\u76F4\u63A5\u6539\uFF09" }),
494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "her-textarea", style: { minHeight: 60 }, value: draft.card.sentence, placeholder: describe({ ...draft.card, sentence: "" }), onChange: (e) => edit((p) => {
495
+ p.card.sentence = e.target.value;
496
+ }) })
497
+ ] })),
498
+ fold("reference", '2. \u53C2\u8003\u56FE\uFF08\u5979\u7684"\u8EAB\u4EFD\u8BC1"\uFF09', status.reference ? "\u2713 \u5DF2\u6709 \u53C2\u8003\u56FE.png" : "\u8FD8\u6CA1\u6709 \u53C2\u8003\u56FE.png", status.reference, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
500
+ status.reference ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-ok", children: "\u2713 \u5DF2\u6709 \u53C2\u8003\u56FE.png" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-warn", children: "\u8FD8\u6CA1\u6709 \u53C2\u8003\u56FE.png" }),
501
+ "\uFF1A\u7EAF\u8272\u80CC\u666F\u3001\u5168\u8EAB\u3001\u6B63\u9762\u7AD9\u7ACB\u3002\u7528\u6587\u751F\u56FE\u5DE5\u5177\u6309\u4E0B\u9762\u7684\u63D0\u793A\u8BCD\u751F\u6210\uFF0C\u5B58\u6210\u300C\u53C2\u8003\u56FE.png\u300D\u653E\u8FDB\u9879\u76EE\u6587\u4EF6\u5939\u3002\u4E4B\u540E\u6BCF\u4E2A\u573A\u666F\u7684\u7167\u7247\u90FD\u8981\u7528\u5B83\u5F53\u53C2\u8003\u56FE\u3002"
502
+ ] }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PromptBox, { text: referencePrompt(draft) })
504
+ ] })),
505
+ fold("desk", "3. \u684C\u5BA0\uFF08\u6D6E\u5728\u6700\u4E0A\u5C42\u65F6\u7684\u5979\uFF09", !draft.desk.on ? "\u4E0D\u8981\u684C\u5BA0" : desk === void 0 ? "\u8FD8\u6CA1\u4FDD\u5B58" : `${desk.made}/${desk.planned} \u6BB5${desk.enough ? "\uFF0C\u591F\u7528\u4E86" : ""}`, !draft.desk.on || desk?.enough === true, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: draft.desk.on, onChange: (e) => edit((p) => {
508
+ p.desk.on = e.target.checked;
509
+ }) }),
510
+ " \u8981\u684C\u5BA0\uFF1A\u7EFF\u5E55\u5168\u8EAB\u89C6\u9891\uFF0C\u5BFC\u5165\u65F6\u62A0\u6389\u80CC\u666F\uFF0C\u5979\u6D6E\u5728\u6240\u6709\u7A97\u53E3\u4E0A\u9762"
511
+ ] }),
512
+ draft.desk.on && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PartStatusLine, { status: desk }),
514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "her-muted", children: "\u9996\u5E27\u7528\u300C\u53C2\u8003\u56FE.png\u300D\uFF0C\u751F\u6210\u7AD6\u7248 9:16\u30011080P\u30015\u20136 \u79D2\u3002" }),
515
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClipList, { project: draft, kind: "desk", clips: draft.desk.clips, pose: "stand", onChange: (clips) => edit((p) => {
516
+ p.desk.clips = clips;
517
+ }) })
518
+ ] })
519
+ ] })),
520
+ fold("scene", "4. \u573A\u666F\uFF08\u4F4F\u5728\u58C1\u7EB8\u91CC\u3001\u666E\u901A\u7A97\u53E3\u65F6\u7684\u5979\uFF09", draft.scenes.length === 0 ? "\u8FD8\u6CA1\u6709\u573A\u666F" : draft.scenes.map((sc) => {
521
+ const part = status.parts.find((p) => p.folder === sceneDir(sc));
522
+ return `${sc.name} ${part === void 0 ? "\u672A\u4FDD\u5B58" : `${part.made}/${part.planned}${part.framed === true ? "" : part.photo === true ? "\xB7\u672A\u6846" : "\xB7\u7F3A\u7167\u7247"}`}`;
523
+ }).join(" \xB7 "), draft.scenes.length > 0 && draft.scenes.every((sc) => {
524
+ const part = status.parts.find((p) => p.folder === sceneDir(sc));
525
+ return part?.enough === true && part.framed === true;
526
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "her-muted", children: [
528
+ '\u6BCF\u4E2A\u573A\u666F\u662F\u4E00\u5F20\u5168\u5C4F\u7167\u7247\uFF0C\u5979\u5728\u7167\u7247\u91CC\u7684\u4E00\u5757\u5730\u65B9\u52A8\u3002\u81EA\u52A8\u6362\u573A\u666F\u6309\u4E0B\u9762\u7684\u65F6\u95F4\u548C"\u4F60\u5728\u4E0D\u5728"\u6765\u9009\u3002',
529
+ screenPx === void 0 ? "" : `\u4F60\u7684\u5C4F\u5E55\u662F ${screenPx.width}\xD7${screenPx.height}\uFF0C\u7167\u7247\u5C31\u505A\u8FD9\u4E48\u5927\u3002`
530
+ ] }),
531
+ draft.scenes.map((scene, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
532
+ SceneEditor,
533
+ {
534
+ project: draft,
535
+ scene,
536
+ isDefault: (draft.default ?? draft.scenes[0]?.id) === scene.id,
537
+ status: status.parts.find((p) => p.folder === sceneDir(scene)),
538
+ screen: screenPx,
539
+ onChange: (change) => edit((p) => {
540
+ change(p.scenes[i]);
541
+ }),
542
+ onDefault: () => edit((p) => {
543
+ p.default = scene.id;
544
+ }),
545
+ onRemove: () => edit((p) => {
546
+ p.scenes.splice(i, 1);
547
+ }),
548
+ onFrame: () => void frame(scene)
549
+ },
550
+ scene.id
551
+ )),
552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", onClick: () => edit((p) => {
553
+ p.scenes.push(newScene(`s${Date.now().toString(36)}`, `\u573A\u666F${p.scenes.length + 1}`));
554
+ }), children: "\u52A0\u4E00\u4E2A\u573A\u666F" })
555
+ ] })),
556
+ fold("import", "5. \u751F\u6210\u3001\u5BFC\u5165", status.importable ? "\u53EF\u4EE5\u5BFC\u5165\u4E86" : "\u7247\u6BB5\u8FD8\u4E0D\u591F", false, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
557
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ol", { className: "her-muted", style: { margin: "4px 0", paddingLeft: 18 }, children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
559
+ '\u6BCF\u4E00\u6B65\u586B\u5B8C\u70B9"\u4FDD\u5B58\u8FD9\u4E00\u6B65"\uFF1A\u63D0\u793A\u8BCD\u5199\u8FDB\u9879\u76EE\u6587\u4EF6\u5939\uFF0C\u6240\u6709\u7247\u6BB5\u5217\u5728\u300C\u751F\u6210\u6E05\u5355.md\u300D',
560
+ draft.generator === "minimax" ? "\uFF0C\u8FD8\u6709\u7ED9 MiniMax Code \u7684\u300CMiniMax\u751F\u6210\u4EFB\u52A1.md\u300D" : draft.generator === "comfyui" ? "\uFF0C\u8FD8\u6709\u7ED9\u811A\u672C\u7528\u7684\u300CComfyUI\u751F\u6210\u6E05\u5355.json\u300D" : "",
561
+ "\u3002"
562
+ ] }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: generatorInfo(draft).how }),
564
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: '\u573A\u666F\u7684\u7167\u7247\u653E\u597D\u540E\u70B9"\u6846\u4EBA\u7269\u533A"\uFF1B\u7247\u6BB5\u9F50\u4E86\u5C31"\u5BFC\u5165\u8FD9\u4E2A\u5F62\u8C61"\u3002\u6BCF\u90E8\u5206\u81F3\u5C11\u8981 3 \u6BB5\uFF08\u5F85\u673A\u3001\u8BF4\u8BDD \xD72\uFF09\uFF0C\u5176\u4ED6\u7684\u4EE5\u540E\u8865\uFF0C\u518D\u5BFC\u5165\u4E00\u6B21\u5C31\u4F1A\u66F4\u65B0\u3002' })
565
+ ] }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", onClick: () => void herApi.workshopOpen(draft.id).then((r) => report(r, "\u5DF2\u6253\u5F00\u6587\u4EF6\u5939")), children: "\u6253\u5F00\u6587\u4EF6\u5939" }),
568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", disabled: !status.importable || state.import.state === "running", onClick: () => void importLook(), children: "\u5BFC\u5165\u8FD9\u4E2A\u5F62\u8C61" })
569
+ ] }),
570
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImportProgress, { state })
571
+ ] }), "\u4FDD\u5B58"),
572
+ note !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: note.kind === "error" ? "her-danger" : "her-ok", children: note.text })
573
+ ] })
574
+ ] });
575
+ }
576
+ var CARDS = ["look", "reference", "desk", "scene", "import"];
577
+ var FOLDS_KEY = "dsh-her.workshop.open";
578
+ function loadFolds() {
579
+ const all = Object.fromEntries(CARDS.map((c) => [c, true]));
580
+ try {
581
+ return { ...all, ...JSON.parse(localStorage.getItem(FOLDS_KEY) ?? "{}") };
582
+ } catch {
583
+ return all;
584
+ }
585
+ }
586
+ function storeFolds(folds) {
587
+ try {
588
+ localStorage.setItem(FOLDS_KEY, JSON.stringify(folds));
589
+ } catch {
590
+ }
591
+ }
592
+ function Fold({ title, summary, done, open, onToggle, children }) {
593
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-card", children: [
594
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "her-fold", onClick: onToggle, "aria-expanded": open, children: [
595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `her-fold-arrow${open ? " open" : ""}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", width: "14", height: "14", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 3.5 10.5 8 6 12.5", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
596
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }),
597
+ !open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `her-fold-sum ${done ? "her-ok" : "her-muted"}`, children: summary })
598
+ ] }),
599
+ open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children })
600
+ ] });
601
+ }
602
+ function ExampleStrip({ examples }) {
603
+ if (examples.length === 0) return null;
604
+ const src = (file) => new URL(`/api/dsh-her/example?f=${encodeURIComponent(file)}`, document.baseURI).href;
605
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-examples", children: [
606
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-label", children: "\u793A\u4F8B" }),
607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "her-example-row", children: examples.map((e) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("figure", { className: "her-example", children: [
608
+ !e.ready ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-example-empty", children: [
609
+ "\u793A\u4F8B\u5F85\u653E",
610
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("br", {}),
611
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("code", { children: [
612
+ "examples/workshop/",
613
+ e.file
614
+ ] })
615
+ ] }) : e.file.endsWith(".mp4") ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { src: src(e.file), muted: true, loop: true, playsInline: true, onMouseEnter: (ev) => void ev.currentTarget.play(), onMouseLeave: (ev) => ev.currentTarget.pause() }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: src(e.file), alt: e.label }),
616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("figcaption", { children: e.label })
617
+ ] }, e.file)) })
618
+ ] });
619
+ }
620
+ function progressText(status) {
621
+ if (status.parts.length === 0) return "\u8FD8\u6CA1\u5F00\u59CB";
622
+ return status.parts.map((p) => `${p.name} ${p.made}/${p.planned}${p.framed === false ? p.photo === true ? "\xB7\u672A\u6846" : "\xB7\u7F3A\u7167\u7247" : ""}`).join(" \xB7 ");
623
+ }
624
+ function PartStatusLine({ status }) {
625
+ if (status === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "her-muted", children: "\u4FDD\u5B58\u540E\u4F1A\u5EFA\u597D\u8FD9\u90E8\u5206\u7684\u6587\u4EF6\u5939\u3002" });
626
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
627
+ "\u5DF2\u6709 ",
628
+ status.made,
629
+ "/",
630
+ status.planned,
631
+ " \u6BB5",
632
+ status.enough ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-ok", children: "\uFF0C\u591F\u7528\u4E86" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-warn", children: "\uFF0C\u6700\u5C11\u8981\u5F85\u673A\u3001\u8BF4\u8BDD \xD72 \u8FD9 3 \u6BB5" }),
633
+ status.photo === false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-warn", children: "\uFF1B\u8FD8\u6CA1\u6709\u7167\u7247 plate.png" }),
634
+ status.photo === true && status.framed === false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-warn", children: "\uFF1B\u7167\u7247\u6709\u4E86\uFF0C\u8FD8\u6CA1\u6846\u4EBA\u7269\u533A" }),
635
+ status.framed === true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-ok", children: "\uFF1B\u4EBA\u7269\u533A\u5DF2\u6846\u597D" })
636
+ ] });
637
+ }
638
+ function PromptBox({ text }) {
639
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", style: { alignItems: "flex-start" }, children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "her-chat", style: { flex: 1, margin: 0, whiteSpace: "pre-wrap" }, children: text }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn small", onClick: () => copy(text), children: "\u590D\u5236" })
642
+ ] });
643
+ }
644
+ function CardEditor({ card, onChange }) {
645
+ const fields = Object.keys(CARD_CHOICES);
646
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-grid", children: [
647
+ fields.map((field) => {
648
+ const choices = CARD_CHOICES[field];
649
+ const own = !choices.includes(card[field]);
650
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: CARD_LABELS[field] }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "her-select", value: own ? OWN : card[field], onChange: (e) => onChange({ ...card, [field]: e.target.value === OWN ? "" : e.target.value }), children: [
653
+ choices.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c, children: c }, c)),
654
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: OWN, children: "\u81EA\u5DF1\u5199\u2026" })
655
+ ] }),
656
+ own && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { marginTop: 4 }, value: card[field], placeholder: field === "age" ? "\u8981\u5199\u6210\u5E74\u7684\uFF0C\u6BD4\u5982 26 \u5C81" : "\u5199\u4F60\u60F3\u8981\u7684", onChange: (e) => onChange({ ...card, [field]: e.target.value }) })
657
+ ] }, field);
658
+ }),
659
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { gridColumn: "1 / -1" }, children: [
660
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "her-label", children: [
661
+ CARD_LABELS.extra,
662
+ "\uFF08\u53EF\u4E0D\u586B\uFF09"
663
+ ] }),
664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: card.extra, placeholder: "\u6BD4\u5982\uFF1A\u5DE6\u773C\u4E0B\u6709\u9897\u5C0F\u75E3\uFF0C\u7B11\u8D77\u6765\u6709\u9152\u7A9D", onChange: (e) => onChange({ ...card, extra: e.target.value }) })
665
+ ] })
666
+ ] });
667
+ }
668
+ function SceneEditor({ project, scene, isDefault, status, screen, onChange, onDefault, onRemove, onFrame }) {
669
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-scenes", children: [
670
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { width: 140 }, value: scene.name, onChange: (e) => onChange((s) => {
672
+ s.name = e.target.value;
673
+ }) }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "her-select", style: { width: 90 }, value: scene.pose, onChange: (e) => onChange((s) => {
675
+ const pose = e.target.value;
676
+ s.clips = s.clips.map((c) => ({ ...c, file: c.file.replace(/^(stand|sit|lie)_/, `${pose}_`), action: c.action.replace(POSE_WORDS[s.pose], POSE_WORDS[pose]) }));
677
+ s.pose = pose;
678
+ }), children: Object.keys(POSE_WORDS).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: p, children: POSE_WORDS[p] }, p)) }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "radio", checked: isDefault, onChange: onDefault }),
681
+ " \u9ED8\u8BA4\u573A\u666F"
682
+ ] }),
683
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn small", onClick: onRemove, children: "\u5220\u6389\u8FD9\u4E2A\u573A\u666F" })
684
+ ] }),
685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "her-label", children: "\u8FD9\u4E2A\u5730\u65B9\u4EC0\u4E48\u6837\uFF08\u5199\u8FDB\u7167\u7247\u7684\u63D0\u793A\u8BCD\uFF09" }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", value: scene.place, placeholder: "\u6BD4\u5982\uFF1A\u6E29\u99A8\u7684\u5367\u5BA4\uFF0C\u6728\u8D28\u5E8A\u5934\u67DC\u548C\u53F0\u706F\uFF0C\u6696\u8272\u706F\u5149\uFF0C\u7A97\u5E18\u534A\u62C9", onChange: (e) => onChange((s) => {
687
+ s.place = e.target.value;
688
+ }) }),
689
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-muted", children: "\u4EC0\u4E48\u65F6\u5019\u5728\u8FD9\uFF1A" }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { width: 90 }, value: scene.hours ?? "", placeholder: "21-24", onChange: (e) => onChange((s) => {
692
+ if (e.target.value.trim() === "") delete s.hours;
693
+ else s.hours = e.target.value.trim();
694
+ }) }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-muted", children: "\u70B9\uFF0C" }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "her-select", style: { width: 150 }, value: scene.user ?? "", onChange: (e) => onChange((s) => {
697
+ if (e.target.value === "") delete s.user;
698
+ else s.user = e.target.value;
699
+ }), children: [
700
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "\u4E0D\u7BA1\u4F60\u5728\u4E0D\u5728" }),
701
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "active", children: "\u4F60\u5728\u7528\u7535\u8111\u65F6" }),
702
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "idle", children: "\u4F60\u5F88\u4E45\u6CA1\u52A8\u65F6" })
703
+ ] })
704
+ ] }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "her-muted", children: [
706
+ "\u7167\u7247\uFF1A\u7528\u53C2\u8003\u56FE\u751F\u6210\uFF08",
707
+ screen === void 0 ? "\u6A2A\u7248\uFF0C\u548C\u5C4F\u5E55\u4E00\u6837\u7684\u6BD4\u4F8B" : `${screen.width}\xD7${screen.height}`,
708
+ "\uFF09\uFF0C\u5B58\u6210 plate.png \u653E\u8FDB\u300C",
709
+ sceneDir(scene),
710
+ "\u300D\u6587\u4EF6\u5939\u3002"
711
+ ] }),
712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PromptBox, { text: scenePhotoPrompt(project, scene, screen) }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PartStatusLine, { status }),
714
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
715
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn", disabled: status?.photo !== true, onClick: onFrame, children: "\u6846\u4EBA\u7269\u533A" }),
716
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "her-muted", children: '\u5728\u684C\u9762\u4E0A\u6253\u5F00"\u505A\u573A\u666F"\uFF0C\u6846\u51FA\u5979\u6D3B\u52A8\u7684\u90A3\u4E00\u5757\uFF1B\u4E4B\u540E\u7528 crop.png \u5F53\u9996\u5E27\u751F\u6210\u89C6\u9891\u3002' })
717
+ ] }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClipList, { project, kind: "scene", scene, clips: scene.clips, pose: scene.pose, onChange: (clips) => onChange((s) => {
719
+ s.clips = clips;
720
+ }) })
721
+ ] });
722
+ }
723
+ function ClipList({ project, kind, scene, clips, pose, onChange }) {
724
+ const [own, setOwn] = (0, import_react.useState)("");
725
+ const set = (i, change) => onChange(clips.map((c, j) => j === i ? { ...c, ...change } : c));
726
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
727
+ clips.map((clip, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", style: { margin: "3px 0" }, children: [
728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { width: 150 }, children: [
729
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: clip.off !== true, disabled: clip.required === true, onChange: (e) => set(i, { off: !e.target.checked }) }),
730
+ " ",
731
+ clip.file
732
+ ] }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { flex: 1, minWidth: 200 }, value: clip.action, disabled: clip.off === true, onChange: (e) => set(i, { action: e.target.value }) }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn small", disabled: clip.off === true, onClick: () => copy(clipPrompt(project, kind, clip, scene)), children: "\u590D\u5236\u63D0\u793A\u8BCD" })
735
+ ] }, clip.file)),
736
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "her-row", children: [
737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "her-input", style: { flex: 1 }, value: own, placeholder: "\u52A0\u4E00\u6BB5\u81EA\u5DF1\u7684\u52A8\u4F5C\uFF0C\u6BD4\u5982\uFF1A\u7AEF\u8D77\u5496\u5561\u559D\u4E00\u53E3\uFF0C\u518D\u653E\u4E0B", onChange: (e) => setOwn(e.target.value) }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "her-btn small", disabled: own.trim() === "", onClick: () => {
739
+ onChange([...clips, customClip(pose, clips, own)]);
740
+ setOwn("");
741
+ }, children: "\u52A0\u4E0A" })
742
+ ] })
743
+ ] });
744
+ }
745
+ function ImportProgress({ state }) {
746
+ const imp = state.import;
747
+ if (imp.state === "running") {
748
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "her-progress", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: `${imp.total === 0 ? 0 : Math.round(imp.done / imp.total * 100)}%` } }) }),
750
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "her-muted", children: imp.step })
751
+ ] });
752
+ }
753
+ if (imp.state !== "done" && imp.state !== "failed") return null;
754
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "her-report", children: imp.lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: line.level === "error" ? "her-danger" : line.level === "warn" ? "her-warn" : "", children: [
755
+ line.level === "ok" ? "\u2705" : line.level === "warn" ? "\u26A0\uFE0F" : "\u274C",
756
+ " ",
757
+ line.text
758
+ ] }, i)) });
759
+ }
760
+
761
+ // src/client/parts.tsx
762
+ var import_react2 = require("react");
763
+ var import_jsx_runtime2 = require("react/jsx-runtime");
764
+ var MODE_TEXT = {
765
+ overlay: { title: "\u6D6E\u5728\u6700\u4E0A\u5C42", desc: "\u50CF\u684C\u5BA0\u4E00\u6837\u5728\u6240\u6709\u7A97\u53E3\u4E0A\u9762\uFF0C\u53EF\u4EE5\u62D6\u52A8\u3001\u70B9\u5979" },
766
+ wallpaper: { title: "\u4F4F\u5728\u58C1\u7EB8\u91CC", desc: "\u5728\u684C\u9762\u56FE\u6807\u548C\u6240\u6709\u7A97\u53E3\u4E0B\u9762\uFF0C\u53EA\u770B\u4E0D\u70B9\uFF1B\u6309\u5FEB\u6377\u952E\u53EB\u5979\u51FA\u6765" },
767
+ window: { title: "\u666E\u901A\u7A97\u53E3", desc: "\u4E00\u4E2A\u53EF\u4EE5\u5173\u6389\u7684\u7A97\u53E3" }
768
+ };
769
+ function Dot({ tone }) {
770
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `her-dot ${tone === "off" ? "" : tone}` });
771
+ }
772
+ function BodyStatus({ state, apply: apply2 }) {
773
+ const [message, setMessage] = (0, import_react2.useState)();
774
+ const body = state.body;
775
+ const install = body.install;
776
+ const start = async () => {
777
+ const result = await herApi.startBody();
778
+ if (result.ok) setMessage(result.message);
779
+ await apply2(Promise.resolve(result));
780
+ };
781
+ const busy = install.state === "checking" || install.state === "downloading" || install.state === "installing";
782
+ const progress = busy && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
783
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-progress", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: `${install.total ? Math.round((install.done ?? 0) / install.total * 100) : 0}%` } }) }),
784
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: install.state === "checking" ? "\u6B63\u5728\u770B\u6709\u6CA1\u6709\u65B0\u7248\u672C\u2026" : install.state === "installing" ? "\u6B63\u5728\u5B89\u88C5\u2026" : `\u6B63\u5728\u4E0B\u8F7D\u8EAB\u4F53\u7A0B\u5E8F ${install.latest ?? ""}\uFF08${mb(install.done)} / ${mb(install.total)} MB\uFF09` })
785
+ ] });
786
+ const failed = install.state === "failed" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-danger", children: [
787
+ "\u4E0B\u8F7D\u5B89\u88C5\u6CA1\u6210\u529F\uFF1A",
788
+ install.error
789
+ ] });
790
+ if (body.connected) {
791
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dot, { tone: "ok" }),
794
+ "\u8EAB\u4F53\u5DF2\u8FDE\u4E0A\uFF08",
795
+ body.info?.os,
796
+ "\uFF0C\u7248\u672C ",
797
+ body.info?.version,
798
+ "\uFF09",
799
+ body.pack === void 0 ? "" : `\uFF0C\u5916\u5F62\uFF1A${body.pack.name}`,
800
+ !install.dev && !busy && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn link", onClick: () => void apply2(herApi.installBody()), children: "\u68C0\u67E5\u66F4\u65B0" }),
801
+ !install.dev && !busy && install.latest !== void 0 && install.restart !== true && install.state === "idle" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "her-muted", children: [
802
+ "\uFF08\u5DF2\u662F\u6700\u65B0\uFF1A",
803
+ install.latest,
804
+ "\uFF09"
805
+ ] })
806
+ ] }),
807
+ install.restart === true && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
809
+ "\u65B0\u7248\u672C ",
810
+ install.installed,
811
+ " \u5DF2\u7ECF\u88C5\u597D\uFF0C\u91CD\u542F\u4E00\u4E0B\u8EAB\u4F53\u5C31\u7528\u4E0A\u4E86\u3002"
812
+ ] }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", onClick: () => void apply2(herApi.restartBody()), children: "\u91CD\u542F\u8EAB\u4F53" })
814
+ ] }),
815
+ progress,
816
+ failed
817
+ ] });
818
+ }
819
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dot, { tone: body.running ? "warn" : "bad" }),
822
+ body.running ? "\u8EAB\u4F53\u5728\u8FD0\u884C\uFF0C\u6B63\u5728\u8FDE\u63A5\u2026" : "\u8EAB\u4F53\u8FD8\u6CA1\u8FD0\u884C"
823
+ ] }),
824
+ !body.running && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-row", children: body.canStart ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", onClick: () => void start(), children: "\u542F\u52A8\u8EAB\u4F53" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", disabled: busy, onClick: () => void apply2(herApi.installBody()), children: "\u4E0B\u8F7D\u5B89\u88C5\u8EAB\u4F53" }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-muted", children: "\u5979\u5728\u684C\u9762\u4E0A\u7684\u6837\u5B50\u662F\u4E00\u4E2A\u5355\u72EC\u7684\u5C0F\u7A0B\u5E8F\uFF08\u7EA6 150 MB\uFF09\uFF0C\u88C5\u5728 dsh-her \u81EA\u5DF1\u7684\u6587\u4EF6\u5939\u91CC\uFF0C\u88C5\u597D\u5C31\u81EA\u52A8\u542F\u52A8\u3002" })
827
+ ] }) }),
828
+ progress,
829
+ failed,
830
+ message !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: message })
831
+ ] });
832
+ }
833
+ var mb = (bytes) => ((bytes ?? 0) / 1e6).toFixed(0);
834
+ function PackPicker({ state, apply: apply2 }) {
835
+ const body = state.body;
836
+ const imp = state.import;
837
+ if (!body.connected) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: "\u8EAB\u4F53\u8FDE\u4E0A\u4E4B\u540E\u624D\u80FD\u9009\u5916\u5F62\u3002" });
838
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-grid", children: [
840
+ body.packs.map((pack) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { className: `her-choice ${pack.packId === body.activePack ? "on" : ""}`, onClick: () => void apply2(herApi.usePack(pack.packId)), children: [
841
+ pack.name,
842
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("small", { children: [
843
+ pack.packId.startsWith("look:") ? "\u591A\u573A\u666F\u5F62\u8C61" : pack.layer === "scene" ? "\u58C1\u7EB8\u573A\u666F" : "\u684C\u5BA0",
844
+ " \xB7 ",
845
+ pack.actions.filter((a) => !a.id.startsWith("scene_")).length,
846
+ " \u4E2A\u52A8\u4F5C",
847
+ pack.emotions.length > 0 ? ` \xB7 ${pack.emotions.length} \u79CD\u60C5\u7EEA` : ""
848
+ ] })
849
+ ] }, pack.packId)),
850
+ body.packs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: "\u8FD8\u6CA1\u6709\u5BFC\u5165\u8FC7\u5916\u5F62\u5305\u3002" })
851
+ ] }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", disabled: imp.state === "picking" || imp.state === "running", onClick: () => void apply2(herApi.pickPack()), children: "\u5BFC\u5165\u6211\u7684\u7D20\u6750\u2026" }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-muted", children: "\u4F1A\u5728\u8EAB\u4F53\u90A3\u53F0\u7535\u8111\u4E0A\u5F39\u51FA\u9009\u62E9\u6587\u4EF6\u5939\u7684\u7A97\u53E3\u3002\u7D20\u6750\u600E\u4E48\u505A\u3001\u600E\u4E48\u547D\u540D\uFF0C\u89C1 docs/\u7D20\u6750\u751F\u6210\u63D0\u793A\u8BCD.md\u3002" })
855
+ ] }),
856
+ imp.state === "picking" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: "\u8BF7\u5728\u684C\u9762\u4E0A\u5F39\u51FA\u7684\u7A97\u53E3\u91CC\u9009\u62E9\u7D20\u6750\u6587\u4EF6\u5939\u2026" }),
857
+ imp.state === "running" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
858
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-progress", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { width: `${imp.total === 0 ? 0 : Math.round(imp.done / imp.total * 100)}%` } }) }),
859
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-muted", children: [
860
+ imp.step,
861
+ "\uFF08",
862
+ imp.done,
863
+ "/",
864
+ imp.total,
865
+ "\uFF09"
866
+ ] })
867
+ ] }),
868
+ (imp.state === "done" || imp.state === "failed") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "her-report", children: imp.lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: line.level === "error" ? "her-danger" : line.level === "warn" ? "her-warn" : "", children: [
869
+ line.level === "ok" ? "\u2705" : line.level === "warn" ? "\u26A0\uFE0F" : "\u274C",
870
+ " ",
871
+ line.text
872
+ ] }, i)) })
873
+ ] });
874
+ }
875
+ function PersonaEditor({ state, apply: apply2, onSaved, saveLabel = "\u4FDD\u5B58" }) {
876
+ const c = state.character;
877
+ const [name, setName] = (0, import_react2.useState)(c.name);
878
+ const [callYou, setCallYou] = (0, import_react2.useState)(c.callYou);
879
+ const [pronoun, setPronoun] = (0, import_react2.useState)(c.pronoun);
880
+ const [persona, setPersona] = (0, import_react2.useState)(c.persona);
881
+ const [idea, setIdea] = (0, import_react2.useState)("");
882
+ const [busy, setBusy] = (0, import_react2.useState)();
883
+ const [error, setError] = (0, import_react2.useState)();
884
+ const [chat, setChat] = (0, import_react2.useState)([]);
885
+ const [line, setLine] = (0, import_react2.useState)("");
886
+ const expand = async () => {
887
+ setBusy("expand");
888
+ setError(void 0);
889
+ const result = await herApi.expandPersona({ name, idea, callYou, pronoun });
890
+ setBusy(void 0);
891
+ if (result.ok) setPersona(result.persona);
892
+ else setError(result.message);
893
+ };
894
+ const tryOne = async () => {
895
+ const message = line.trim();
896
+ if (message === "") return;
897
+ setBusy("try");
898
+ setLine("");
899
+ const history = [...chat, { who: "you", text: message }];
900
+ setChat(history);
901
+ const result = await herApi.tryPersona({ name, persona, callYou, pronoun, history: chat, message });
902
+ setBusy(void 0);
903
+ setChat(result.ok ? [...history, { who: "her", text: result.reply }] : history);
904
+ if (!result.ok) setError(result.message);
905
+ };
906
+ const save = async () => {
907
+ setBusy("save");
908
+ const ok = await apply2(herApi.saveCharacter({ name, persona, callYou, pronoun }));
909
+ setBusy(void 0);
910
+ if (ok) onSaved?.();
911
+ };
912
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
913
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1, minWidth: 140 }, children: [
915
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u5979\u7684\u540D\u5B57" }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "her-input", value: name, onChange: (e) => setName(e.target.value) })
917
+ ] }),
918
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1, minWidth: 140 }, children: [
919
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u5979\u600E\u4E48\u79F0\u547C\u4F60" }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "her-input", value: callYou, placeholder: "\u6BD4\u5982\uFF1A\u963F\u6587", onChange: (e) => setCallYou(e.target.value) })
921
+ ] }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { width: 120 }, children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u63D0\u5230\u4F60\u65F6\u7528" }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "her-select", value: pronoun, onChange: (e) => setPronoun(e.target.value), children: [
925
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "\u4ED6", children: "\u4ED6" }),
926
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "\u5979", children: "\u5979" }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "TA", children: "TA" })
928
+ ] })
929
+ ] })
930
+ ] }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u4E00\u53E5\u8BDD\u63CF\u8FF0\u5979\uFF08\u53EF\u9009\uFF09\uFF0C\u8BA9\u6A21\u578B\u5E2E\u4F60\u5199\u6210\u5B8C\u6574\u4EBA\u8BBE" }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
933
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "her-input", style: { flex: 1 }, value: idea, placeholder: "\u6BD4\u5982\uFF1A\u4E8C\u5341\u4E94\u5C81\u7684\u7F16\u5267\uFF0C\u7231\u5410\u69FD\uFF0C\u559C\u6B22\u732B\u548C\u706B\u9505", onChange: (e) => setIdea(e.target.value) }),
934
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", disabled: busy !== void 0 || idea.trim() === "", onClick: () => void expand(), children: busy === "expand" ? "\u6B63\u5728\u5199\u2026" : "\u5E2E\u6211\u5199" })
935
+ ] }),
936
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u4EBA\u8BBE\uFF08\u53EF\u4EE5\u76F4\u63A5\u6539\uFF09" }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "her-textarea", value: persona, onChange: (e) => setPersona(e.target.value) }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: "\u5148\u8BD5\u804A\u51E0\u53E5\uFF08\u4E0D\u4F1A\u8FDB\u5979\u7684\u8BB0\u5FC6\uFF09" }),
939
+ chat.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-chat", children: chat.map((m, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: m.who, children: [
940
+ m.who === "you" ? "\u4F60\uFF1A" : `${name}\uFF1A`,
941
+ m.text
942
+ ] }, i)) }),
943
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
944
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "her-input", style: { flex: 1 }, value: line, placeholder: "\u8BF4\u4E00\u53E5\u8BD5\u8BD5", onChange: (e) => setLine(e.target.value), onKeyDown: (e) => {
945
+ if (e.key === "Enter") void tryOne();
946
+ } }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", disabled: busy !== void 0 || persona.trim() === "", onClick: () => void tryOne(), children: busy === "try" ? "\u2026" : "\u53D1\u9001" })
948
+ ] }),
949
+ error !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-error", children: error }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", disabled: busy !== void 0, onClick: () => void save(), children: saveLabel }) })
951
+ ] });
952
+ }
953
+ function KeyHelp({ state }) {
954
+ if (state.voice.speaking.status === "no-quota") {
955
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-warn", children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "\u767E\u70BC\u8D26\u53F7\u7684\u989D\u5EA6\u7528\u5B8C\u4E86\uFF0C\u5979\u73B0\u5728\u53EA\u7528\u6587\u5B57\u56DE\u7B54\u3002" }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: '\u5230\u963F\u91CC\u4E91\u767E\u70BC\u63A7\u5236\u53F0\u5F00\u901A\u6309\u91CF\u4ED8\u8D39\uFF1B\u5982\u679C\u5DF2\u7ECF\u5F00\u901A\uFF0C\u628A"\u4EC5\u4F7F\u7528\u514D\u8D39\u989D\u5EA6"\u5173\u6389\u3002\u5F04\u597D\u540E\u6700\u591A\u534A\u5206\u949F\u5979\u5C31\u80FD\u8BF4\u8BDD\uFF0C\u4E0D\u7528\u91CD\u542F\u3002' }),
958
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-faint", children: state.voice.speaking.error })
959
+ ] });
960
+ }
961
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-warn", children: [
962
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "\u5979\u7684\u58F0\u97F3\u7528\u963F\u91CC\u4E91\u767E\u70BC\uFF08CosyVoice\uFF09\uFF0C\u8FD8\u6CA1\u6709\u627E\u5230 API key\uFF0C\u73B0\u5728\u53EA\u7528\u6587\u5B57\u56DE\u7B54\u3002" }),
963
+ state.platform === "darwin" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
964
+ "\u5728\u7EC8\u7AEF\u8FD0\u884C\u4E0B\u9762\u8FD9\u6761\u547D\u4EE4\uFF0C\u6309\u63D0\u793A\u7C98\u8D34 key\uFF08\u4E24\u6B21\uFF0C\u5C4F\u5E55\u4E0A\u4E0D\u663E\u793A\uFF09\uFF0C\u4E0D\u7528\u91CD\u542F\uFF1A",
965
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
966
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "security add-generic-password -a dsh-her -s dsh-her-dashscope -w" })
967
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
968
+ "\u628A key \u8BBE\u6210\u73AF\u5883\u53D8\u91CF ",
969
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "DASHSCOPE_API_KEY" }),
970
+ "\uFF0C\u7136\u540E\u91CD\u542F dsh\u3002"
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-faint", children: "key \u4E0D\u4F1A\u5B58\u8FDB\u4EFB\u4F55\u6587\u4EF6\uFF0C\u4E5F\u4E0D\u8981\u8D34\u5230\u804A\u5929\u91CC\u3002" })
973
+ ] });
974
+ }
975
+ function VoicePicker({ state, apply: apply2, onSaved, saveLabel = "\u7528\u8FD9\u4E2A\u58F0\u97F3" }) {
976
+ const [voice, setVoice] = (0, import_react2.useState)(state.character.voice.id);
977
+ const [speed, setSpeed] = (0, import_react2.useState)(state.character.voice.speed);
978
+ const [error, setError] = (0, import_react2.useState)();
979
+ const [playing, setPlaying] = (0, import_react2.useState)();
980
+ const { status: speaking, error: speakingError } = state.voice.speaking;
981
+ const listen = async (which) => {
982
+ setVoice(which);
983
+ setPlaying(which);
984
+ setError(void 0);
985
+ const result = await herApi.previewVoice(which, speed, `\u4F60\u597D\u5440\uFF0C\u6211\u662F${state.character.name}\uFF0C\u6211\u5728\u4F60\u684C\u9762\u4E0A\u7B49\u4F60\u597D\u4E45\u5566\u3002\u4ECA\u5929\u8FC7\u5F97\u600E\u4E48\u6837\uFF1F`);
986
+ setPlaying(void 0);
987
+ if (result.ok) playAudio(result.audio, result.format);
988
+ else setError(result.message);
989
+ };
990
+ const save = async () => {
991
+ if (await apply2(herApi.saveCharacter({ voice, speed }))) onSaved?.();
992
+ };
993
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
994
+ (speaking === "no-key" || speaking === "no-quota") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(KeyHelp, { state }),
995
+ speaking === "off" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-warn", children: "\u5979\u7684\u58F0\u97F3\u5DF2\u5173\u95ED\uFF08\u63D2\u4EF6\u8BBE\u7F6E\u91CC voice.enabled \u662F false\uFF09\uFF0C\u53EA\u7528\u6587\u5B57\u56DE\u7B54\u3002" }),
996
+ speaking === "installing" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: "\u7B2C\u4E00\u6B21\u7528\uFF0C\u6B63\u5728\u51C6\u5907 Edge \u8BED\u97F3\u7EC4\u4EF6\uFF08\u4E00\u4E24\u5206\u949F\uFF09\u2026" }),
997
+ speaking === "failed" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-warn", children: [
998
+ "\u5979\u7684\u58F0\u97F3\u521A\u624D\u51FA\u9519\u4E86\uFF1A",
999
+ speakingError ?? "\u672A\u77E5\u539F\u56E0",
1000
+ "\u3002\u4F1A\u81EA\u52A8\u91CD\u8BD5\u3002"
1001
+ ] }),
1002
+ ["edge", "cosyvoice"].map((service) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "her-label", children: SERVICE_NAMES[service] }),
1004
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-grid", children: VOICES.filter((v2) => v2.service === service).map((v2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { className: `her-choice ${v2.id === voice ? "on" : ""}`, disabled: speaking === "off", onClick: () => void listen(v2.id), children: [
1005
+ v2.name,
1006
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: playing === v2.id ? "\u6B63\u5728\u5408\u6210\u2026" : `${v2.desc} \xB7 \u70B9\u4E00\u4E0B\u8BD5\u542C` })
1007
+ ] }, v2.id)) })
1008
+ ] }, service)),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "her-muted", children: [
1011
+ "\u8BED\u901F ",
1012
+ speed.toFixed(1)
1013
+ ] }),
1014
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "range", min: 0.7, max: 1.4, step: 0.1, value: speed, onChange: (e) => setSpeed(Number(e.target.value)) }),
1015
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", disabled: speaking === "off", onClick: () => void listen(voice), children: "\u7528\u8FD9\u4E2A\u8BED\u901F\u8BD5\u542C" })
1016
+ ] }),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-faint", children: "Edge \u7684\u58F0\u97F3\u514D\u8D39\uFF0C\u4F46\u4E0D\u662F\u5FAE\u8F6F\u516C\u5F00\u7684\u6B63\u5F0F\u63A5\u53E3\uFF0C\u53EF\u80FD\u54EA\u5929\u5931\u6548\uFF1B\u767E\u70BC\u7684\u58F0\u97F3\u66F4\u7A33\uFF0C\u8981 key \u548C\u4ED8\u8D39\u989D\u5EA6\u3002\u7528\u4F60\u81EA\u5DF1\u5F55\u7684\u58F0\u97F3\u514B\u9686\uFF0C\u4E4B\u540E\u4F1A\u52A0\u5728\u8FD9\u91CC\u3002" }),
1018
+ error !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-error", children: error }),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", onClick: () => void save(), children: saveLabel }) })
1020
+ ] });
1021
+ }
1022
+ var DEFAULT_CHOICE = "";
1023
+ function ModelPicker({ state, apply: apply2 }) {
1024
+ const chosen = state.character.model;
1025
+ const [groups, setGroups] = (0, import_react2.useState)();
1026
+ const [loadError, setLoadError] = (0, import_react2.useState)();
1027
+ const [choice, setChoice] = (0, import_react2.useState)(chosen === void 0 ? DEFAULT_CHOICE : `${chosen.provider}/${chosen.model}`);
1028
+ const [effort, setEffort] = (0, import_react2.useState)(chosen?.effort ?? "");
1029
+ const [saved, setSaved] = (0, import_react2.useState)(false);
1030
+ (0, import_react2.useEffect)(() => {
1031
+ void modelCatalog().then((result) => {
1032
+ if (result.ok) setGroups(result.groups);
1033
+ else setLoadError(result.message);
1034
+ });
1035
+ }, []);
1036
+ const [provider, model] = choice === DEFAULT_CHOICE ? [void 0, void 0] : [choice.slice(0, choice.indexOf("/")), choice.slice(choice.indexOf("/") + 1)];
1037
+ const efforts = groups?.find((g) => g.id === provider)?.models.find((m) => m.id === model)?.reasoning?.efforts ?? [];
1038
+ const nameOf = (p, m) => {
1039
+ const group = groups?.find((g) => g.id === p);
1040
+ return `${group?.name ?? p} \xB7 ${group?.models.find((x) => x.id === m)?.name ?? m}`;
1041
+ };
1042
+ const save = async () => {
1043
+ setSaved(false);
1044
+ const value = provider === void 0 ? null : { provider, model, ...effort === "" ? {} : { effort } };
1045
+ if (await apply2(herApi.saveCharacter({ model: value }))) setSaved(true);
1046
+ };
1047
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1048
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-muted", children: [
1049
+ "\u73B0\u5728\u7528\u7684\uFF1A",
1050
+ state.model === null ? "\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B" : `${nameOf(state.model.provider, state.model.model)}${state.model.effort ? `\uFF08\u601D\u8003\uFF1A${state.model.effort}\uFF09` : ""}`,
1051
+ state.model !== null && !state.model.chosen && "\uFF08dsh \u7684\u9ED8\u8BA4\u6A21\u578B\uFF09"
1052
+ ] }),
1053
+ loadError !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-error", children: [
1054
+ "\u62FF\u4E0D\u5230 dsh \u7684\u6A21\u578B\u5217\u8868\uFF1A",
1055
+ loadError
1056
+ ] }),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
1058
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "her-select", style: { flex: 1, minWidth: 220 }, value: choice, disabled: groups === void 0, onChange: (e) => {
1059
+ setChoice(e.target.value);
1060
+ setEffort("");
1061
+ }, children: [
1062
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: DEFAULT_CHOICE, children: "\u8DDF\u968F dsh \u7684\u9ED8\u8BA4\u6A21\u578B" }),
1063
+ groups?.map((g) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("optgroup", { label: g.name, children: g.models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: `${g.id}/${m.id}`, children: m.name }, m.id)) }, g.id))
1064
+ ] }),
1065
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "her-select", style: { width: 150 }, value: effort, disabled: efforts.length === 0, onChange: (e) => setEffort(e.target.value), children: [
1066
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "\u601D\u8003\uFF1A\u9ED8\u8BA4" }),
1067
+ efforts.map((x) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: x.id, children: [
1068
+ "\u601D\u8003\uFF1A",
1069
+ x.name
1070
+ ] }, x.id))
1071
+ ] }),
1072
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn primary", disabled: groups === void 0, onClick: () => void save(), children: "\u7528\u8FD9\u4E2A\u6A21\u578B" })
1073
+ ] }),
1074
+ saved && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-ok", children: "\u5DF2\u6362\u597D\uFF0C\u4ECE\u5979\u7684\u4E0B\u4E00\u53E5\u8BDD\u5F00\u59CB\u751F\u6548\uFF1B\u4E4B\u524D\u804A\u8FC7\u7684\u90FD\u8FD8\u5728\u3002" }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-faint", children: "\u804A\u5929\u8981\u7684\u662F\u5FEB\uFF1A\u9009 Flash \u4E00\u7C7B\u7684\u5FEB\u6A21\u578B\u3001\u601D\u8003\u9009 Low\uFF08\u6216 Off\uFF09\uFF0C\u5979\u5F00\u53E3\u4F1A\u5FEB\u5F88\u591A\u3002\u6A21\u578B\u7684\u8D26\u53F7\u548C\u4EF7\u683C\u5728 dsh \u7684\u8BBE\u7F6E\u91CC\u7BA1\u3002" })
1076
+ ] });
1077
+ }
1078
+ function MicCheck({ state }) {
1079
+ const shortcut = state.platform === "darwin" ? "\u2303\u2325\u7A7A\u683C" : "Ctrl+Alt+\u7A7A\u683C";
1080
+ const fresh = state.heard !== void 0 && Date.now() - state.heard.at < 12e4;
1081
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
1083
+ "\u6309 ",
1084
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: shortcut }),
1085
+ '\uFF0C\u5BF9\u7740\u9EA6\u514B\u98CE\u8BF4\u4E00\u53E5\u8BDD\uFF08\u6BD4\u5982"\u4F60\u597D\u5440"\uFF09\u3002\u7B2C\u4E00\u6B21\u4F1A\u5F39\u51FA\u9EA6\u514B\u98CE\u6743\u9650\u8BF7\u6C42\uFF0C\u8BF7\u5141\u8BB8\u3002'
1086
+ ] }),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "\u5979\u811A\u4E0B\u7684\u5C0F\u5706\u70B9\u4EAE\u8D77\u8868\u793A\u5728\u542C\uFF0C\u53D8\u7EFF\u8868\u793A\u542C\u5230\u4E86\u58F0\u97F3\u3002" }),
1088
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: fresh ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Dot, { tone: "ok" }),
1090
+ "\u6211\u542C\u5230\u4E86\uFF1A\u300C",
1091
+ state.heard.text,
1092
+ "\u300D"
1093
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-muted", children: "\u8FD8\u6CA1\u542C\u5230\u2026\u2026" }) }),
1094
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-faint", children: "\u542C\u4E0D\u6E05\u65F6\uFF1A\u6362\u4E2A\u5B89\u9759\u7684\u5730\u65B9\uFF0C\u6216\u8005\u68C0\u67E5\u7CFB\u7EDF\u8BBE\u7F6E\u91CC\u9009\u7684\u9EA6\u514B\u98CE\u3002\u5916\u653E\u65F6\u5979\u7684\u58F0\u97F3\u53EF\u80FD\u88AB\u5F53\u6210\u4F60\u5728\u8BF4\u8BDD\uFF0C\u6234\u8033\u673A\u6548\u679C\u6700\u597D\u3002" })
1095
+ ] });
1096
+ }
1097
+ function ScenePicker({ state, apply: apply2 }) {
1098
+ const look = state.body.look;
1099
+ if (look === void 0) return null;
1100
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-scenes", children: [
1101
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
1102
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: look.name }),
1103
+ " \u7684\u573A\u666F",
1104
+ look.desk ? '\uFF08"\u6D6E\u5728\u6700\u4E0A\u5C42"\u65F6\u663E\u793A\u684C\u5BA0\uFF0C\u6362\u573A\u666F\u5728\u5176\u4ED6\u4E24\u79CD\u6A21\u5F0F\u4E0B\u770B\u5F97\u5230\uFF09' : ""
1105
+ ] }),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
1107
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: look.auto, onChange: (e) => void apply2(herApi.setLook({ auto: e.target.checked })) }),
1109
+ " \u81EA\u52A8\u6362\u573A\u666F"
1110
+ ] }),
1111
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
1112
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: look.locked, onChange: (e) => void apply2(herApi.setLook({ locked: e.target.checked })) }),
1113
+ " \u5F85\u5728\u73B0\u5728\u8FD9\u91CC\u4E0D\u52A8"
1114
+ ] })
1115
+ ] }),
1116
+ look.scenes.map((scene) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row her-scene", children: [
1117
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: scene.on, disabled: scene.id === look.current, onChange: (e) => void apply2(herApi.setLook({ on: { [scene.id]: e.target.checked } })) }),
1119
+ " ",
1120
+ scene.name
1121
+ ] }),
1122
+ scene.id === look.current && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag ok", children: "\u73B0\u5728\u5728\u8FD9" }),
1123
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-muted", children: scene.when }),
1124
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", disabled: scene.id === look.current || !scene.on, onClick: () => void apply2(herApi.setLook({ go: scene.id })), children: "\u73B0\u5728\u53BB\u8FD9\u91CC" })
1125
+ ] }, scene.id)),
1126
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: '\u81EA\u52A8\u6362\u573A\u666F\u65F6\uFF0C\u6BCF\u4E2A\u573A\u666F\u81F3\u5C11\u5F85 20 \u5206\u949F\uFF0C\u4F60\u4EEC\u8BF4\u8BDD\u7684\u65F6\u5019\u4E0D\u6362\u3002\u8DDF\u5979\u8BF4"\u53BB\u5367\u5BA4\u5427""\u5C31\u5F85\u5728\u8FD9"\u4E5F\u884C\u3002' })
1127
+ ] });
1128
+ }
1129
+ function ModePicker({ state, apply: apply2 }) {
1130
+ const current = state.body.mode;
1131
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "her-grid", children: ["overlay", "wallpaper", "window"].map((mode) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { className: `her-choice ${current === mode ? "on" : ""}`, disabled: !state.body.connected, onClick: () => void apply2(herApi.setMode(mode)), children: [
1132
+ MODE_TEXT[mode].title,
1133
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: MODE_TEXT[mode].desc })
1134
+ ] }, mode)) });
1135
+ }
1136
+ function MemoryView({ state, apply: apply2 }) {
1137
+ const [report, setReport] = (0, import_react2.useState)();
1138
+ const { diary, lastDigestDay } = state.memory;
1139
+ const facts = [...state.memory.facts].reverse();
1140
+ const episodes = [...state.memory.episodes].reverse();
1141
+ const today = (/* @__PURE__ */ new Date()).toDateString();
1142
+ const isNew = (at) => new Date(at).toDateString() === today;
1143
+ const digest = async () => {
1144
+ setReport("\u6B63\u5728\u6574\u7406\u2026");
1145
+ const result = await herApi.digest();
1146
+ setReport(result.ok ? result.report : result.message);
1147
+ if (result.ok) await apply2(Promise.resolve(result));
1148
+ };
1149
+ const download = async () => {
1150
+ const result = await herApi.exportMemory();
1151
+ if (!result.ok) return;
1152
+ const link = document.createElement("a");
1153
+ link.href = URL.createObjectURL(new Blob([result.markdown], { type: "text/markdown" }));
1154
+ link.download = `\u5979\u8BB0\u5F97\u7684\u4E8B-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.md`;
1155
+ link.click();
1156
+ URL.revokeObjectURL(link.href);
1157
+ };
1158
+ const forget = (id, text) => {
1159
+ if (window.confirm(`\u8BA9\u5979\u5FD8\u6389\u8FD9\u6761\uFF1F
1160
+
1161
+ ${text}`)) void apply2(herApi.memoryRemove([id]));
1162
+ };
1163
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-card", children: [
1165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h2", { children: [
1166
+ "\u5173\u4E8E\u4F60\uFF08",
1167
+ facts.length,
1168
+ "\uFF09"
1169
+ ] }),
1170
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: '\u5979\u804A\u5929\u65F6\u81EA\u5DF1\u8BB0\u4E0B\u7684\u76F4\u63A5\u751F\u6548\uFF1B\u804A\u5B8C\u4E00\u5206\u949F\u540E\u5979\u4F1A\u628A\u521A\u624D\u7684\u8BDD\u8FC7\u4E00\u904D\uFF0C\u6311\u51FA\u6765\u7684\u5148"\u5F85\u786E\u8BA4"\uFF0C7 \u5929\u540E\u81EA\u52A8\u786E\u8BA4\uFF0C\u654F\u611F\u7684\u8981\u4F60\u70B9\u786E\u8BA4\u3002' }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "her-list", children: [
1172
+ facts.map((f) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
1173
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "grow", children: f.text }),
1174
+ isNew(f.createdAt) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag ok", children: "\u4ECA\u5929" }),
1175
+ f.status === "candidate" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag warn", children: "\u5F85\u786E\u8BA4" }),
1176
+ f.sensitive && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag bad", children: "\u654F\u611F" }),
1177
+ f.status === "candidate" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn small", onClick: () => void apply2(herApi.memoryConfirm([f.id])), children: "\u786E\u8BA4" }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn small", onClick: () => forget(f.id, f.text), children: "\u5FD8\u6389" })
1179
+ ] }, f.id)),
1180
+ facts.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { className: "her-muted", children: "\u8FD8\u6CA1\u6709" })
1181
+ ] })
1182
+ ] }),
1183
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-card", children: [
1184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h2", { children: [
1185
+ "\u4E8B\u4EF6\uFF08",
1186
+ episodes.length,
1187
+ "\uFF09"
1188
+ ] }),
1189
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: "\u6709\u65E5\u671F\u7684\u4E8B\uFF0C\u5728\u5F53\u5929\u548C\u524D\u4E00\u5929\u5979\u4F1A\u81EA\u7136\u5730\u63D0\u8D77\u3002" }),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "her-list", children: [
1191
+ episodes.map((e) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-faint", children: e.at.slice(0, 10) }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "grow", children: e.text }),
1194
+ isNew(e.at) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag ok", children: "\u4ECA\u5929" }),
1195
+ e.due !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-tag", children: e.due }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn small", onClick: () => forget(e.id, e.text), children: "\u5FD8\u6389" })
1197
+ ] }, e.id)),
1198
+ episodes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { className: "her-muted", children: "\u8FD8\u6CA1\u6709" })
1199
+ ] })
1200
+ ] }),
1201
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-card", children: [
1202
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h2", { children: [
1203
+ "\u5979\u7684\u65E5\u8BB0\uFF08",
1204
+ diary.length,
1205
+ " \u5929\uFF09"
1206
+ ] }),
1207
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "her-muted", children: [
1208
+ "\u65E5\u8BB0\u6BCF\u5929\u5199\u4E00\u7BC7\uFF1A\u4F60\u6700\u540E\u4E00\u6B21\u548C\u5979\u8BF4\u8BDD 30 \u5206\u949F\u540E\uFF08\u6216 23:30 \u4EE5\u540E\uFF09\u3002\u4E0A\u6B21\u5199\u5230\uFF1A",
1209
+ lastDigestDay ?? "\u8FD8\u6CA1\u6709"
1210
+ ] }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: diary.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "her-muted", children: "\u8FD8\u6CA1\u6709" }) : diary.join("\u3001") }),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "her-row", children: [
1213
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", onClick: () => void digest(), children: "\u73B0\u5728\u6574\u7406\u4ECA\u5929" }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "her-btn", onClick: () => void download(), children: "\u5BFC\u51FA\u5168\u90E8\uFF08Markdown\uFF09" })
1215
+ ] }),
1216
+ report !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "her-muted", children: report })
1217
+ ] })
1218
+ ] });
1219
+ }
1220
+
1221
+ // src/client/Page.tsx
1222
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1223
+ var POLL_MS = 2e3;
1224
+ function HerPage() {
1225
+ const [state, setState] = (0, import_react3.useState)();
1226
+ const [error, setError] = (0, import_react3.useState)();
1227
+ const [wizard, setWizard] = (0, import_react3.useState)();
1228
+ const refresh = (0, import_react3.useCallback)(async () => {
1229
+ const result = await herApi.state();
1230
+ if (result.ok) {
1231
+ setState(result.state);
1232
+ setError(void 0);
1233
+ } else setError(result.message);
1234
+ }, []);
1235
+ (0, import_react3.useEffect)(() => {
1236
+ void refresh();
1237
+ const timer = setInterval(() => void refresh(), POLL_MS);
1238
+ return () => clearInterval(timer);
1239
+ }, [refresh]);
1240
+ const apply2 = (0, import_react3.useCallback)(async (call) => {
1241
+ const result = await call;
1242
+ if (result.ok) {
1243
+ setState(result.state);
1244
+ setError(void 0);
1245
+ return true;
1246
+ }
1247
+ setError(result.message);
1248
+ return false;
1249
+ }, []);
1250
+ if (state === void 0) {
1251
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "her-page", children: error === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: "\u6B63\u5728\u8FDE\u63A5\u5979\u7684\u5927\u8111\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "her-error", children: [
1252
+ "\u8FDE\u4E0D\u4E0A\uFF1A",
1253
+ error
1254
+ ] }) });
1255
+ }
1256
+ const showWizard = wizard ?? !state.character.onboarded;
1257
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-page", children: [
1258
+ showWizard ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Wizard, { state, apply: apply2, onDone: () => setWizard(false) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Manage, { state, apply: apply2, onWizard: () => setWizard(true) }),
1259
+ error !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-error", children: error })
1260
+ ] });
1261
+ }
1262
+ var STEPS = ["\u68C0\u67E5", "\u8EAB\u4F53", "\u5916\u5F62", "\u4EBA\u8BBE", "\u58F0\u97F3", "\u9EA6\u514B\u98CE", "\u653E\u5230\u684C\u9762"];
1263
+ function Wizard({ state, apply: apply2, onDone }) {
1264
+ const [step, setStep] = (0, import_react3.useState)(0);
1265
+ const next = () => setStep((s) => Math.min(s + 1, STEPS.length - 1));
1266
+ const finish = async () => {
1267
+ if (await apply2(herApi.saveCharacter({ onboarded: true }))) onDone();
1268
+ };
1269
+ const body = state.body;
1270
+ const nav = (canNext, hint) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-row", children: [
1271
+ step > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn", onClick: () => setStep(step - 1), children: "\u4E0A\u4E00\u6B65" }),
1272
+ step < STEPS.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn primary", disabled: !canNext, onClick: next, children: "\u4E0B\u4E00\u6B65" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn primary", onClick: () => void finish(), children: "\u5B8C\u6210" }),
1273
+ hint !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "her-muted", children: hint })
1274
+ ] });
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-head", children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: "\u8BA9\u5979\u4F4F\u5230\u4F60\u7684\u684C\u9762\u4E0A" }),
1278
+ state.character.onboarded && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn link", onClick: onDone, children: "\u56DE\u5230\u7BA1\u7406\u9875" })
1279
+ ] }),
1280
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "her-steps", children: STEPS.map((name, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { className: `her-step ${i === step ? "on" : i < step ? "done" : ""}`, onClick: () => setStep(i), children: [
1281
+ i + 1,
1282
+ ". ",
1283
+ name
1284
+ ] }, name)) }),
1285
+ step === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1286
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5148\u770B\u770B\u51C6\u5907\u597D\u4E86\u6CA1\u6709" }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: state.model === null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1288
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: "bad" }),
1289
+ "dsh \u8FD8\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B\u3002\u8BF7\u5148\u5728 dsh \u8BBE\u7F6E\u91CC\u914D\u597D\u4E00\u4E2A\u6A21\u578B\uFF08\u6BD4\u5982 DeepSeek\uFF09\uFF0C\u518D\u56DE\u6765\u3002"
1290
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: "ok" }),
1292
+ "\u5927\u8111\u7528\u7684\u6A21\u578B\uFF1A",
1293
+ state.model.provider,
1294
+ " / ",
1295
+ state.model.model
1296
+ ] }) }),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1298
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: toneOf(state.voice.speaking.status) }),
1299
+ "\u5979\u7684\u58F0\u97F3\uFF1A",
1300
+ speakingText(state.voice.speaking.status, state.voice.speaking.service)
1301
+ ] }),
1302
+ (state.voice.speaking.status === "no-key" || state.voice.speaking.status === "no-quota") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(KeyHelp, { state }),
1303
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1304
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: toneOf(state.voice.status) }),
1305
+ "\u542C\u4F60\u8BF4\u8BDD\uFF1A",
1306
+ listeningText(state.voice.status)
1307
+ ] }),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1309
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: "ok" }),
1310
+ "\u7CFB\u7EDF\uFF1A",
1311
+ platformText(state.platform)
1312
+ ] }),
1313
+ nav(state.model !== null, state.model === null ? "\u6CA1\u6709\u6A21\u578B\u5979\u6CA1\u6CD5\u8BF4\u8BDD" : void 0)
1314
+ ] }),
1315
+ step === 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5979\u7684\u8EAB\u4F53" }),
1317
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: '\u5979\u7684\u6837\u5B50\u548C\u58F0\u97F3\u5728\u4E00\u4E2A\u5355\u72EC\u7684\u684C\u9762\u7A0B\u5E8F\u91CC\uFF08"\u8EAB\u4F53"\uFF09\uFF0C\u8FD9\u91CC\u7684 dsh \u662F\u5979\u7684\u5927\u8111\u3002\u4E24\u8FB9\u8FDE\u4E0A\u5979\u624D\u80FD\u51FA\u73B0\u5728\u684C\u9762\u4E0A\u3002' }),
1318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BodyStatus, { state, apply: apply2 }),
1319
+ nav(body.connected, body.connected ? void 0 : "\u4E5F\u53EF\u4EE5\u5148\u8DF3\u8FC7\uFF0C\u4E4B\u540E\u5728\u7BA1\u7406\u9875\u518D\u8FDE"),
1320
+ !body.connected && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "her-row", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn link", onClick: next, children: "\u5148\u8DF3\u8FC7" }) })
1321
+ ] }),
1322
+ step === 2 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u9009\u4E00\u4E2A\u5916\u5F62" }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: "\u5916\u5F62\u5305\u662F\u4E00\u7EC4\u77ED\u89C6\u9891\uFF1A\u5F85\u673A\u3001\u8BF4\u8BDD\u3001\u51E0\u4E2A\u52A8\u4F5C\u3002\u70B9\u4E00\u4E0B\u5C31\u6362\u4E0A\uFF1B\u4E5F\u53EF\u4EE5\u5BFC\u5165\u4F60\u81EA\u5DF1\u751F\u6210\u7684\u7D20\u6750\u3002" }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PackPicker, { state, apply: apply2 }),
1326
+ nav(true)
1327
+ ] }),
1328
+ step === 3 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5979\u662F\u8C01" }),
1330
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PersonaEditor, { state, apply: apply2, onSaved: next, saveLabel: "\u4FDD\u5B58\uFF0C\u4E0B\u4E00\u6B65" }),
1331
+ nav(true)
1332
+ ] }),
1333
+ step === 4 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1334
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5979\u7684\u58F0\u97F3" }),
1335
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VoicePicker, { state, apply: apply2, onSaved: next, saveLabel: "\u7528\u8FD9\u4E2A\u58F0\u97F3\uFF0C\u4E0B\u4E00\u6B65" }),
1336
+ nav(true)
1337
+ ] }),
1338
+ step === 5 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1339
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u8BD5\u8BD5\u548C\u5979\u8BF4\u8BDD" }),
1340
+ body.connected ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MicCheck, { state }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: "\u8EAB\u4F53\u8FDE\u4E0A\u4E4B\u540E\u624D\u80FD\u7528\u9EA6\u514B\u98CE\u3002" }),
1341
+ nav(true)
1342
+ ] }),
1343
+ step === 6 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1344
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u628A\u5979\u653E\u5230\u684C\u9762\u4E0A" }),
1345
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: "\u968F\u65F6\u53EF\u4EE5\u5728\u7BA1\u7406\u9875\u6539\u3002" }),
1346
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ModePicker, { state, apply: apply2 }),
1347
+ !body.connected && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: "\u8EAB\u4F53\u8FDE\u4E0A\u4E4B\u540E\u624D\u80FD\u5207\u6362\u3002" }),
1348
+ nav(true)
1349
+ ] })
1350
+ ] });
1351
+ }
1352
+ var TABS = [["overview", "\u6982\u51B5"], ["settings", "\u8BBE\u7F6E"], ["memory", "\u5979\u8BB0\u5F97\u7684\u4E8B"], ["workshop", "\u5F62\u8C61\u5DE5\u574A"]];
1353
+ function Manage({ state, apply: apply2, onWizard }) {
1354
+ const [tab, setTab] = (0, import_react3.useState)("overview");
1355
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1356
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-head", children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { children: state.character.name }),
1358
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-btn link", onClick: onWizard, children: "\u91CD\u65B0\u8D70\u4E00\u904D\u5411\u5BFC" })
1359
+ ] }),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "her-tabs", children: TABS.map(([id, label]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: `her-tab ${tab === id ? "on" : ""}`, onClick: () => setTab(id), children: label }, id)) }),
1361
+ tab === "overview" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1363
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u8EAB\u4F53" }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BodyStatus, { state, apply: apply2 })
1365
+ ] }),
1366
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5927\u8111" }),
1368
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: state.model === null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: "bad" }),
1370
+ "\u6CA1\u6709\u53EF\u7528\u7684\u6A21\u578B"
1371
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1372
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: "ok" }),
1373
+ state.model.provider,
1374
+ " / ",
1375
+ state.model.model,
1376
+ state.model.chosen ? "" : "\uFF08dsh \u9ED8\u8BA4\uFF09"
1377
+ ] }) }),
1378
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1379
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: toneOf(state.voice.speaking.status) }),
1380
+ "\u5979\u7684\u58F0\u97F3\uFF1A",
1381
+ speakingText(state.voice.speaking.status, state.voice.speaking.service),
1382
+ "\uFF08",
1383
+ voiceName(state.character.voice.id),
1384
+ "\uFF09"
1385
+ ] }),
1386
+ (state.voice.speaking.status === "no-key" || state.voice.speaking.status === "no-quota") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(KeyHelp, { state }),
1387
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Dot, { tone: toneOf(state.voice.status) }),
1389
+ "\u542C\u4F60\u8BF4\u8BDD\uFF1A",
1390
+ listeningText(state.voice.status)
1391
+ ] }),
1392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "her-muted", children: [
1393
+ "\u8BB0\u5F97 ",
1394
+ state.memory.facts.length,
1395
+ " \u4EF6\u5173\u4E8E\u4F60\u7684\u4E8B\u3001",
1396
+ state.memory.episodes.length,
1397
+ " \u4E2A\u4E8B\u4EF6\uFF0C\u5199\u4E86 ",
1398
+ state.memory.diary.length,
1399
+ " \u5929\u65E5\u8BB0\u3002"
1400
+ ] }),
1401
+ state.heard !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "her-faint", children: [
1402
+ "\u6700\u8FD1\u542C\u5230\uFF1A\u300C",
1403
+ state.heard.text,
1404
+ "\u300D"
1405
+ ] })
1406
+ ] }),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "her-muted", children: '\u6539\u4EBA\u8BBE\u3001\u6362\u58F0\u97F3\u3001\u5916\u5F62\u3001\u6A21\u578B\uFF0C\u90FD\u5728"\u8BBE\u7F6E"\u91CC\uFF1B\u5979\u8BB0\u4F4F\u4E86\u4EC0\u4E48\uFF0C\u5728"\u5979\u8BB0\u5F97\u7684\u4E8B"\u91CC\u3002' })
1408
+ ] }),
1409
+ tab === "settings" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Settings, { state, apply: apply2 }),
1410
+ tab === "memory" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MemoryView, { state, apply: apply2 }),
1411
+ tab === "workshop" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Workshop, { state, apply: apply2 })
1412
+ ] });
1413
+ }
1414
+ var SECTIONS = [["persona", "\u4EBA\u8BBE"], ["voice", "\u58F0\u97F3"], ["look", "\u5916\u5F62"], ["model", "\u6A21\u578B"], ["place", "\u653E\u5728\u54EA\u91CC"]];
1415
+ function Settings({ state, apply: apply2 }) {
1416
+ const jump = (id) => document.getElementById(`her-set-${id}`)?.scrollIntoView({ behavior: "smooth", block: "start" });
1417
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "her-steps", children: SECTIONS.map(([id, label]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "her-step", onClick: () => jump(id), children: label }, id)) }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", id: "her-set-persona", children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u4EBA\u8BBE" }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PersonaEditor, { state, apply: apply2 })
1422
+ ] }),
1423
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", id: "her-set-voice", children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u58F0\u97F3" }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VoicePicker, { state, apply: apply2, saveLabel: "\u7528\u8FD9\u4E2A\u58F0\u97F3" })
1426
+ ] }),
1427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", id: "her-set-look", children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u5916\u5F62" }),
1429
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PackPicker, { state, apply: apply2 }),
1430
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ScenePicker, { state, apply: apply2 })
1431
+ ] }),
1432
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", id: "her-set-model", children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u6A21\u578B" }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ModelPicker, { state, apply: apply2 })
1435
+ ] }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "her-card", id: "her-set-place", children: [
1437
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "\u653E\u5728\u54EA\u91CC" }),
1438
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ModePicker, { state, apply: apply2 })
1439
+ ] })
1440
+ ] });
1441
+ }
1442
+ function toneOf(status) {
1443
+ if (status === "ready") return "ok";
1444
+ if (status === "off") return "off";
1445
+ if (status === "no-key" || status === "no-quota" || status === "failed") return "bad";
1446
+ return "warn";
1447
+ }
1448
+ function speakingText(status, service) {
1449
+ const name = service === "edge" ? "\u5FAE\u8F6F Edge" : "\u963F\u91CC\u4E91\u767E\u70BC";
1450
+ switch (status) {
1451
+ case "ready":
1452
+ return `\u5DF2\u5C31\u7EEA\uFF08${name}\uFF09`;
1453
+ case "checking":
1454
+ return `\u6B63\u5728\u8FDE\u63A5${name}`;
1455
+ case "installing":
1456
+ return "\u6B63\u5728\u51C6\u5907 Edge \u8BED\u97F3\u7EC4\u4EF6\uFF08\u7B2C\u4E00\u6B21\u7528\uFF0C\u4E00\u4E24\u5206\u949F\uFF09";
1457
+ case "no-key":
1458
+ return "\u8FD8\u6CA1\u6709\u767E\u70BC\u7684 API key\uFF0C\u73B0\u5728\u53EA\u7528\u6587\u5B57\u56DE\u7B54";
1459
+ case "no-quota":
1460
+ return "\u767E\u70BC\u7684\u989D\u5EA6\u7528\u5B8C\u4E86\uFF0C\u73B0\u5728\u53EA\u7528\u6587\u5B57\u56DE\u7B54";
1461
+ case "failed":
1462
+ return `${name}\u7684\u8BED\u97F3\u51FA\u9519\u4E86\uFF0C\u6682\u65F6\u53EA\u7528\u6587\u5B57\uFF08\u4F1A\u81EA\u52A8\u91CD\u8BD5\uFF09`;
1463
+ case "off":
1464
+ return "\u5DF2\u5173\u95ED\uFF0C\u53EA\u7528\u6587\u5B57";
1465
+ default:
1466
+ return status;
1467
+ }
1468
+ }
1469
+ function listeningText(status) {
1470
+ switch (status) {
1471
+ case "ready":
1472
+ return "\u5DF2\u5C31\u7EEA\uFF08\u672C\u5730\u8BC6\u522B\uFF09";
1473
+ case "downloading":
1474
+ return "\u6B63\u5728\u4E0B\u8F7D\u8BC6\u522B\u6A21\u578B\uFF08\u7EA6 170 MB\uFF0C\u53EA\u4E0B\u4E00\u6B21\uFF09";
1475
+ case "loading":
1476
+ return "\u6B63\u5728\u52A0\u8F7D";
1477
+ case "failed":
1478
+ return "\u51FA\u9519\u4E86\uFF0C\u6682\u65F6\u53EA\u80FD\u6253\u5B57";
1479
+ default:
1480
+ return status;
1481
+ }
1482
+ }
1483
+ function platformText(platform) {
1484
+ if (platform === "darwin") return "macOS";
1485
+ if (platform === "win32") return "Windows";
1486
+ return platform;
1487
+ }
1488
+
1489
+ // src/client/styles.ts
1490
+ var v = (name, fallback) => `var(--dsw-alias-${name}, ${fallback})`;
1491
+ var TEXT = v("label-primary", "CanvasText");
1492
+ var MUTED = v("label-secondary", "GrayText");
1493
+ var FAINT = v("label-tertiary", "GrayText");
1494
+ var LINE = v("border-l2", "rgba(127,127,127,.22)");
1495
+ var LAYER1 = v("bg-layer-1", "rgba(127,127,127,.06)");
1496
+ var LAYER2 = v("bg-layer-2", "rgba(127,127,127,.10)");
1497
+ var HOVER = v("interactive-bg-hover", "rgba(127,127,127,.12)");
1498
+ var DANGER = v("state-error-primary", "#d14343");
1499
+ var WARN = v("state-warn-primary", "#b7791f");
1500
+ var OK = v("state-success-primary", "#2f855a");
1501
+ var ACCENT = v("button-primary-fill", "#4d6bfe");
1502
+ var ON_ACCENT = v("label-primary-foreground", "#fff");
1503
+ var STYLES = `
1504
+ .her-page{box-sizing:border-box;height:100%;overflow:auto;padding:20px 24px 40px;color:${TEXT};font-size:13px;line-height:1.55}
1505
+ .her-page *{box-sizing:border-box}
1506
+ .her-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:14px}
1507
+ .her-head h1{font-size:18px;font-weight:600;margin:0}
1508
+ .her-muted{color:${MUTED}}.her-faint{color:${FAINT}}
1509
+ .her-ok{color:${OK}}.her-warn{color:${WARN}}.her-danger{color:${DANGER}}
1510
+ .her-card{background:${LAYER1};border-radius:12px;padding:14px 16px;margin-bottom:12px;max-width:760px}
1511
+ .her-card h2{font-size:15px;font-weight:600;margin:0 0 6px}
1512
+ .her-card p{margin:4px 0}
1513
+ .her-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:8px 0}
1514
+ .her-fold{display:flex;align-items:center;gap:10px;width:100%;border:none;background:none;padding:0;color:${TEXT};font:inherit;cursor:pointer;text-align:left}
1515
+ .her-fold h2{margin:0}
1516
+ .her-fold-arrow{flex:none;display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:50%;background:${LAYER2};color:${ACCENT};transition:transform .15s,background .15s}
1517
+ .her-fold-arrow.open{transform:rotate(90deg)}
1518
+ .her-fold:hover .her-fold-arrow{background:${HOVER}}
1519
+ .her-fold:focus-visible{outline:2px solid ${ACCENT};outline-offset:4px;border-radius:6px}
1520
+ .her-fold-sum{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}
1521
+ .her-examples{margin-top:12px}
1522
+ .her-example-row{display:flex;gap:10px;flex-wrap:wrap}
1523
+ .her-example{margin:0;width:150px}
1524
+ .her-example img,.her-example video,.her-example-empty{width:150px;height:110px;object-fit:cover;border-radius:8px;background:${LAYER2};display:block}
1525
+ .her-example-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;border:1px dashed ${LINE};color:${FAINT};font-size:11px;text-align:center;padding:6px}
1526
+ .her-example-empty code{font-size:10px;word-break:break-all}
1527
+ .her-example figcaption{font-size:11px;color:${MUTED};margin-top:3px}
1528
+ .her-scenes{margin-top:14px;border-top:1px solid ${LINE};padding-top:10px}
1529
+ .her-scene{margin:4px 0}.her-scene label{min-width:7em}.her-scene .her-muted{flex:1}
1530
+ .her-btn{border:1px solid ${LINE};background:${LAYER2};color:${TEXT};border-radius:8px;padding:6px 12px;font:inherit;cursor:pointer}
1531
+ .her-btn:hover{background:${HOVER}}
1532
+ .her-btn:disabled{opacity:.5;cursor:default}
1533
+ .her-btn.primary{background:${ACCENT};border-color:${ACCENT};color:${ON_ACCENT}}
1534
+ .her-btn.small{padding:2px 8px;font-size:12px}
1535
+ .her-btn.link{border:none;background:none;color:${ACCENT};padding:2px 4px}
1536
+ .her-input,.her-textarea,.her-select{width:100%;border:1px solid ${LINE};background:${LAYER2};color:${TEXT};border-radius:8px;padding:7px 10px;font:inherit}
1537
+ .her-textarea{min-height:120px;resize:vertical}
1538
+ .her-label{display:block;font-size:12px;color:${MUTED};margin:10px 0 4px}
1539
+ .her-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:8px;margin:8px 0}
1540
+ .her-choice{border:1px solid ${LINE};background:${LAYER2};border-radius:10px;padding:10px;cursor:pointer;text-align:left;color:${TEXT};font:inherit}
1541
+ .her-choice:hover{background:${HOVER}}
1542
+ .her-choice.on{border-color:${ACCENT};box-shadow:0 0 0 1px ${ACCENT}}
1543
+ .her-choice small{display:block;color:${MUTED};margin-top:2px}
1544
+ .her-steps{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 14px;max-width:760px}
1545
+ .her-step{font-size:12px;padding:3px 10px;border-radius:999px;background:${LAYER1};color:${MUTED};border:none;cursor:pointer;font-family:inherit}
1546
+ .her-step.on{background:${ACCENT};color:${ON_ACCENT}}
1547
+ .her-step.done{color:${OK}}
1548
+ .her-tabs{display:flex;gap:4px;margin-bottom:12px;flex-wrap:wrap}
1549
+ .her-tab{border:none;background:none;color:${MUTED};padding:6px 10px;border-radius:8px;cursor:pointer;font:inherit}
1550
+ .her-tab.on{background:${LAYER2};color:${TEXT};font-weight:600}
1551
+ .her-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;background:${FAINT};vertical-align:0}
1552
+ .her-dot.ok{background:${OK}}.her-dot.warn{background:${WARN}}.her-dot.bad{background:${DANGER}}
1553
+ .her-list{list-style:none;margin:6px 0;padding:0}
1554
+ .her-list li{display:flex;gap:8px;align-items:baseline;padding:6px 0;border-bottom:1px solid ${LINE}}
1555
+ .her-list li:last-child{border-bottom:none}
1556
+ .her-list li .grow{flex:1;min-width:0;word-break:break-word}
1557
+ .her-tag{font-size:11px;padding:0 6px;border-radius:999px;background:${LAYER2};color:${MUTED};white-space:nowrap}
1558
+ .her-tag.warn{color:${WARN}}.her-tag.bad{color:${DANGER}}.her-tag.ok{color:${OK}}
1559
+ .her-chat{background:${LAYER2};border-radius:10px;padding:8px 10px;margin:8px 0;max-height:220px;overflow:auto}
1560
+ .her-chat div{margin:3px 0}
1561
+ .her-chat .you{color:${MUTED}}
1562
+ .her-report{margin:6px 0;padding-left:0;list-style:none}
1563
+ .her-report li{margin:2px 0}
1564
+ .her-progress{height:6px;background:${LAYER2};border-radius:3px;overflow:hidden;margin:6px 0}
1565
+ .her-progress div{height:100%;background:${ACCENT};transition:width .3s}
1566
+ .her-error{color:${DANGER};margin:6px 0}
1567
+ `;
1568
+
1569
+ // src/client/index.tsx
1570
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1571
+ var PACKAGE_NAME = "dsh-her";
1572
+ var inject = ["slots", "locale"];
1573
+ var PANEL_ID = "dsh-her";
1574
+ var NS = "dsh-her";
1575
+ function HerIcon({ size }) {
1576
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z" }) });
1577
+ }
1578
+ function apply(ctx) {
1579
+ ctx.effect(() => ctx.locale.register(NS, { zh: { panel: "\u5979" }, en: { panel: "Her" } }), "dsh-her: dictionaries");
1580
+ ctx.effect(() => {
1581
+ const style = document.createElement("style");
1582
+ style.dataset.dshPlugin = PACKAGE_NAME;
1583
+ style.textContent = STYLES;
1584
+ document.head.append(style);
1585
+ return () => {
1586
+ style.remove();
1587
+ };
1588
+ }, "dsh-her: styles");
1589
+ const t = ctx.locale.bind(NS);
1590
+ ctx.slots.inject("main", () => ctx.slots.register({ name: "main", key: PANEL_ID, locale: NS }, HerPage));
1591
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
1592
+ name: "sidebar.panellist",
1593
+ id: PANEL_ID,
1594
+ order: 31,
1595
+ locale: NS,
1596
+ label: () => t("panel")
1597
+ }, HerIcon));
1598
+ }
1599
+
1600
+ return module.exports;
1601
+ }
1602
+ });