dsh-live2dwaifu 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -380,6 +380,17 @@ curl -X POST http://127.0.0.1:<端口>/dsh-live2d/api/push \
380
380
 
381
381
  版本号怎么走:动了功能和结构就抬第二位(1.2.0),只补洞和小修就抬第三位(1.2.1)。
382
382
 
383
+ ### 1.2.2
384
+
385
+ - 人设卡、音色、语料三个页面的按钮和说明改成通用说法:保存/追加保存/覆盖保存/放弃修改/选择文件/播放/关掉。
386
+ - 状态提示统一成「读取中/已读取/保存中/已保存/保存失败」,不再出现「读…」「存好了」这种。
387
+ - 语料页的示例和解析结果里,标签从「他/你」换成「对方/角色」。
388
+
389
+ ### 1.2.1
390
+
391
+ - 人设卡、音色、语料三个页面右上角各加了一个「关掉」。之前只能点外层那条栏上的按钮,页面本身没出口。
392
+ - 外层那条栏的按钮改成「× 关掉」,更好认。
393
+
383
394
  ### 1.2.0
384
395
 
385
396
  - 语料吃的文件多了:`.md`、`.csv`、`.srt`、`.vtt`、`.ass`。字幕会自动去掉序号、时间轴和样式标签。
@@ -37,37 +37,43 @@
37
37
  .tips { color:#6f7783; font-size:11.5px; line-height:1.8; margin-top:10px; }
38
38
  code { background:rgba(255,255,255,.06); border-radius:4px; padding:1px 5px; font-size:11px; }
39
39
  .state { color:var(--dim); font-size:11.5px; margin-left:auto; }
40
+
41
+ .sheet-close { position: fixed; right: 18px; top: 14px; z-index: 20;
42
+ padding: 7px 14px; border-radius: 9px; border: 1px solid rgba(255,255,255,.16);
43
+ background: rgba(30,36,45,.92); color: #dfe4ec; font: inherit; font-size: 12px;
44
+ cursor: pointer; backdrop-filter: blur(6px); }
45
+ .sheet-close:hover { background: rgba(46,55,68,.96); }
40
46
  </style>
41
47
  </head>
42
48
  <body>
43
49
 
44
50
  <h1>语料</h1>
45
- <p class="sub">把角色说过的话喂进来,之后说话就照这个调子。不用排版整齐,能读懂就行。</p>
51
+ <p class="sub">导入角色说过的话,作为生成回复时的语气参考。不要求格式整齐,能解析即可。</p>
46
52
 
47
53
  <div class="row">
48
54
  <select id="models"></select>
49
55
  <div class="btns">
50
- <button class="act" id="pick">选文件</button>
51
- <button class="act primary" id="append">加进去</button>
52
- <button class="act" id="replace">替换成这些</button>
56
+ <button class="act" id="pick">选择文件</button>
57
+ <button class="act primary" id="append">追加保存</button>
58
+ <button class="act" id="replace">覆盖保存</button>
53
59
  </div>
54
60
  <span class="state" id="state"></span>
55
61
  </div>
56
62
 
57
63
  <div class="cols">
58
64
  <div class="col box">
59
- <h2>把话贴进来</h2>
60
- <textarea id="raw" spellcheck="false" placeholder="随便贴,几种写法都认:
65
+ <h2>粘贴文本</h2>
66
+ <textarea id="raw" spellcheck="false" placeholder="直接粘贴,支持以下几种写法:
61
67
 
62
- 他:今天吃啥
63
- 你:锅里还有,热的。
68
+ 对方:今天忙吗
69
+ 角色:刚坐下,手还是湿的。
64
70
 
65
- 或者只贴角色的台词,一行一句:
66
- 锅里还有,热的。
67
- 先坐下。水在桌上。
71
+ 或者只粘贴角色台词,一行一句:
72
+ 刚坐下,手还是湿的。
73
+ 水在桌上。
68
74
 
69
- 也支持带引号的一句:「嗯。我也是。」"></textarea>
70
- <div id="drop">拖文件进来,或者点一下选(可以多选)<br /><span style="font-size:11px">文本、字幕、表格都认;图片和视频见下面</span></div>
75
+ 也支持带引号的一句:「嗯,我也是。」"></textarea>
76
+ <div id="drop">拖放文件到此处,或点击选择文件(支持多选)<br /><span style="font-size:11px">支持文本、字幕、表格;图片与视频见下方说明</span></div>
71
77
  <input id="files" type="file" multiple accept=".txt,.json,.jsonl,.csv,.md,.srt,.vtt,.ass,image/*,video/*" style="display:none" />
72
78
  <div class="tips">
73
79
  认得的写法:<code>对方:… / 角色:…</code>、<code>「…」</code>、<code>- …</code>、一行一句、
@@ -78,8 +84,8 @@
78
84
  </div>
79
85
 
80
86
  <div class="col box">
81
- <h2>解析出来长这样</h2>
82
- <div class="stat" id="stat">还没有内容</div>
87
+ <h2>解析结果</h2>
88
+ <div class="stat" id="stat">暂无内容</div>
83
89
  <div id="preview"></div>
84
90
  <div class="tips" id="saved"></div>
85
91
  </div>
@@ -162,10 +168,10 @@
162
168
  box.innerHTML = '';
163
169
  var show = [];
164
170
  for (var i = 0; i < parsed.pairs.length && show.length < 8; i += 1) {
165
- show.push('他:' + parsed.pairs[i].user + '<br>你:' + parsed.pairs[i].assistant);
171
+ show.push('对方:' + parsed.pairs[i].user + '<br>角色:' + parsed.pairs[i].assistant);
166
172
  }
167
173
  for (var j = 0; j < parsed.lines.length && show.length < 14; j += 1) {
168
- show.push('你:' + parsed.lines[j]);
174
+ show.push('角色:' + parsed.lines[j]);
169
175
  }
170
176
  show.forEach(function (html) {
171
177
  var d = document.createElement('div');
@@ -185,15 +191,15 @@
185
191
 
186
192
  function load() {
187
193
  if (!modelId) return;
188
- $('state').textContent = '读…';
194
+ $('state').textContent = '读取中';
189
195
  fetch('/dsh-live2d/api/corpus?id=' + encodeURIComponent(modelId))
190
196
  .then(function (r) { return r.json(); })
191
197
  .then(function (d) {
192
198
  stored = { lines: d.lines || [], pairs: d.pairs || [] };
193
- $('state').textContent = '读好了';
199
+ $('state').textContent = '已读取';
194
200
  render();
195
201
  })
196
- .catch(function (e) { $('state').textContent = '读不了:' + e.message; });
202
+ .catch(function (e) { $('state').textContent = '读取失败:' + e.message; });
197
203
  }
198
204
 
199
205
  function save(mode) {
@@ -208,10 +214,10 @@
208
214
  };
209
215
  }
210
216
  if (!payload.lines.length && !payload.pairs.length) {
211
- $('state').textContent = '没有可存的内容';
217
+ $('state').textContent = '没有可保存的内容';
212
218
  return;
213
219
  }
214
- $('state').textContent = '存…';
220
+ $('state').textContent = '保存中';
215
221
  fetch('/dsh-live2d/api/corpus', {
216
222
  method: 'POST',
217
223
  headers: { 'content-type': 'application/json' },
@@ -219,13 +225,13 @@
219
225
  })
220
226
  .then(function (r) { return r.json(); })
221
227
  .then(function (d) {
222
- if (!d.ok) { $('state').textContent = '没存上:' + (d.error || '?'); return; }
223
- $('state').textContent = '存好了,共 ' + d.total + ' 条';
228
+ if (!d.ok) { $('state').textContent = '保存失败:' + (d.error || '未知错误'); return; }
229
+ $('state').textContent = '已保存,共 ' + d.total + ' 条';
224
230
  $('raw').value = '';
225
231
  files = [];
226
232
  load();
227
233
  })
228
- .catch(function (e) { $('state').textContent = '没存上:' + e.message; });
234
+ .catch(function (e) { $('state').textContent = '保存失败:' + e.message; });
229
235
  }
230
236
 
231
237
  $('raw').addEventListener('input', reparse);
@@ -256,17 +262,17 @@
256
262
  texts.push(f);
257
263
  });
