dsh-live2dwaifu 1.3.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/assets/corpus.html +4 -5
- package/assets/mobile.html +1 -0
- package/assets/tone.html +19 -12
- package/assets/viewer.js +0 -1
- package/lib/client.js +20 -3
- package/lib/index.js +12 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -380,6 +380,25 @@ 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
|
+
### 2.0.1
|
|
384
|
+
|
|
385
|
+
- 修:拖动时没限制边界,卡片能被推到屏幕外,标题栏抓不回来。现在拖动过程中就夹住,标题栏永远露在视口里。
|
|
386
|
+
- 加:双击标题栏回到右下角默认位置。
|
|
387
|
+
|
|
388
|
+
### 2.0.0
|
|
389
|
+
|
|
390
|
+
- 修:`ISOLATION` 这个变量在 1.3.0 被误删,`/api/chat` 全部返回 500——那之后她其实一句都说不出来。这是这版最主要的问题。
|
|
391
|
+
- 修:补回 1.3.1 里漏掉的语料接口(之前保存语料会 404)。
|
|
392
|
+
- 修:调音页的下拉框改成只试听,不再顺手把正在显示的模型换掉;要换模型另点「切到这只模型」。
|
|
393
|
+
- 修:卡片位置存在本机,窗口缩小后可能跑到屏幕外,现在重开时自动收回可见范围。
|
|
394
|
+
- 修:页面切到后台时暂停轮询,不再空跑请求。
|
|
395
|
+
- 修:所有接受模型 id 的接口加了字符校验,避免拼出奇怪路径。
|
|
396
|
+
- 改:对话要求补一条——不要每句都反问,不要用「哈哈」「嗯嗯」凑字数。
|
|
397
|
+
- 清:删掉几个没用到的常量、开关和恒真判断。
|
|
398
|
+
|
|
399
|
+
<details>
|
|
400
|
+
<summary>2.0.0 之前的记录(黑历史,留着自嘲)</summary>
|
|
401
|
+
|
|
383
402
|
### 1.3.1
|
|
384
403
|
|
|
385
404
|
- 修:语料接口整个漏掉了(页面打得开,存不进去,返回 404)。补回来。
|
|
@@ -457,6 +476,8 @@ curl -X POST http://127.0.0.1:<端口>/dsh-live2d/api/push \
|
|
|
457
476
|
- 人设卡编辑页、音色调色页。
|
|
458
477
|
- 对外接口:`/api/chat`、`/api/push`、`/api/poll`。
|
|
459
478
|
|
|
479
|
+
</details>
|
|
480
|
+
|
|
460
481
|
## 反馈
|
|
461
482
|
|
|
462
483
|
遇到问题、想要新功能、觉得哪句话还是像 AI,都可以写信到 **FujiwaraReisen@outlook.com**。
|
package/assets/corpus.html
CHANGED
|
@@ -147,11 +147,10 @@
|
|
|
147
147
|
var said = unquote(m[2]);
|
|
148
148
|
if (!said) continue;
|
|
149
149
|
if (USERS.test(who)) { pending = said; continue; }
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
150
|
+
// 不是「他/我/用户」的说话人,一律当角色
|
|
151
|
+
pairs.push({ user: pending || '', assistant: said });
|
|
152
|
+
pending = null;
|
|
153
|
+
continue;
|
|
155
154
|
}
|
|
156
155
|
var t = unquote(line);
|
|
157
156
|
if (t) lines.push(t);
|
package/assets/mobile.html
CHANGED
package/assets/tone.html
CHANGED
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
<div class="foot">
|
|
93
93
|
<button class="act primary" id="save">保存</button>
|
|
94
|
+
<button class="act" id="useModel">切到这只模型</button>
|
|
94
95
|
<button class="act" id="reset">恢复默认</button>
|
|
95
96
|
<span class="state" id="state"></span>
|
|
96
97
|
</div>
|
|
@@ -205,21 +206,27 @@
|
|
|
205
206
|
|
|
206
207
|
sel.addEventListener('change', function () {
|
|
207
208
|
modelId = sel.value;
|
|
208
|
-
loadVoice(
|
|
209
|
+
loadVoice();
|
|
209
210
|
});
|
|
210
211
|
|
|
211
|
-
function
|
|
212
|
+
$('useModel').addEventListener('click', function () {
|
|
212
213
|
if (!modelId) return;
|
|
213
|
-
state.textContent = '
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
state.textContent = '切换中';
|
|
215
|
+
fetch('/dsh-live2d/api/select', {
|
|
216
|
+
method: 'POST',
|
|
217
|
+
headers: { 'content-type': 'application/json' },
|
|
218
|
+
body: JSON.stringify({ id: modelId })
|
|
219
|
+
})
|
|
220
|
+
.then(function () {
|
|
220
221
|
frame.src = '/dsh-live2d/viewer?embed=1&t=' + Date.now();
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
state.textContent = '已切换';
|
|
223
|
+
})
|
|
224
|
+
.catch(function (e) { state.textContent = '切换失败:' + e.message; });
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
function loadVoice() {
|
|
228
|
+
if (!modelId) return;
|
|
229
|
+
state.textContent = '读取中';
|
|
223
230
|
fetch('/dsh-live2d/api/voice?id=' + encodeURIComponent(modelId))
|
|
224
231
|
.then(function (r) { return r.json(); })
|
|
225
232
|
.then(function (d) {
|
|
@@ -249,7 +256,7 @@
|
|
|
249
256
|
state.textContent = '尚未导入模型';
|
|
250
257
|
return;
|
|
251
258
|
}
|
|
252
|
-
loadVoice(
|
|
259
|
+
loadVoice();
|
|
253
260
|
})
|
|
254
261
|
.catch(function (e) { state.textContent = '读取模型列表失败:' + e.message; });
|
|
255
262
|
|
package/assets/viewer.js
CHANGED
|
@@ -44358,7 +44358,6 @@ void main(void)\r
|
|
|
44358
44358
|
follow: "\u804A\u5929\u7A97\u53E3\u51FA\u73B0\u65B0\u56DE\u590D\u65F6\uFF0C\u7531\u89D2\u8272\u5FF5\u51FA\u6765"
|
|
44359
44359
|
};
|
|
44360
44360
|
var SERVER_HINT = {
|
|
44361
|
-
proactive: "\u4E00\u6BB5\u65F6\u95F4\u6CA1\u4EBA\u8BF4\u8BDD\uFF0C\u89D2\u8272\u81EA\u5DF1\u5F00\u4E00\u53E5",
|
|
44362
44361
|
choices: "\u89D2\u8272\u89C9\u5F97\u9700\u8981\u9009\u62E9\u65F6\uFF0C\u6C14\u6CE1\u4E0A\u65B9\u51FA\u73B0\u53EF\u70B9\u7684\u6309\u94AE",
|
|
44363
44362
|
memory: "\u4FDD\u7559\u804A\u8FC7\u7684\u5185\u5BB9\uFF0C\u4E0B\u6B21\u63A5\u7740\u804A\uFF08\u5360\u66F4\u591A\u6A21\u578B\u989D\u5EA6\uFF09"
|
|
44364
44363
|
};
|
package/lib/client.js
CHANGED
|
@@ -121,6 +121,10 @@
|
|
|
121
121
|
react.useEffect(() => localStorage.setItem('dsh-l2d-bg', bg), [bg]);
|
|
122
122
|
react.useEffect(() => localStorage.setItem('dsh-l2d-size', JSON.stringify(size)), [size]);
|
|
123
123
|
react.useEffect(() => localStorage.setItem('dsh-l2d-pos', JSON.stringify(pos)), [pos]);
|
|
124
|
+
react.useEffect(() => {
|
|
125
|
+
setPos((p) => clampPos(p, size.w, open ? size.h : 38));
|
|
126
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
127
|
+
}, []);
|
|
124
128
|
react.useEffect(() => {
|
|
125
129
|
frameWin = (ref.current && ref.current.contentWindow) || null;
|
|
126
130
|
}, [nonce, open]);
|
|
@@ -128,7 +132,7 @@
|
|
|
128
132
|
let seq = null;
|
|
129
133
|
let stop = false;
|
|
130
134
|
const tick = () => {
|
|
131
|
-
if (stop) return;
|
|
135
|
+
if (stop || document.hidden) return;
|
|
132
136
|
fetch(BASE + '/api/poll?since=' + (seq === null ? 0 : seq))
|
|
133
137
|
.then((r) => r.json())
|
|
134
138
|
.then((d) => {
|
|
@@ -180,6 +184,18 @@
|
|
|
180
184
|
.catch(() => setTitle('Live2D'));
|
|
181
185
|
}, [nonce]);
|
|
182
186
|
|
|
187
|
+
// 卡片钉在右下角,位移后可能把标题栏拖出屏幕;这里保证标题栏始终露在视口里
|
|
188
|
+
const clampPos = (p, w, h) => {
|
|
189
|
+
const vw = window.innerWidth;
|
|
190
|
+
const vh = window.innerHeight;
|
|
191
|
+
const minX = 6 - (vw - 18 - w);
|
|
192
|
+
const minY = 6 - (vh - 18 - h);
|
|
193
|
+
return {
|
|
194
|
+
x: Math.round(Math.min(12, Math.max(minX, p.x))),
|
|
195
|
+
y: Math.round(Math.min(12, Math.max(minY, p.y))),
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
183
199
|
// 拖动时盖一层全屏透明遮罩:既挡住 iframe 吃事件,也保证 pointermove 一定落在父窗口
|
|
184
200
|
const beginDrag = (e, kind) => {
|
|
185
201
|
if (e.button !== undefined && e.button !== 0) return;
|
|
@@ -209,7 +225,8 @@
|
|
|
209
225
|
const hh = Math.max(200, Math.min(maxH, h0 + (sy - ev.clientY)));
|
|
210
226
|
setSize({ w: Math.round(w), h: Math.round(hh) });
|
|
211
227
|
} else {
|
|
212
|
-
|
|
228
|
+
const hNow = open ? h0 : 38;
|
|
229
|
+
setPos(clampPos({ x: x0 + (ev.clientX - sx), y: y0 + (ev.clientY - sy) }, w0, hNow));
|
|
213
230
|
}
|
|
214
231
|
};
|
|
215
232
|
|
|
@@ -285,7 +302,7 @@
|
|
|
285
302
|
{ className: 'dsh-l2d-card' + (open ? '' : ' collapsed'), style: cardStyle },
|
|
286
303
|
h(
|
|
287
304
|
'div',
|
|
288
|
-
{ className: 'dsh-l2d-head', onPointerDown: startMove },
|
|
305
|
+
{ className: 'dsh-l2d-head', onPointerDown: startMove, onDoubleClick: () => setPos({ x: 0, y: 0 }) },
|
|
289
306
|
h('span', { className: 'dsh-l2d-dot' }),
|
|
290
307
|
h('span', { className: 'dsh-l2d-name' }, title),
|
|
291
308
|
h(
|
package/lib/index.js
CHANGED
|
@@ -24,7 +24,6 @@ const MEME_TAGS = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const DEFAULT_SETTINGS = {
|
|
27
|
-
proactive: true,
|
|
28
27
|
/** 多久没人说话才自己开口,单位分钟;0 = 关闭 */
|
|
29
28
|
proactiveInterval: 30,
|
|
30
29
|
/** off | own | both | external —— 测试功能,默认关 */
|
|
@@ -175,6 +174,13 @@ const TALK_SAMPLES = [
|
|
|
175
174
|
'他:明天要早起 —— 你:那我今晚早点关灯。',
|
|
176
175
|
].join('\n');
|
|
177
176
|
|
|
177
|
+
/** 世界隔离:不说破自己在哪儿 */
|
|
178
|
+
const ISOLATION = [
|
|
179
|
+
'【世界隔离】你不知道什么是程序、界面、插件、模型、参数。',
|
|
180
|
+
'有人问起,你就当他就站在你身边。不要提“系统”“设置”“网页”“token”这些词。',
|
|
181
|
+
'你不知道他那边什么样,得他讲给你听。',
|
|
182
|
+
].join('\n');
|
|
183
|
+
|
|
178
184
|
/** 语料:lines = 她的台词,pairs = 一问一答 */
|
|
179
185
|
function readCorpus(id) {
|
|
180
186
|
try {
|
|
@@ -404,7 +410,6 @@ async function generate(slot, prompt, limit = 900) {
|
|
|
404
410
|
}
|
|
405
411
|
|
|
406
412
|
/* ── 主动开口:没人说话,她自己开一句 ─────────────────────── */
|
|
407
|
-
const IDLE_MS = 12 * 60 * 1000;
|
|
408
413
|
let lastAutoAt = 0;
|
|
409
414
|
|
|
410
415
|
async function maybeTalk(ctx) {
|
|
@@ -523,7 +528,12 @@ function findModel3(dir) {
|
|
|
523
528
|
return out[0];
|
|
524
529
|
}
|
|
525
530
|
|
|
531
|
+
function validId(id) {
|
|
532
|
+
return /^[\w.-]+$/.test(String(id || ''));
|
|
533
|
+
}
|
|
534
|
+
|
|
526
535
|
function readMeta(id) {
|
|
536
|
+
if (!validId(id)) return null;
|
|
527
537
|
try {
|
|
528
538
|
return JSON.parse(readFileSync(join(MODELS, id, 'meta.json'), 'utf8'));
|
|
529
539
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live2dwaifu",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "给 DSH 网页右下角请一个 Live2D 角色:会眨眼、会呼吸、视线跟着鼠标、点她有反应、说话时嘴动、心情变了眉毛跟着动。模型不用内置——谁装谁放自己的文件夹;支持自定义 .exp3.json 表情和 .motion3.json 动作,卡片能拖能缩,背景可换。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|