dsh-live2dwaifu 1.0.4 → 1.0.6
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 +4 -2
- package/lib/client.js +71 -56
- package/lib/index.js +29 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,8 +47,8 @@ dsh plugin --profile desktop add dsh-live2dwaifu
|
|
|
47
47
|
|---|---|
|
|
48
48
|
| **⚙** | 抽屉:模型列表、自定义表情、全部开关 |
|
|
49
49
|
| **◐** | 换背景:透明 / 深色 / 浅色 / 纸质 |
|
|
50
|
-
| **♪** |
|
|
51
|
-
| **✎** |
|
|
50
|
+
| **♪** | 音色调色页(每只模型各存一份音色),在卡片里盖一层打开 |
|
|
51
|
+
| **✎** | 人设卡编辑页,同样盖一层打开 |
|
|
52
52
|
| **↻** | 重载 iframe(模型卡住时点这个) |
|
|
53
53
|
| **⌄ / ⌃** | 收起 / 展开 |
|
|
54
54
|
|
|
@@ -123,6 +123,8 @@ dsh plugin --profile desktop add dsh-live2dwaifu
|
|
|
123
123
|
|
|
124
124
|
提示词里还会明确禁止:不总结、不分点、不讲道理、不解释心情、你没问就不给建议。
|
|
125
125
|
|
|
126
|
+
另外给了几组说话样例(“他:今天吃啥 —— 你:锅里还有,热的。”这种),让她照着那个密度和信息量说,不是照着内容抄。长度硬卡在四十字上下,超了会被截。
|
|
127
|
+
|
|
126
128
|
> 还是觉得哪句像 AI?记下来告诉作者,往规则里加。
|
|
127
129
|
|
|
128
130
|
### 她偶尔给你选项
|
package/lib/client.js
CHANGED
|
@@ -47,6 +47,16 @@
|
|
|
47
47
|
transition:opacity .15s ease,background .15s ease;border-radius:16px 0 8px 0;z-index:2;
|
|
48
48
|
background:repeating-linear-gradient(135deg,transparent 0 2px,rgba(255,255,255,.45) 2px 3px) no-repeat left top/10px 10px}
|
|
49
49
|
.dsh-l2d-grip:hover{opacity:1;background-color:rgba(255,255,255,.08)}
|
|
50
|
+
.dsh-l2d-sheet{position:fixed;inset:0;z-index:9500;background:rgba(10,12,16,.74);
|
|
51
|
+
backdrop-filter:blur(4px);display:flex;flex-direction:column;padding:18px 18px 22px}
|
|
52
|
+
.dsh-l2d-sheet-bar{display:flex;align-items:center;gap:10px;padding:0 0 10px;
|
|
53
|
+
color:#cfd6e2;font-size:12px;letter-spacing:.5px}
|
|
54
|
+
.dsh-l2d-sheet-bar span{margin-right:auto}
|
|
55
|
+
.dsh-l2d-sheet-bar button{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.13);
|
|
56
|
+
border-radius:8px;color:#dfe4ec;font:inherit;font-size:11px;padding:5px 12px;cursor:pointer}
|
|
57
|
+
.dsh-l2d-sheet-bar button:hover{background:rgba(255,255,255,.16)}
|
|
58
|
+
.dsh-l2d-sheet-body{flex:1;border:0;border-radius:14px;background:#12141a;
|
|
59
|
+
box-shadow:0 24px 60px rgba(0,0,0,.6)}
|
|
50
60
|
.dsh-l2d-card.resizing{cursor:nwse-resize;box-shadow:0 0 0 1px rgba(106,169,255,.45),0 22px 58px rgba(0,0,0,.6)}
|
|
51
61
|
.dsh-l2d-card.moving{cursor:move;box-shadow:0 0 0 1px rgba(106,169,255,.35),0 22px 58px rgba(0,0,0,.6)}
|
|
52
62
|
`;
|
|
@@ -85,6 +95,7 @@
|
|
|
85
95
|
const [pos, setPos] = react.useState(readPos);
|
|
86
96
|
const [draft, setDraft] = react.useState('');
|
|
87
97
|
const [busy, setBusy] = react.useState(false);
|
|
98
|
+
const [sheet, setSheet] = react.useState(null);
|
|
88
99
|
const [nonce, setNonce] = react.useState(0);
|
|
89
100
|
const ref = react.useRef(null);
|
|
90
101
|
|
|
@@ -151,73 +162,61 @@
|
|
|
151
162
|
.catch(() => setTitle('Live2D'));
|
|
152
163
|
}, [nonce]);
|
|
153
164
|
|
|
154
|
-
//
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
if (f) f.style.pointerEvents = on ? 'none' : '';
|
|
158
|
-
if (cardEl && cardEl.classList) {
|
|
159
|
-
cardEl.classList.toggle('resizing', on && cls === 'resizing');
|
|
160
|
-
cardEl.classList.toggle('moving', on && cls === 'moving');
|
|
161
|
-
}
|
|
162
|
-
document.body.style.userSelect = on ? 'none' : '';
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const bindDrag = (move, cls, cardEl) => {
|
|
166
|
-
const up = () => {
|
|
167
|
-
window.removeEventListener('pointermove', move);
|
|
168
|
-
window.removeEventListener('pointerup', up);
|
|
169
|
-
window.removeEventListener('pointercancel', up);
|
|
170
|
-
lockPointer(false, cls, cardEl);
|
|
171
|
-
};
|
|
172
|
-
window.addEventListener('pointermove', move);
|
|
173
|
-
window.addEventListener('pointerup', up);
|
|
174
|
-
window.addEventListener('pointercancel', up);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// 卡片钉在右下角,所以只有左上角会动:往左上拖变大,往右下拖变小
|
|
178
|
-
const startDrag = (e) => {
|
|
165
|
+
// 拖动时盖一层全屏透明遮罩:既挡住 iframe 吃事件,也保证 pointermove 一定落在父窗口
|
|
166
|
+
const beginDrag = (e, kind) => {
|
|
167
|
+
if (e.button !== undefined && e.button !== 0) return;
|
|
179
168
|
e.preventDefault();
|
|
180
169
|
e.stopPropagation();
|
|
181
|
-
const cardEl = e.currentTarget.closest('.dsh-l2d-card');
|
|
170
|
+
const cardEl = e.currentTarget && e.currentTarget.closest ? e.currentTarget.closest('.dsh-l2d-card') : null;
|
|
182
171
|
const sx = e.clientX;
|
|
183
172
|
const sy = e.clientY;
|
|
184
173
|
const w0 = size.w;
|
|
185
174
|
const h0 = size.h;
|
|
186
|
-
lockPointer(true, 'resizing', cardEl);
|
|
187
|
-
bindDrag((ev) => {
|
|
188
|
-
const maxW = Math.min(720, Math.max(240, window.innerWidth - 60));
|
|
189
|
-
const maxH = Math.max(240, window.innerHeight - 80);
|
|
190
|
-
const w = Math.max(200, Math.min(maxW, w0 + (sx - ev.clientX)));
|
|
191
|
-
const hh = Math.max(220, Math.min(maxH, h0 + (sy - ev.clientY)));
|
|
192
|
-
setSize({ w: Math.round(w), h: Math.round(hh) });
|
|
193
|
-
}, 'resizing', cardEl);
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const startMove = (e) => {
|
|
197
|
-
if (e.button !== undefined && e.button !== 0) return;
|
|
198
|
-
const cardEl = e.currentTarget.closest('.dsh-l2d-card');
|
|
199
|
-
const sx = e.clientX;
|
|
200
|
-
const sy = e.clientY;
|
|
201
175
|
const x0 = pos.x;
|
|
202
176
|
const y0 = pos.y;
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
177
|
+
const cls = kind === 'resize' ? 'resizing' : 'moving';
|
|
178
|
+
const shield = document.createElement('div');
|
|
179
|
+
shield.setAttribute('data-dsh-l2d-shield', '1');
|
|
180
|
+
shield.style.cssText =
|
|
181
|
+
'position:fixed;left:0;top:0;right:0;bottom:0;z-index:99999;background:transparent;touch-action:none;cursor:' +
|
|
182
|
+
(kind === 'resize' ? 'nwse-resize' : 'move');
|
|
183
|
+
document.body.appendChild(shield);
|
|
184
|
+
if (cardEl && cardEl.classList) cardEl.classList.add(cls);
|
|
185
|
+
|
|
186
|
+
const move = (ev) => {
|
|
187
|
+
if (kind === 'resize') {
|
|
188
|
+
const maxW = Math.max(240, Math.min(760, window.innerWidth - 40));
|
|
189
|
+
const maxH = Math.max(240, window.innerHeight - 60);
|
|
190
|
+
const w = Math.max(200, Math.min(maxW, w0 + (sx - ev.clientX)));
|
|
191
|
+
const hh = Math.max(200, Math.min(maxH, h0 + (sy - ev.clientY)));
|
|
192
|
+
setSize({ w: Math.round(w), h: Math.round(hh) });
|
|
193
|
+
} else {
|
|
194
|
+
setPos({ x: Math.round(x0 + (ev.clientX - sx)), y: Math.round(y0 + (ev.clientY - sy)) });
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const finish = () => {
|
|
199
|
+
shield.removeEventListener('pointermove', move);
|
|
200
|
+
if (shield.parentNode) shield.parentNode.removeChild(shield);
|
|
201
|
+
if (cardEl && cardEl.classList) cardEl.classList.remove(cls);
|
|
202
|
+
window.removeEventListener('pointermove', move);
|
|
203
|
+
window.removeEventListener('pointerup', finish);
|
|
204
|
+
window.removeEventListener('pointercancel', finish);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
shield.addEventListener('pointermove', move);
|
|
208
|
+
window.addEventListener('pointermove', move);
|
|
209
|
+
window.addEventListener('pointerup', finish);
|
|
210
|
+
window.addEventListener('pointercancel', finish);
|
|
215
211
|
};
|
|
212
|
+
|
|
213
|
+
const startDrag = (e) => beginDrag(e, 'resize');
|
|
214
|
+
const startMove = (e) => beginDrag(e, 'move');
|
|
216
215
|
const noDrag = (e) => e.stopPropagation();
|
|
217
216
|
|
|
218
217
|
const manage = () => ref.current && ref.current.contentWindow.postMessage({ type: 'dsh-live2d/manage' }, '*');
|
|
219
|
-
const openTone = () =>
|
|
220
|
-
const openPersona = () =>
|
|
218
|
+
const openTone = () => setSheet('tone');
|
|
219
|
+
const openPersona = () => setSheet('persona');
|
|
221
220
|
|
|
222
221
|
const send = () => {
|
|
223
222
|
const t = draft.trim();
|
|
@@ -249,7 +248,21 @@
|
|
|
249
248
|
? { width: size.w + 'px', height: size.h + 'px', transform: 'translate(' + pos.x + 'px,' + pos.y + 'px)' }
|
|
250
249
|
: { transform: 'translate(' + pos.x + 'px,' + pos.y + 'px)' };
|
|
251
250
|
|
|
252
|
-
|
|
251
|
+
const overlay = sheet
|
|
252
|
+
? h(
|
|
253
|
+
'div',
|
|
254
|
+
{ className: 'dsh-l2d-sheet', onPointerDown: noDrag },
|
|
255
|
+
h(
|
|
256
|
+
'div',
|
|
257
|
+
{ className: 'dsh-l2d-sheet-bar' },
|
|
258
|
+
h('span', null, sheet === 'tone' ? '音色' : '人设卡'),
|
|
259
|
+
h('button', { onClick: () => setSheet(null) }, '关掉'),
|
|
260
|
+
),
|
|
261
|
+
h('iframe', { className: 'dsh-l2d-sheet-body', src: BASE + '/' + sheet }),
|
|
262
|
+
)
|
|
263
|
+
: null;
|
|
264
|
+
|
|
265
|
+
const card = h(
|
|
253
266
|
'div',
|
|
254
267
|
{ className: 'dsh-l2d-card' + (open ? '' : ' collapsed'), style: cardStyle },
|
|
255
268
|
h(
|
|
@@ -299,6 +312,8 @@
|
|
|
299
312
|
: null,
|
|
300
313
|
open ? h('div', { className: 'dsh-l2d-grip', onPointerDown: startDrag, title: '拖动改大小' }) : null,
|
|
301
314
|
);
|
|
315
|
+
|
|
316
|
+
return h(react.Fragment, null, card, overlay);
|
|
302
317
|
}
|
|
303
318
|
|
|
304
319
|
let frameWin = null;
|
package/lib/index.js
CHANGED
|
@@ -156,22 +156,28 @@ function guessMood(text) {
|
|
|
156
156
|
/** 默认音色:一个字一声短音 */
|
|
157
157
|
const DEFAULT_VOICE = { preset: 'soft', type: 'triangle', base: 400, jit: 10, dur: 60, vol: 32, spd: 62 };
|
|
158
158
|
|
|
159
|
-
/**
|
|
159
|
+
/** 没写人设卡时用的通用版,不绑定具体角色 */
|
|
160
160
|
const DEFAULT_PERSONA = [
|
|
161
|
-
'
|
|
162
|
-
'
|
|
163
|
-
'
|
|
161
|
+
'你叫{name}。',
|
|
162
|
+
'说话短,一句常常不到十五个字;不用感叹号,不写书面语,不解释心情。',
|
|
163
|
+
'情绪落在具体的东西上——桌上有什么、手上在做什么、外面天色怎样。',
|
|
164
|
+
'不总结,不升华,不写括号里的动作。',
|
|
164
165
|
].join('\n');
|
|
165
166
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
'
|
|
169
|
-
'
|
|
167
|
+
/** 参考密度:只给节奏和信息量,不是让模型照抄内容 */
|
|
168
|
+
const TALK_SAMPLES = [
|
|
169
|
+
'他:今天吃啥 —— 你:锅里还有,热的。',
|
|
170
|
+
'他:累死了 —— 你:先坐下。水在桌上。',
|
|
171
|
+
'他:你在干嘛 —— 你:擦桌子。手是湿的。',
|
|
172
|
+
'他:我想你了 —— 你:嗯。我也是。',
|
|
173
|
+
'他:明天要早起 —— 你:那我今晚早点关灯。',
|
|
170
174
|
].join('\n');
|
|
171
175
|
|
|
172
176
|
function personaOf(meta) {
|
|
173
177
|
const p = typeof meta?.persona === 'string' ? meta.persona.trim() : '';
|
|
174
|
-
|
|
178
|
+
if (p) return p;
|
|
179
|
+
const nm = String(meta?.name || '她').trim() || '她';
|
|
180
|
+
return DEFAULT_PERSONA.replace('{name}', nm);
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
function readChatStore(id) {
|
|
@@ -283,6 +289,10 @@ function humanize(input) {
|
|
|
283
289
|
.trim();
|
|
284
290
|
const parts = t.split(/(?<=[。!?!?~])/).filter((s) => s.trim());
|
|
285
291
|
if (parts.length > 3) t = parts.slice(0, 3).join('');
|
|
292
|
+
if (t.length > 60) {
|
|
293
|
+
const cut = t.match(/^[\s\S]{0,60}?[。!?!?]/);
|
|
294
|
+
t = cut ? cut[0] : t.slice(0, 60);
|
|
295
|
+
}
|
|
286
296
|
return t.trim();
|
|
287
297
|
}
|
|
288
298
|
|
|
@@ -360,8 +370,9 @@ async function maybeTalk(ctx) {
|
|
|
360
370
|
personaOf(meta),
|
|
361
371
|
'现在是 ' + nowText() + '。',
|
|
362
372
|
hist ? '你们之前说的:\n' + hist : '',
|
|
363
|
-
'
|
|
364
|
-
'
|
|
373
|
+
'有一阵子没人说话了。你想开口,扔一句过去——提一件你正在做的事、问一句、或者吐槽一下都行。',
|
|
374
|
+
'一到两句,四十字以内;具体,别总结,别讲道理。只输出这句话本身,不要引号,不要旁白。',
|
|
375
|
+
'密度参考(别照抄):\n' + TALK_SAMPLES,
|
|
365
376
|
]
|
|
366
377
|
.filter(Boolean)
|
|
367
378
|
.join('\n\n');
|
|
@@ -584,14 +595,17 @@ async function handle(req, res, ctx) {
|
|
|
584
595
|
const prompt = [
|
|
585
596
|
personaOf(meta),
|
|
586
597
|
meta.isolation === false ? '' : ISOLATION,
|
|
587
|
-
'现在是 ' + nowText() + '
|
|
598
|
+
'现在是 ' + nowText() + '。',
|
|
588
599
|
store.summary ? '之前聊过的,你还记得:\n' + store.summary : '',
|
|
589
600
|
facts.length ? '关于他,你记得这些:\n' + facts.map((f) => '- ' + f).join('\n') : '',
|
|
590
601
|
lines ? '你们之前说的:\n' + lines : '',
|
|
591
602
|
'他说:' + text,
|
|
592
|
-
'
|
|
593
|
-
|
|
594
|
-
'
|
|
603
|
+
'照上面这个人说话,回他一句。',
|
|
604
|
+
'一到两句,四十字以内。要具体:答应、拒绝、反问、吐槽、提一件正在做的事,都行。' +
|
|
605
|
+
'别总结他的话,别讲道理,别说“我理解你”“希望帮到你”“还需要什么”;他没问就别建议。' +
|
|
606
|
+
'可以不接他的话,扔一句自己的事;允许只回两个字。',
|
|
607
|
+
'只输出这句话本身:不要引号,不要旁白,不要括号里的动作。',
|
|
608
|
+
'往下是这个人的说话密度,只学节奏和信息量,别照抄内容:\n' + TALK_SAMPLES,
|
|
595
609
|
st.choices ? '如果你想让他挑,可以在最后另起一行写 ||选项一|选项二,最多三项。' : '',
|
|
596
610
|
st.memesMode === 'own' || st.memesMode === 'both'
|
|
597
611
|
? '如果你想附一张贴图,就在最后另起一行写 ||图:名字。能挑的名字有:' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live2dwaifu",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "给 DSH 网页右下角请一个 Live2D 角色:会眨眼、会呼吸、视线跟着鼠标、点她有反应、说话时嘴动、心情变了眉毛跟着动。模型不用内置——谁装谁放自己的文件夹;支持自定义 .exp3.json 表情和 .motion3.json 动作,卡片能拖能缩,背景可换。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|