258
264
  if (skipped.length) {
259
- $('saved').textContent = '这些没读:' + skipped.join('、') + ' —— 图片先留着当形象参考,视频请导出字幕(.srt)或直接贴台词。';
265
+ $('saved').textContent = '未读取:' + skipped.join('、') + '。图片可留作形象参考;视频请先导出字幕(.srt)或直接粘贴台词。';
260
266
  }
261
- if (!texts.length) { $('state').textContent = '没有可读的文本'; return; }
262
- $('state').textContent = '读文件…';
267
+ if (!texts.length) { $('state').textContent = '没有可读取的文本文件'; return; }
268
+ $('state').textContent = '正在读取文件';
263
269
  var left = texts.length;
264
270
  texts.forEach(function (f) {
265
271
  var reader = new FileReader();
266
272
  reader.onload = function () {
267
273
  files.push(String(reader.result || ''));
268
274
  left -= 1;
269
- if (left === 0) { $('state').textContent = '读好了 ' + texts.length + ' 个文件'; reparse(); }
275
+ if (left === 0) { $('state').textContent = '已读取 ' + texts.length + ' 个文件'; reparse(); }
270
276
  };
271
277
  reader.onerror = function () { left -= 1; if (left === 0) reparse(); };
272
278
  reader.readAsText(f, 'utf-8');
@@ -299,10 +305,10 @@
299
305
  });
