dsh-live2dwaifu 0.6.0 → 0.7.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.
- package/README.md +11 -1
- package/assets/memes/bunaifan.jpg +0 -0
- package/assets/memes/deyi.jpg +0 -0
- package/assets/memes/diantou.jpg +0 -0
- package/assets/memes/haixiu.jpg +0 -0
- package/assets/memes/jingya.jpg +0 -0
- package/assets/memes/kaixin.jpg +0 -0
- package/assets/memes/kun.jpg +0 -0
- package/assets/memes/manifest.json +29 -0
- package/assets/memes/shengqi.jpg +0 -0
- package/assets/memes/sikao.jpg +0 -0
- package/assets/memes/wuyu.jpg +0 -0
- package/assets/memes/xianqi.jpg +0 -0
- package/assets/memes/xin-cui.jpg +0 -0
- package/assets/memes/xin-daxiao.jpg +0 -0
- package/assets/memes/xin-hei.jpg +0 -0
- package/assets/memes/xin-ing.jpg +0 -0
- package/assets/memes/xin-peng.jpg +0 -0
- package/assets/memes/xin-tiao.jpg +0 -0
- package/assets/memes/xin-xiao.jpg +0 -0
- package/assets/memes/xin-yao.jpg +0 -0
- package/assets/memes/zhaoshou.jpg +0 -0
- package/assets/viewer.html +4 -0
- package/assets/viewer.js +37 -1
- package/lib/index.js +52 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
**会出声**:一个字一声很短的电音,标点停一拍,句尾沉一下——不是朗读,是“哔哔”那种。标题栏的 **♪** 打开调音页,放个真模型在旁边,边调边听;每只模型各存一份音色,换人换声。音量拉到 0 就安静,只剩气泡和口型。
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**她也会甩图(测试功能)**:⚙ 里有一根滑轴,四档——不使用 / 只使用自带 / 均开启 / 只用 dsh-meme。自带那套是夏铃自己的 Q 版贴纸,二十张,随插件分发,版权归本插件;dsh-meme 那套只是借用你本机已经装的图库。默认关着,功能还在试。
|
|
27
27
|
|
|
28
28
|
**她还会问你要选项**:话说到了需要你挑的地方,气泡下面会冒出两三个按钮,点一个就等于你回了她。
|
|
29
29
|
|
|
@@ -125,6 +125,16 @@ frame.contentWindow.postMessage({ type: 'dsh-live2d/manage' }, '*');
|
|
|
125
125
|
**能放动作吗?**
|
|
126
126
|
暂时不能编排 `.motion3.json`,表情是固定的七种。想要更多情绪,给它发 `mood`,等以后的版本。
|
|
127
127
|
|
|
128
|
+
## 关于别人的插件
|
|
129
|
+
|
|
130
|
+
她的图、她的耳朵,都是从你**已经装好的**插件里读的。这个包里不带任何第三方素材,也一行没改别人的代码:
|
|
131
|
+
|
|
132
|
+
- **表情图**:从 `dsh-meme` 的图库目录里挑一张,只在本机显示,不上传、不分发。没装 `dsh-meme` 就没有图,其它照常。
|
|
133
|
+
- **麦克风**:交给 `dsh-ears`,本插件不碰录音,只是把转写好的字送进对话。
|
|
134
|
+
- **对话**:走 DSH 自己的模型接口(`ctx.llm`),不经过任何人的会话,也不写你的聊天记录。
|
|
135
|
+
|
|
136
|
+
不喜欢这些联动,⚙ 里都能单独关掉。
|
|
137
|
+
|
|
128
138
|
## 已知限制
|
|
129
139
|
|
|
130
140
|
- 只支持 Cubism 3 / 4(表情只认 `.exp3.json`,不认 `.motion3.json` 里的表情轨)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "xialing-001",
|
|
3
|
+
"name": "夏铃",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"license": "随本插件以 MIT 分发;图由 LFR 用 Seedream 生成",
|
|
6
|
+
"note": "Q 版三头身,白大褂 + 红高领毛衣 + 黑裤,淡灰虹膜,细黑框眼镜。",
|
|
7
|
+
"items": [
|
|
8
|
+
{ "file": "deyi.jpg", "label": "得意", "moods": ["开心"] },
|
|
9
|
+
{ "file": "shengqi.jpg", "label": "生气", "moods": ["生气"] },
|
|
10
|
+
{ "file": "haixiu.jpg", "label": "害羞", "moods": ["害羞"] },
|
|
11
|
+
{ "file": "kun.jpg", "label": "困", "moods": ["困"] },
|
|
12
|
+
{ "file": "wuyu.jpg", "label": "无语", "moods": ["无语"] },
|
|
13
|
+
{ "file": "jingya.jpg", "label": "惊讶", "moods": ["惊讶"] },
|
|
14
|
+
{ "file": "kaixin.jpg", "label": "开心", "moods": ["开心"] },
|
|
15
|
+
{ "file": "sikao.jpg", "label": "想事情", "moods": ["平静"] },
|
|
16
|
+
{ "file": "xianqi.jpg", "label": "嫌弃", "moods": ["无语", "生气"] },
|
|
17
|
+
{ "file": "diantou.jpg", "label": "点头", "moods": ["平静"] },
|
|
18
|
+
{ "file": "zhaoshou.jpg", "label": "打招呼", "moods": ["开心"] },
|
|
19
|
+
{ "file": "bunaifan.jpg", "label": "别烦我", "moods": ["生气"] },
|
|
20
|
+
{ "file": "xin-ing.jpg", "label": "心动", "moods": ["害羞"] },
|
|
21
|
+
{ "file": "xin-cui.jpg", "label": "手指抵唇", "moods": ["害羞"] },
|
|
22
|
+
{ "file": "xin-peng.jpg", "label": "崩溃", "moods": ["惊讶"] },
|
|
23
|
+
{ "file": "xin-tiao.jpg", "label": "挑衅", "moods": ["生气"] },
|
|
24
|
+
{ "file": "xin-xiao.jpg", "label": "看好戏", "moods": ["开心"] },
|
|
25
|
+
{ "file": "xin-daxiao.jpg", "label": "大笑", "moods": ["开心"] },
|
|
26
|
+
{ "file": "xin-hei.jpg", "label": "暗笑", "moods": ["无语"] },
|
|
27
|
+
{ "file": "xin-yao.jpg", "label": "馋", "moods": ["害羞"] }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/viewer.html
CHANGED
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
#settings h3 { font-size: 10px; letter-spacing: .6px; color: #6f7783; font-weight: 600; margin: 8px 0 4px; }
|
|
43
43
|
.opt { display: flex; align-items: center; gap: 8px; padding: 3px 2px; font-size: 12px; color: #c3c9d3; }
|
|
44
44
|
.opt input { accent-color: #6aa9ff; }
|
|
45
|
+
.opt-col { padding: 4px 2px 2px; }
|
|
46
|
+
.opt-col input[type=range] { width: 100%; accent-color: #6aa9ff; }
|
|
47
|
+
.opt-lab { font-size: 12px; color: #dfe4ec; margin-top: 2px; }
|
|
48
|
+
.opt-note { font-size: 10.5px; line-height: 1.6; color: #6f7783; margin-top: 4px; }
|
|
45
49
|
.bub-img { display: block; max-width: 100%; max-height: 120px; border-radius: 8px; margin-bottom: 6px; object-fit: contain; }
|
|
46
50
|
#actions { display: flex; gap: 6px; }
|
|
47
51
|
#actions button { flex: 1; background: #2f343d; color: #cfd4dd; border: 1px solid #3c424c;
|
package/assets/viewer.js
CHANGED
|
@@ -44346,10 +44346,11 @@ void main(void)\r
|
|
|
44346
44346
|
];
|
|
44347
44347
|
var SERVER_OPTS = [
|
|
44348
44348
|
["proactive", "\u6CA1\u4EBA\u7406\u5C31\u81EA\u5DF1\u5F00\u53E3"],
|
|
44349
|
-
["memes", "\u4F1A\u7529\u8868\u60C5\u5305"],
|
|
44350
44349
|
["choices", "\u7ED9\u9009\u9879\u8BA9\u4ED6\u6311"],
|
|
44351
44350
|
["memory", "\u8BB0\u4F4F\u4E4B\u524D\u8BF4\u7684"]
|
|
44352
44351
|
];
|
|
44352
|
+
var MEME_MODES = ["off", "own", "both", "external"];
|
|
44353
|
+
var MEME_LABELS = ["\u4E0D\u4F7F\u7528", "\u53EA\u4F7F\u7528\u81EA\u5E26", "\u5747\u5F00\u542F", "\u53EA\u7528 dsh-meme"];
|
|
44353
44354
|
var serverSettings = {};
|
|
44354
44355
|
function renderSettings() {
|
|
44355
44356
|
const box = $2("settings");
|
|
@@ -44391,6 +44392,41 @@ void main(void)\r
|
|
|
44391
44392
|
row.appendChild(document.createTextNode(label));
|
|
44392
44393
|
box.appendChild(row);
|
|
44393
44394
|
}
|
|
44395
|
+
const h3 = document.createElement("h3");
|
|
44396
|
+
h3.textContent = "\u8868\u60C5\u5305\uFF08\u6D4B\u8BD5\u529F\u80FD\uFF09";
|
|
44397
|
+
box.appendChild(h3);
|
|
44398
|
+
const wrap = document.createElement("div");
|
|
44399
|
+
wrap.className = "opt-col";
|
|
44400
|
+
const slider = document.createElement("input");
|
|
44401
|
+
slider.type = "range";
|
|
44402
|
+
slider.min = "0";
|
|
44403
|
+
slider.max = String(MEME_MODES.length - 1);
|
|
44404
|
+
slider.step = "1";
|
|
44405
|
+
const cur = Math.max(0, MEME_MODES.indexOf(serverSettings.memesMode || "off"));
|
|
44406
|
+
slider.value = String(cur);
|
|
44407
|
+
const lab = document.createElement("div");
|
|
44408
|
+
lab.className = "opt-lab";
|
|
44409
|
+
lab.textContent = MEME_LABELS[cur];
|
|
44410
|
+
slider.oninput = () => {
|
|
44411
|
+
lab.textContent = MEME_LABELS[Number(slider.value)];
|
|
44412
|
+
};
|
|
44413
|
+
slider.onchange = () => {
|
|
44414
|
+
const mode = MEME_MODES[Number(slider.value)];
|
|
44415
|
+
serverSettings.memesMode = mode;
|
|
44416
|
+
fetch(BASE + "/api/settings", {
|
|
44417
|
+
method: "POST",
|
|
44418
|
+
headers: { "content-type": "application/json" },
|
|
44419
|
+
body: JSON.stringify({ settings: { memesMode: mode } })
|
|
44420
|
+
}).catch(() => {
|
|
44421
|
+
});
|
|
44422
|
+
};
|
|
44423
|
+
const note = document.createElement("div");
|
|
44424
|
+
note.className = "opt-note";
|
|
44425
|
+
note.textContent = "\u81EA\u5E26\u90A3\u5957\u662F\u590F\u94C3\u7684 Q \u7248\u8D34\u7EB8\uFF0C\u4E8C\u5341\u5F20\uFF1Bdsh-meme \u90A3\u5957\u501F\u7528\u4F60\u672C\u673A\u5DF2\u7ECF\u88C5\u7684\u56FE\u5E93\u3002\u529F\u80FD\u8FD8\u5728\u8BD5\u9A8C\uFF0C\u53EF\u80FD\u4F1A\u8C03\u3002";
|
|
44426
|
+
wrap.appendChild(slider);
|
|
44427
|
+
wrap.appendChild(lab);
|
|
44428
|
+
wrap.appendChild(note);
|
|
44429
|
+
box.appendChild(wrap);
|
|
44394
44430
|
}
|
|
44395
44431
|
function loadSettings() {
|
|
44396
44432
|
fetch(BASE + "/api/settings").then((r2) => r2.json()).then((d2) => {
|
package/lib/index.js
CHANGED
|
@@ -25,7 +25,8 @@ const MEME_TAGS = {
|
|
|
25
25
|
|
|
26
26
|
const DEFAULT_SETTINGS = {
|
|
27
27
|
proactive: true,
|
|
28
|
-
|
|
28
|
+
/** off | own | both | external —— 测试功能,默认关 */
|
|
29
|
+
memesMode: 'off',
|
|
29
30
|
choices: true,
|
|
30
31
|
memory: true,
|
|
31
32
|
follow: true,
|
|
@@ -35,6 +36,45 @@ const DEFAULT_SETTINGS = {
|
|
|
35
36
|
sound: true,
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
const MEMES_MODES = ['off', 'own', 'both', 'external'];
|
|
40
|
+
const OWN_MEME_DIR = join(ASSETS, 'memes');
|
|
41
|
+
|
|
42
|
+
function readOwnMemes() {
|
|
43
|
+
try {
|
|
44
|
+
const m = JSON.parse(readFileSync(join(OWN_MEME_DIR, 'manifest.json'), 'utf8'));
|
|
45
|
+
return Array.isArray(m.items) ? m.items : [];
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 从自带那套里按情绪挑一张,返回文件名 */
|
|
52
|
+
function pickOwnMeme(mood) {
|
|
53
|
+
const items = readOwnMemes();
|
|
54
|
+
if (!items.length) return null;
|
|
55
|
+
const hit = items.filter((it) => Array.isArray(it.moods) && it.moods.includes(mood));
|
|
56
|
+
const pool = hit.length ? hit : items;
|
|
57
|
+
const it = pool[Math.floor(Math.random() * pool.length)];
|
|
58
|
+
return it && it.file ? it.file : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 按模式挑一张配图,返回可直接用的 URL */
|
|
62
|
+
function pickImage(mode, mood) {
|
|
63
|
+
const m = MEMES_MODES.includes(mode) ? mode : 'off';
|
|
64
|
+
if (m === 'off') return null;
|
|
65
|
+
const pool = [];
|
|
66
|
+
if (m === 'own' || m === 'both') {
|
|
67
|
+
const f = pickOwnMeme(mood);
|
|
68
|
+
if (f) pool.push('/dsh-live2d/ownmeme/' + encodeURIComponent(f));
|
|
69
|
+
}
|
|
70
|
+
if (m === 'external' || m === 'both') {
|
|
71
|
+
const f = pickMeme(mood);
|
|
72
|
+
if (f) pool.push('/dsh-live2d/media/' + f.split('/').map(encodeURIComponent).join('/'));
|
|
73
|
+
}
|
|
74
|
+
if (!pool.length) return null;
|
|
75
|
+
return pool[Math.floor(Math.random() * pool.length)];
|
|
76
|
+
}
|
|
77
|
+
|
|
38
78
|
function settingsOf(cfg) {
|
|
39
79
|
return { ...DEFAULT_SETTINGS, ...(cfg?.settings || {}) };
|
|
40
80
|
}
|
|
@@ -383,6 +423,12 @@ async function handle(req, res, ctx) {
|
|
|
383
423
|
return file ? sendFile(res, file) : send(res, 403, 'forbidden');
|
|
384
424
|
}
|
|
385
425
|
|
|
426
|
+
// 自带的 Q 版表情包
|
|
427
|
+
if (path.startsWith('/ownmeme/')) {
|
|
428
|
+
const file = safeJoin(OWN_MEME_DIR, decodeURIComponent(path.slice('/ownmeme/'.length)));
|
|
429
|
+
return file ? sendFile(res, file) : send(res, 403, 'forbidden');
|
|
430
|
+
}
|
|
431
|
+
|
|
386
432
|
// 表情图库
|
|
387
433
|
if (path.startsWith('/media/')) {
|
|
388
434
|
const file = safeJoin(MEMES_ROOT, decodeURIComponent(path.slice('/media/'.length)));
|
|
@@ -445,7 +491,8 @@ async function handle(req, res, ctx) {
|
|
|
445
491
|
choices = FALLBACK_CHOICES[guessMood(line)] || FALLBACK_CHOICES['平静'];
|
|
446
492
|
}
|
|
447
493
|
if (!line && !choices.length) return sendJson(res, 200, { ok: true, reply: '', id });
|
|
448
|
-
const
|
|
494
|
+
const moodNow = guessMood(line);
|
|
495
|
+
const image = st.memesMode !== 'off' && Math.random() < 0.35 ? pickImage(st.memesMode, moodNow) : null;
|
|
449
496
|
|
|
450
497
|
writeChat(id, hist.concat([
|
|
451
498
|
{ role: 'user', text, at: Date.now() },
|
|
@@ -491,6 +538,9 @@ async function handle(req, res, ctx) {
|
|
|
491
538
|
for (const k of Object.keys(DEFAULT_SETTINGS)) {
|
|
492
539
|
if (typeof patch?.[k] === 'boolean') next[k] = patch[k];
|
|
493
540
|
}
|
|
541
|
+
if (typeof patch?.memesMode === 'string' && MEMES_MODES.includes(patch.memesMode)) {
|
|
542
|
+
next.memesMode = patch.memesMode;
|
|
543
|
+
}
|
|
494
544
|
writeConfig({ ...cfg, settings: next });
|
|
495
545
|
return sendJson(res, 200, { ok: true, settings: next });
|
|
496
546
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live2dwaifu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "给 DSH 网页右下角请一个 Live2D 角色:会眨眼、会呼吸、视线跟着鼠标、点她有反应、说话时嘴动、心情变了眉毛跟着动。模型不用内置——谁装谁放自己的文件夹;支持自定义 .exp3.json 表情和 .motion3.json 动作,卡片能拖能缩,背景可换。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|