300
306
  modelId = d.selected || (list[0] && list[0].id) || '';
301
307
  if (modelId) sel.value = modelId;
302
- if (!list.length) { $('state').textContent = '还没有模型'; return; }
308
+ if (!list.length) { $('state').textContent = '尚未导入模型'; return; }
303
309
  load();
304
310
  })
305
- .catch(function (e) { $('state').textContent = '读不了模型表:' + e.message; });
311
+ .catch(function (e) { $('state').textContent = '读取模型列表失败:' + e.message; });
306
312
 
307
313
  $('models').addEventListener('change', function (e) {
308
314
  modelId = e.target.value;
@@ -310,5 +316,15 @@
310
316
  });
311
317
  })();
312
318
  </script>
319
+ <button id="sheet-close" class="sheet-close">关掉</button>
320
+
321
+ <script>
322
+ (function () {
323
+ var b = document.getElementById('sheet-close');
324
+ if (b) b.onclick = function () {
325
+ try { window.parent.postMessage({ type: 'dsh-live2d/close' }, '*'); } catch (e) {}
326
+ };
327
+ })();
328
+ </script>
313
329
  </body>
314
330
  </html>
@@ -35,26 +35,32 @@
35
35
  button.act:hover { filter:brightness(1.12); }
36
36
  .state { color:var(--dim); font-size:11px; margin-left:auto; }
37
37
  .tip { color:#6f7783; font-size:11px; margin-top:10px; line-height:1.7; }
38
+
39
+ .sheet-close { position: fixed; right: 18px; top: 14px; z-index: 20;
40
+ padding: 7px 14px; border-radius: 9px; border: 1px solid rgba(255,255,255,.16);
41
+ background: rgba(30,36,45,.92); color: #dfe4ec; font: inherit; font-size: 12px;
42
+ cursor: pointer; backdrop-filter: blur(6px); }
43
+ .sheet-close:hover { background: rgba(46,55,68,.96); }
38
44
  </style>
39
45
  </head>
40
46
  <body>
41
47
 
42
48
  <div class="left">
43
49
  <div class="card"><iframe id="live" src="/dsh-live2d/viewer?embed=1"></iframe></div>
44
- <div class="tip">左边就是她本人。改完存一下,她下一句就按新的人设说。</div>
50
+ <div class="tip">左侧是当前模型预览。保存后,下一次回答就会按新的设定生成。</div>
45
51
  </div>
46
52
 
47
53
  <div class="panel">
48
54
  <h1>人设卡</h1>
49
- <p class="sub">她是谁、怎么说话,全在这段字里。</p>
55
+ <p class="sub">角色的身份、性格、说话方式,都写在这里。</p>
50
56
 
51
57
  <div class="group">
52
- <h2>配给哪个模型</h2>
58
+ <h2>目标模型</h2>
53
59
  <select id="models"></select>
54
60
  </div>
55
61
 
56
62
  <div class="group">
57
- <h2>人设</h2>
63
+ <h2>角色设定</h2>
58
64
  <textarea id="persona" spellcheck="false"></textarea>
59
65
  </div>
60
66
 
@@ -63,18 +69,18 @@
63
69
  <label class="switch">
64
70
  <input type="checkbox" id="isolation" checked />
65
71
  <span>
66
- <b>让她待在自己的世界里</b>
67
- <em>打开后,她不知道什么是程序、界面、插件、模型。问起来就当你在她身边,不提“系统”“设置”“网页”。关掉她就会老老实实承认自己是插件里的角色。</em>
72
+ <b>角色视角隔离</b>
73
+ <em>打开后,角色不会提到程序、界面、插件、模型这些词,按设定世界的视角回答。关闭后角色会承认自己是插件里的角色。</em>
68
74
  </span>
69
75
  </label>
70
76
  </div>
71
77
 
72
78
  <div class="foot">
73
- <button class="act primary" id="save">存</button>
74
- <button class="act" id="reload">撤销改动</button>
79
+ <button class="act primary" id="save">保存</button>
80
+ <button class="act" id="reload">放弃修改</button>
75
81
  <span class="state" id="state"></span>
76
82
  </div>
77
- <div class="tip">存在 <code>~/.dsh/dsh-live2d/models/&lt;id&gt;/meta.json</code> 的 <code>persona</code> 字段里,想手改也行。</div>
83
+ <div class="tip">保存在 <code>~/.dsh/dsh-live2d/models/&lt;id&gt;/meta.json</code> 的 <code>persona</code> 字段,也可以直接编辑该文件。</div>
78
84
  </div>
79
85
 
80
86
  <script>
@@ -90,20 +96,20 @@
90
96
 
91
97
  function load() {
92
98
  if (!modelId) return;
93
- state.textContent = '读…';
99
+ state.textContent = '读取中';
94
100
  fetch('/dsh-live2d/api/persona?id=' + encodeURIComponent(modelId))
95
101
  .then(function (r) { return r.json(); })
96
102
  .then(function (d) {
97
103
  area.value = d.persona || '';
98
104
  iso.checked = d.isolation !== false;
99
- state.textContent = d.custom ? '这份是手写的' : '还是默认那份';
105
+ state.textContent = d.custom ? '已自定义' : '使用默认设定';
100
106
  })
101
- .catch(function (e) { state.textContent = '读不了:' + e.message; });
107
+ .catch(function (e) { state.textContent = '读取失败:' + e.message; });
102
108
  }
103
109
 
104
110
  function save() {
105
111
  if (!modelId) return;
106
- state.textContent = '存…';
112
+ state.textContent = '保存中';
107
113
  fetch('/dsh-live2d/api/persona', {
108
114
  method: 'POST',
109
115
  headers: { 'content-type': 'application/json' },
@@ -111,12 +117,12 @@
111
117
  })
112
118
  .then(function (r) { return r.json(); })
113
119
  .then(function (d) {
114
- state.textContent = d.ok ? '存好了' : '没存上:' + (d.error || '?');
120
+ state.textContent = d.ok ? '已保存' : '保存失败:' + (d.error || '未知错误');
115
121
  if (d.ok) {
116
122
  frame.src = '/dsh-live2d/viewer?embed=1&t=' + Date.now();
117
123
  }
118
124
  })
119
- .catch(function (e) { state.textContent = '没存上:' + e.message; });
125
+ .catch(function (e) { state.textContent = '保存失败:' + e.message; });
120
126
  }
121
127
 
122
128
  $('save').addEventListener('click', save);
@@ -139,10 +145,20 @@
139
145
  });
140
146
  modelId = d.selected || (list[0] && list[0].id) || '';
141
147
  if (modelId) sel.value = modelId;
142
- if (!list.length) { state.textContent = '还没有模型'; return; }
148
+ if (!list.length) { state.textContent = '尚未导入模型'; return; }
143
149
  load();
144
150
  })
145
- .catch(function (e) { state.textContent = '读不了模型表:' + e.message; });
151
+ .catch(function (e) { state.textContent = '读取模型列表失败:' + e.message; });
152
+ })();
153
+ </script>
154
+ <button id="sheet-close" class="sheet-close">关掉</button>
155
+
156
+ <script>
157
+ (function () {
158
+ var b = document.getElementById('sheet-close');
159
+ if (b) b.onclick = function () {
160
+ try { window.parent.postMessage({ type: 'dsh-live2d/close' }, '*'); } catch (e) {}
161
+ };
146
162
  })();
147
163
  </script>
148
164
  </body>
package/assets/tone.html CHANGED
@@ -38,26 +38,32 @@
38
38
  .foot { display:flex; align-items:center; gap:10px; }
39
39
  .state { color:var(--dim); font-size:11px; margin-left:auto; }
40
40
  .tip { color:#6f7783; font-size:11px; margin-top:10px; line-height:1.7; }
41
+
42
+ .sheet-close { position: fixed; right: 18px; top: 14px; z-index: 20;
43
+ padding: 7px 14px; border-radius: 9px; border: 1px solid rgba(255,255,255,.16);
44
+ background: rgba(30,36,45,.92); color: #dfe4ec; font: inherit; font-size: 12px;
45
+ cursor: pointer; backdrop-filter: blur(6px); }
46
+ .sheet-close:hover { background: rgba(46,55,68,.96); }
41
47
  </style>
42
48
  </head>
43
49
  <body>
44
50
 
45
51
  <div class="left">
46
52
  <div class="card"><iframe id="live" src="/dsh-live2d/viewer?embed=1"></iframe></div>
47
- <div class="tip">没声音就先点她一下——浏览器要一次点击才允许出声。</div>
53
+ <div class="tip">没有声音时,先点击一次页面——浏览器要求先有用户操作才允许播放。</div>
48
54
  </div>
49
55
 
50
56
  <div class="panel">
51
- <h1>音色</h1>
52
- <p class="sub">一个字一声短音。音量 0 就是安静,只留气泡和口型。</p>
57
+ <h1>声音</h1>
58
+ <p class="sub">每说一个字发一声短音。音量调到 0 即静音,只保留文字和口型。</p>
53
59
 
54
60
  <div class="group">
55
- <h2>配给哪个模型</h2>
61
+ <h2>目标模型</h2>
56
62
  <select id="models"></select>
57
63
  </div>
58
64
 
59
65
  <div class="group">
60
- <h2>音色</h2>
66
+ <h2>音色预设</h2>
61
67
  <div class="presets" id="presets">
62
68
  <button data-p="soft" class="on">柔和</button>
63
69
  <button data-p="crisp">清脆</button>
@@ -76,19 +82,19 @@
76
82
  </div>
77
83
 
78
84
  <div class="group">
79
- <h2>试一句</h2>
85
+ <h2>试听</h2>
80
86
  <div class="typebox">
81
87
  <input id="text" value="窗台那盆,红了。" />
82
- <button class="act primary" id="say">说</button>
88
+ <button class="act primary" id="say">播放</button>
83
89
  </div>
84
90
  </div>
85
91
 
86
92
  <div class="foot">
87
- <button class="act primary" id="save">存到这只模型</button>
93
+ <button class="act primary" id="save">保存</button>
88
94
  <button class="act" id="reset">恢复默认</button>
89
95
  <span class="state" id="state"></span>
90
96
  </div>
91
- <div class="tip">每只模型各存一份。换模型、换人,声音跟着换。</div>
97
+ <div class="tip">每只模型单独保存一份音色设置。</div>
92
98
  </div>
93
99
 
94
100
  <script>
@@ -179,12 +185,12 @@
179
185
  voice = Object.assign({}, DEFAULTS);
180
186
  show();
181
187
  pushTone();
182
- state.textContent = '已恢复默认,还没存';
188
+ state.textContent = '已恢复默认值,尚未保存';
183
189
  });
184
190
 
185
191
  $('save').addEventListener('click', function () {
186
192
  if (!modelId) return;
187
- state.textContent = '存…';
193
+ state.textContent = '保存中';
188
194
  fetch('/dsh-live2d/api/voice', {
189
195
  method: 'POST',
190
196
  headers: { 'content-type': 'application/json' },
@@ -192,9 +198,9 @@
192
198
  })
193
199
  .then(function (r) { return r.json(); })
194
200
  .then(function (d) {
195
- state.textContent = d.ok ? '存好了' : '没存上:' + (d.error || '?');
201
+ state.textContent = d.ok ? '已保存' : '保存失败:' + (d.error || '未知错误');
196
202
  })
197
- .catch(function (e) { state.textContent = '没存上:' + e.message; });
203
+ .catch(function (e) { state.textContent = '保存失败:' + e.message; });
198
204
  });
199
205
 
200
206
  sel.addEventListener('change', function () {
@@ -204,7 +210,7 @@
204
210
 
205
211
  function loadVoice(alsoSwitch) {
206
212
  if (!modelId) return;
207
- state.textContent = '读…';
213
+ state.textContent = '读取中';
208
214
  if (alsoSwitch) {
209
215
  fetch('/dsh-live2d/api/select', {
210
216
  method: 'POST',
@@ -221,9 +227,9 @@
221
227
  show();
222
228
  pushTone();
223
229
  setTimeout(pushTone, 900);
224
- state.textContent = '读好了';
230
+ state.textContent = '已读取';
225
231
  })
226
- .catch(function (e) { state.textContent = '读不了:' + e.message; });
232
+ .catch(function (e) { state.textContent = '读取失败:' + e.message; });
227
233
  }
228
234
 
229
235
  fetch('/dsh-live2d/api/models')
@@ -240,17 +246,27 @@
240
246
  modelId = d.selected || (list[0] && list[0].id) || '';
241
247
  if (modelId) sel.value = modelId;
242
248
  if (!list.length) {
243
- state.textContent = '还没有模型';
249
+ state.textContent = '尚未导入模型';
244
250
  return;
245
251
  }
246
252
  loadVoice(false);
247
253
  })
248
- .catch(function (e) { state.textContent = '读不了模型表:' + e.message; });
254
+ .catch(function (e) { state.textContent = '读取模型列表失败:' + e.message; });
249
255
 
250
256
  frame.addEventListener('load', function () {
251
257
  pushTone();
252
258
  });
253
259
  })();
254
260
  </script>
261
+ <button id="sheet-close" class="sheet-close">关掉</button>
262
+
263
+ <script>
264
+ (function () {
265
+ var b = document.getElementById('sheet-close');
266
+ if (b) b.onclick = function () {
267
+ try { window.parent.postMessage({ type: 'dsh-live2d/close' }, '*'); } catch (e) {}
268
+ };
269
+ })();
270
+ </script>
255
271
  </body>
256
272
  </html>
package/lib/client.js CHANGED
@@ -101,6 +101,7 @@
101
101
  const onMsg = (e) => {
102
102
  const d = e.data || {};
103
103
  if (d.type === 'dsh-live2d/open' && d.sheet) setSheet(String(d.sheet));
104
+ if (d.type === 'dsh-live2d/close') setSheet(null);
104
105
  };
105
106
  window.addEventListener('message', onMsg);
106
107
  return () => window.removeEventListener('message', onMsg);
@@ -265,7 +266,7 @@
265
266
  'div',
266
267
  { className: 'dsh-l2d-sheet-bar' },
267
268
  h('span', null, SHEET_TITLE[sheet] || sheet),
268
- h('button', { onClick: () => setSheet(null) }, '关掉'),
269
+ h('button', { onClick: () => setSheet(null) }, '× 关掉'),
269
270
  ),
270
271
  h('iframe', { className: 'dsh-l2d-sheet-body', src: BASE + '/' + sheet }),
271
272
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-live2dwaifu",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "给 DSH 网页右下角请一个 Live2D 角色:会眨眼、会呼吸、视线跟着鼠标、点她有反应、说话时嘴动、心情变了眉毛跟着动。模型不用内置——谁装谁放自己的文件夹;支持自定义 .exp3.json 表情和 .motion3.json 动作,卡片能拖能缩,背景可换。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",