dsh-live2dwaifu 0.1.4 → 0.2.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 +9 -1
- package/assets/viewer.html +4 -1
- package/assets/viewer.js +103 -6
- package/lib/client.js +113 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,15 @@
|
|
|
13
13
|
| 展开 | 一张小卡片,上半身,底下一条气泡装台词 |
|
|
14
14
|
| 收起 | 缩成一条,只剩名字和一个 ⌃ |
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
按住标题栏能把她拖到屏幕任何地方,右下角那个小三角拖的是大小;标题栏的 **◐** 一键换背景(透明 / 深色 / 浅色 / 纸质)。位置、大小、背景、开合状态都记在本机,下次还是这样。
|
|
17
|
+
|
|
18
|
+
**她会看你**:鼠标在窗口里走,眼珠跟着转,脑袋也偏一点。
|
|
19
|
+
|
|
20
|
+
**能戳**:点她的头会害羞,戳身上会瞪你,点底下就应你一声。
|
|
21
|
+
|
|
22
|
+
**会自己动**:模型里带 `.motion3.json` 的,隔十几秒自己播一个动作。气泡能拖着挪地方,说完过一阵自己淡下去。
|
|
23
|
+
|
|
24
|
+
**跟着聊**:她挂在聊天流上(`conversation.chat.node` 插槽),助手每说一句她就念一句,情绪按话里的词换。
|
|
17
25
|
|
|
18
26
|
## 三十秒上手
|
|
19
27
|
|
package/assets/viewer.html
CHANGED
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
#stage { display: block; width: 100%; height: 100%; }
|
|
11
11
|
#bubble { position: absolute; left: 12px; right: 12px; bottom: 10px; min-height: 30px; box-sizing: border-box;
|
|
12
12
|
background: rgba(24,27,33,.94); border: 1px solid #3a4049; border-radius: 10px; padding: 7px 10px;
|
|
13
|
-
color: #dfe4ec; font-size: 13px; line-height: 1.45;
|
|
13
|
+
color: #dfe4ec; font-size: 13px; line-height: 1.45;
|
|
14
|
+
opacity: 0; transition: opacity .35s ease; cursor: grab; user-select: none;
|
|
15
|
+
max-height: 40%; overflow: hidden; touch-action: none; }
|
|
16
|
+
#bubble:active { cursor: grabbing; }
|
|
14
17
|
#hint { position: absolute; inset: 0; display: none; align-items: center; justify-content: center;
|
|
15
18
|
text-align: center; padding: 20px; color: #98a0ad; }
|
|
16
19
|
#hint.show { display: flex; }
|
package/assets/viewer.js
CHANGED
|
@@ -44312,6 +44312,15 @@ void main(void)\r
|
|
|
44312
44312
|
var exps = [];
|
|
44313
44313
|
var expParams = /* @__PURE__ */ new Map();
|
|
44314
44314
|
var expName = null;
|
|
44315
|
+
var gazeX = 0;
|
|
44316
|
+
var gazeY = 0;
|
|
44317
|
+
var pokeAt = 0;
|
|
44318
|
+
var sayFade = 0;
|
|
44319
|
+
var motionGroups = [];
|
|
44320
|
+
var nextMotionAt = 0;
|
|
44321
|
+
var POKE_HEAD = ["\u5514\u3002", "\u522B\u6478\u3002", "\u5934\u53D1\u4E71\u4E86\u3002", "\u55EF\u2026\u2026", "\u4F60\u624B\u51C9\u3002"];
|
|
44322
|
+
var POKE_BODY = ["\u770B\u4EC0\u4E48\u770B\u3002", "\u6233\u4EC0\u4E48\u3002", "\u54FC\u3002", "\u522B\u95F9\u3002"];
|
|
44323
|
+
var POKE_LOW = ["\u5728\u5462\u3002", "\u6709\u4E8B\uFF1F", "\u600E\u4E48\u4E86\u3002"];
|
|
44315
44324
|
var base = {};
|
|
44316
44325
|
var BASE_IDS = ["ParamMouthForm", "ParamMouthOpenY", "ParamBrowLY", "ParamBrowRY", "ParamBrowLAngle", "ParamBrowRAngle", "ParamEyeLSmile", "ParamEyeRSmile", "ParamEyeLOpen", "ParamEyeROpen", "ParamEyeBallX", "ParamEyeBallY", "ParamAngleX", "ParamAngleY", "ParamAngleZ"];
|
|
44317
44326
|
for (const id of BASE_IDS) base[id] = 0;
|
|
@@ -44361,6 +44370,13 @@ void main(void)\r
|
|
|
44361
44370
|
exps = entry.expressions || [];
|
|
44362
44371
|
expParams = /* @__PURE__ */ new Map();
|
|
44363
44372
|
expName = null;
|
|
44373
|
+
try {
|
|
44374
|
+
const defs = model.internalModel && model.internalModel.motionManager && model.internalModel.motionManager.definitions || {};
|
|
44375
|
+
motionGroups = Object.keys(defs).filter((k2) => Array.isArray(defs[k2]) && defs[k2].length);
|
|
44376
|
+
} catch {
|
|
44377
|
+
motionGroups = [];
|
|
44378
|
+
}
|
|
44379
|
+
nextMotionAt = performance.now() + 6e3;
|
|
44364
44380
|
renderExps();
|
|
44365
44381
|
fit();
|
|
44366
44382
|
applyMood("\u5E73\u9759");
|
|
@@ -44440,13 +44456,22 @@ void main(void)\r
|
|
|
44440
44456
|
function say(text, m2) {
|
|
44441
44457
|
if (m2) applyMood(m2);
|
|
44442
44458
|
const bubble = $2("bubble");
|
|
44459
|
+
let s2 = String(text == null ? "" : text).replace(/\s+/g, " ").trim();
|
|
44460
|
+
if (s2.length > 180) s2 = s2.slice(0, 178) + "\u2026";
|
|
44443
44461
|
bubble.textContent = "";
|
|
44462
|
+
bubble.style.opacity = "1";
|
|
44463
|
+
clearTimeout(sayFade);
|
|
44444
44464
|
let i2 = 0;
|
|
44445
44465
|
const t2 = setInterval(() => {
|
|
44446
|
-
bubble.textContent =
|
|
44466
|
+
bubble.textContent = s2.slice(0, ++i2);
|
|
44447
44467
|
mouth = 0.14;
|
|
44448
|
-
if (i2 >=
|
|
44449
|
-
|
|
44468
|
+
if (i2 >= s2.length) {
|
|
44469
|
+
clearInterval(t2);
|
|
44470
|
+
sayFade = setTimeout(() => {
|
|
44471
|
+
bubble.style.opacity = "0";
|
|
44472
|
+
}, Math.min(14e3, 4e3 + s2.length * 110));
|
|
44473
|
+
}
|
|
44474
|
+
}, 55);
|
|
44450
44475
|
}
|
|
44451
44476
|
var idleIdx = 0;
|
|
44452
44477
|
function idleTick() {
|
|
@@ -44483,17 +44508,33 @@ void main(void)\r
|
|
|
44483
44508
|
}
|
|
44484
44509
|
}
|
|
44485
44510
|
setP("ParamBreath", (Math.sin(t2 * 1.1) + 1) / 2);
|
|
44511
|
+
const gx = Math.max(-1, Math.min(1, gazeX));
|
|
44512
|
+
const gy = Math.max(-1, Math.min(1, gazeY));
|
|
44513
|
+
setP("ParamEyeBallX", gx * 0.9);
|
|
44514
|
+
setP("ParamEyeBallY", -gy * 0.65);
|
|
44486
44515
|
if (!window.__holdHead) {
|
|
44487
|
-
setP("ParamAngleX", Math.sin(t2 * 0.43) * 4);
|
|
44488
|
-
setP("ParamAngleY", Math.sin(t2 * 0.31 + 1) *
|
|
44489
|
-
setP("ParamBodyAngleX", Math.sin(t2 * 0.29) * 2);
|
|
44516
|
+
setP("ParamAngleX", Math.sin(t2 * 0.43) * 1.4 + gx * 7);
|
|
44517
|
+
setP("ParamAngleY", Math.sin(t2 * 0.31 + 1) * 1.2 - gy * 5);
|
|
44518
|
+
setP("ParamBodyAngleX", Math.sin(t2 * 0.29) * 2 + gx * 2);
|
|
44490
44519
|
}
|
|
44520
|
+
maybeMotion();
|
|
44491
44521
|
if (mouth > 0) {
|
|
44492
44522
|
mouth = Math.max(0, mouth - app.ticker.deltaMS / 1e3);
|
|
44493
44523
|
setP("ParamMouthOpenY", (Math.sin(t2 * 18) * 0.5 + 0.5) * 0.26 + 0.02);
|
|
44494
44524
|
}
|
|
44495
44525
|
core.update();
|
|
44496
44526
|
});
|
|
44527
|
+
function maybeMotion() {
|
|
44528
|
+
if (!model || !motionGroups.length) return;
|
|
44529
|
+
const now = performance.now();
|
|
44530
|
+
if (now < nextMotionAt) return;
|
|
44531
|
+
nextMotionAt = now + 12e3 + Math.random() * 16e3;
|
|
44532
|
+
const g2 = motionGroups.indexOf("Idle") >= 0 ? "Idle" : motionGroups[Math.floor(Math.random() * motionGroups.length)];
|
|
44533
|
+
try {
|
|
44534
|
+
model.motion(g2, void 0, 3);
|
|
44535
|
+
} catch {
|
|
44536
|
+
}
|
|
44537
|
+
}
|
|
44497
44538
|
window.addEventListener("resize", () => setTimeout(fit, 60));
|
|
44498
44539
|
refresh(true).then(() => {
|
|
44499
44540
|
if (state.models.length) setTimeout(idleTick, 2500);
|
|
@@ -44562,6 +44603,62 @@ void main(void)\r
|
|
|
44562
44603
|
const d2 = e2.data || {};
|
|
44563
44604
|
if (d2.type === "dsh-live2d/bg") applyBg(String(d2.bg || "none"));
|
|
44564
44605
|
});
|
|
44606
|
+
window.addEventListener("pointermove", (e2) => {
|
|
44607
|
+
if (e2.target && e2.target.id === "bubble") return;
|
|
44608
|
+
gazeX = e2.clientX / Math.max(1, window.innerWidth) * 2 - 1;
|
|
44609
|
+
gazeY = e2.clientY / Math.max(1, window.innerHeight) * 2 - 1;
|
|
44610
|
+
});
|
|
44611
|
+
window.addEventListener("pointerleave", () => {
|
|
44612
|
+
gazeX = 0;
|
|
44613
|
+
gazeY = 0;
|
|
44614
|
+
});
|
|
44615
|
+
var pokeIdx = 0;
|
|
44616
|
+
$2("stage").addEventListener("pointerdown", (e2) => {
|
|
44617
|
+
if (!core) return;
|
|
44618
|
+
const now = Date.now();
|
|
44619
|
+
if (now - pokeAt < 800) return;
|
|
44620
|
+
pokeAt = now;
|
|
44621
|
+
const h2 = Math.max(1, app.renderer.height);
|
|
44622
|
+
const rel = e2.clientY / h2;
|
|
44623
|
+
if (rel < 0.42) say(POKE_HEAD[pokeIdx++ % POKE_HEAD.length], "\u5BB3\u7F9E");
|
|
44624
|
+
else if (rel < 0.75) say(POKE_BODY[pokeIdx++ % POKE_BODY.length], "\u751F\u6C14");
|
|
44625
|
+
else say(POKE_LOW[pokeIdx++ % POKE_LOW.length], "\u5F00\u5FC3");
|
|
44626
|
+
});
|
|
44627
|
+
(function dragBubble() {
|
|
44628
|
+
const b2 = $2("bubble");
|
|
44629
|
+
if (!b2) return;
|
|
44630
|
+
let bx = 0;
|
|
44631
|
+
let by = 0;
|
|
44632
|
+
try {
|
|
44633
|
+
const s2 = JSON.parse(localStorage.getItem("dsh-l2d-bub") || "{}");
|
|
44634
|
+
bx = Number(s2.x) || 0;
|
|
44635
|
+
by = Number(s2.y) || 0;
|
|
44636
|
+
b2.style.transform = "translate(" + bx + "px," + by + "px)";
|
|
44637
|
+
} catch {
|
|
44638
|
+
}
|
|
44639
|
+
b2.addEventListener("pointerdown", (e2) => {
|
|
44640
|
+
e2.preventDefault();
|
|
44641
|
+
const sx = e2.clientX;
|
|
44642
|
+
const sy = e2.clientY;
|
|
44643
|
+
const x0 = bx;
|
|
44644
|
+
const y0 = by;
|
|
44645
|
+
const move = (ev) => {
|
|
44646
|
+
bx = x0 + ev.clientX - sx;
|
|
44647
|
+
by = y0 + ev.clientY - sy;
|
|
44648
|
+
b2.style.transform = "translate(" + bx + "px," + by + "px)";
|
|
44649
|
+
};
|
|
44650
|
+
const up = () => {
|
|
44651
|
+
window.removeEventListener("pointermove", move);
|
|
44652
|
+
window.removeEventListener("pointerup", up);
|
|
44653
|
+
try {
|
|
44654
|
+
localStorage.setItem("dsh-l2d-bub", JSON.stringify({ x: Math.round(bx), y: Math.round(by) }));
|
|
44655
|
+
} catch {
|
|
44656
|
+
}
|
|
44657
|
+
};
|
|
44658
|
+
window.addEventListener("pointermove", move);
|
|
44659
|
+
window.addEventListener("pointerup", up);
|
|
44660
|
+
});
|
|
44661
|
+
})();
|
|
44565
44662
|
})();
|
|
44566
44663
|
/*! Bundled license information:
|
|
44567
44664
|
|
package/lib/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
;(() => {
|
|
2
|
-
const makeFactory = () => {
|
|
2
|
+
const makeFactory = (require) => {
|
|
3
3
|
const react = require('react');
|
|
4
4
|
const h = react.createElement;
|
|
5
5
|
const name = 'dsh-live2d';
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
display:flex;flex-direction:column}
|
|
18
18
|
.dsh-l2d-card.collapsed{width:146px!important;height:36px!important}
|
|
19
19
|
.dsh-l2d-head{display:flex;align-items:center;gap:6px;padding:8px 10px;color:#9aa3b0;font-size:11px;
|
|
20
|
-
letter-spacing:.5px;border-bottom:1px solid #2c313a;user-select:none;flex:none}
|
|
20
|
+
letter-spacing:.5px;border-bottom:1px solid #2c313a;user-select:none;flex:none;cursor:move}
|
|
21
21
|
.dsh-l2d-dot{width:6px;height:6px;border-radius:50%;background:#6aa9ff;flex:none}
|
|
22
22
|
.dsh-l2d-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
23
23
|
.dsh-l2d-btns{margin-left:auto;display:flex;gap:6px;flex:none}
|
|
@@ -46,17 +46,31 @@
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function readPos() {
|
|
50
|
+
try {
|
|
51
|
+
const p = JSON.parse(localStorage.getItem('dsh-l2d-pos') || '{}');
|
|
52
|
+
return { x: Number(p.x) || 0, y: Number(p.y) || 0 };
|
|
53
|
+
} catch {
|
|
54
|
+
return { x: 0, y: 0 };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
function Panel() {
|
|
50
59
|
const [open, setOpen] = react.useState(() => localStorage.getItem('dsh-l2d-open') !== '0');
|
|
51
60
|
const [title, setTitle] = react.useState('Live2D');
|
|
52
61
|
const [bg, setBg] = react.useState(() => localStorage.getItem('dsh-l2d-bg') || 'none');
|
|
53
62
|
const [size, setSize] = react.useState(readSize);
|
|
63
|
+
const [pos, setPos] = react.useState(readPos);
|
|
54
64
|
const [nonce, setNonce] = react.useState(0);
|
|
55
65
|
const ref = react.useRef(null);
|
|
56
66
|
|
|
57
67
|
react.useEffect(() => localStorage.setItem('dsh-l2d-open', open ? '1' : '0'), [open]);
|
|
58
68
|
react.useEffect(() => localStorage.setItem('dsh-l2d-bg', bg), [bg]);
|
|
59
69
|
react.useEffect(() => localStorage.setItem('dsh-l2d-size', JSON.stringify(size)), [size]);
|
|
70
|
+
react.useEffect(() => localStorage.setItem('dsh-l2d-pos', JSON.stringify(pos)), [pos]);
|
|
71
|
+
react.useEffect(() => {
|
|
72
|
+
frameWin = (ref.current && ref.current.contentWindow) || null;
|
|
73
|
+
}, [nonce, open]);
|
|
60
74
|
react.useEffect(() => {
|
|
61
75
|
if (ref.current && ref.current.contentWindow) {
|
|
62
76
|
ref.current.contentWindow.postMessage({ type: 'dsh-live2d/bg', bg }, '*');
|
|
@@ -92,26 +106,48 @@
|
|
|
92
106
|
window.addEventListener('pointerup', up);
|
|
93
107
|
};
|
|
94
108
|
|
|
109
|
+
const startMove = (e) => {
|
|
110
|
+
if (e.button !== undefined && e.button !== 0) return;
|
|
111
|
+
const sx = e.clientX;
|
|
112
|
+
const sy = e.clientY;
|
|
113
|
+
const x0 = pos.x;
|
|
114
|
+
const y0 = pos.y;
|
|
115
|
+
const move = (ev) => {
|
|
116
|
+
const nx = x0 + (ev.clientX - sx);
|
|
117
|
+
const ny = y0 + (ev.clientY - sy);
|
|
118
|
+
setPos({ x: Math.round(nx), y: Math.round(ny) });
|
|
119
|
+
};
|
|
120
|
+
const up = () => {
|
|
121
|
+
window.removeEventListener('pointermove', move);
|
|
122
|
+
window.removeEventListener('pointerup', up);
|
|
123
|
+
};
|
|
124
|
+
window.addEventListener('pointermove', move);
|
|
125
|
+
window.addEventListener('pointerup', up);
|
|
126
|
+
};
|
|
127
|
+
const noDrag = (e) => e.stopPropagation();
|
|
128
|
+
|
|
95
129
|
const manage = () => ref.current && ref.current.contentWindow.postMessage({ type: 'dsh-live2d/manage' }, '*');
|
|
96
130
|
const cycleBg = () => setBg(BGS[(BGS.indexOf(bg) + 1) % BGS.length]);
|
|
97
131
|
|
|
98
|
-
const cardStyle = open
|
|
132
|
+
const cardStyle = open
|
|
133
|
+
? { width: size.w + 'px', height: size.h + 'px', transform: 'translate(' + pos.x + 'px,' + pos.y + 'px)' }
|
|
134
|
+
: { transform: 'translate(' + pos.x + 'px,' + pos.y + 'px)' };
|
|
99
135
|
|
|
100
136
|
return h(
|
|
101
137
|
'div',
|
|
102
138
|
{ className: 'dsh-l2d-card' + (open ? '' : ' collapsed'), style: cardStyle },
|
|
103
139
|
h(
|
|
104
140
|
'div',
|
|
105
|
-
{ className: 'dsh-l2d-head' },
|
|
141
|
+
{ className: 'dsh-l2d-head', onPointerDown: startMove },
|
|
106
142
|
h('span', { className: 'dsh-l2d-dot' }),
|
|
107
143
|
h('span', { className: 'dsh-l2d-name' }, title),
|
|
108
144
|
h(
|
|
109
145
|
'span',
|
|
110
146
|
{ className: 'dsh-l2d-btns' },
|
|
111
|
-
open && h('button', { title: '模型管理', onClick: manage }, '⚙'),
|
|
112
|
-
open && h('button', { title: '背景:' + BG_LABEL[bg], onClick: cycleBg }, '◐'),
|
|
113
|
-
open && h('button', { title: '刷新', onClick: () => setNonce((n) => n + 1) }, '↻'),
|
|
114
|
-
h('button', { title: open ? '收起' : '展开', onClick: () => setOpen(!open) }, open ? '⌄' : '⌃'),
|
|
147
|
+
open && h('button', { title: '模型管理', onPointerDown: noDrag, onClick: manage }, '⚙'),
|
|
148
|
+
open && h('button', { title: '背景:' + BG_LABEL[bg], onPointerDown: noDrag, onClick: cycleBg }, '◐'),
|
|
149
|
+
open && h('button', { title: '刷新', onPointerDown: noDrag, onClick: () => setNonce((n) => n + 1) }, '↻'),
|
|
150
|
+
h('button', { title: open ? '收起' : '展开', onPointerDown: noDrag, onClick: () => setOpen(!open) }, open ? '⌄' : '⌃'),
|
|
115
151
|
),
|
|
116
152
|
),
|
|
117
153
|
open
|
|
@@ -121,21 +157,89 @@
|
|
|
121
157
|
className: 'dsh-l2d-body',
|
|
122
158
|
src: BASE + '/viewer?embed=1&bg=' + encodeURIComponent(bg),
|
|
123
159
|
allow: 'autoplay',
|
|
160
|
+
onLoad: () => {
|
|
161
|
+
frameWin = (ref.current && ref.current.contentWindow) || null;
|
|
162
|
+
},
|
|
124
163
|
})
|
|
125
164
|
: null,
|
|
126
165
|
open ? h('div', { className: 'dsh-l2d-grip', onPointerDown: startDrag, title: '拖动改大小' }) : null,
|
|
127
166
|
);
|
|
128
167
|
}
|
|
129
168
|
|
|
169
|
+
let frameWin = null;
|
|
170
|
+
let lastSaid = '';
|
|
171
|
+
let lastSaidAt = 0;
|
|
172
|
+
|
|
173
|
+
const MOOD_HINTS = [
|
|
174
|
+
['生气', ['气', '烦', '讨厌', '凭什么']],
|
|
175
|
+
['委屈', ['委屈', '难过', '对不起', '哭']],
|
|
176
|
+
['害羞', ['唔', '别看', '害羞', '脸红']],
|
|
177
|
+
['开心', ['谢谢', '喜欢', '开心', '好耶']],
|
|
178
|
+
['困', ['困', '睡', '熬夜']],
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
function guessMood(text) {
|
|
182
|
+
for (let i = 0; i < MOOD_HINTS.length; i++) {
|
|
183
|
+
const words = MOOD_HINTS[i][1];
|
|
184
|
+
for (let j = 0; j < words.length; j++) {
|
|
185
|
+
if (text.indexOf(words[j]) >= 0) return MOOD_HINTS[i][0];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return '平静';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function pushSay(text) {
|
|
192
|
+
const t = String(text || '').replace(/\s+/g, ' ').trim();
|
|
193
|
+
if (!t || t === lastSaid) return;
|
|
194
|
+
if (Date.now() - lastSaidAt < 700) return;
|
|
195
|
+
lastSaid = t;
|
|
196
|
+
lastSaidAt = Date.now();
|
|
197
|
+
if (!frameWin) return;
|
|
198
|
+
try {
|
|
199
|
+
frameWin.postMessage({ type: 'dsh-live2d/say', text: t, mood: guessMood(t) }, '*');
|
|
200
|
+
} catch {}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function ChatProbe(props) {
|
|
204
|
+
const anchor = react.useRef(null);
|
|
205
|
+
const role =
|
|
206
|
+
(props && (props.role || props.kind)) ||
|
|
207
|
+
(props && props.node && (props.node.role || props.node.kind)) ||
|
|
208
|
+
(props && props.message && props.message.role) ||
|
|
209
|
+
'';
|
|
210
|
+
const skip = /user|human|prompt/i.test(String(role));
|
|
211
|
+
react.useEffect(() => {
|
|
212
|
+
if (skip) return;
|
|
213
|
+
const el = anchor.current;
|
|
214
|
+
if (!el) return;
|
|
215
|
+
let node = el.parentElement;
|
|
216
|
+
let text = '';
|
|
217
|
+
for (let i = 0; i < 5 && node; i++) {
|
|
218
|
+
const t = (node.textContent || '').trim();
|
|
219
|
+
if (t.length > 3 && t.length < 900) {
|
|
220
|
+
text = t;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
node = node.parentElement;
|
|
224
|
+
}
|
|
225
|
+
if (text) pushSay(text);
|
|
226
|
+
});
|
|
227
|
+
if (skip) return null;
|
|
228
|
+
return h('i', { ref: anchor, style: { display: 'none' } });
|
|
229
|
+
}
|
|
230
|
+
|
|
130
231
|
function apply(ctx) {
|
|
131
232
|
ensureCss();
|
|
132
233
|
ctx.slots.inject('shell.overlay', function* () {
|
|
133
234
|
yield ctx.slots.register({ name: 'shell.overlay', id: 'live2d', order: 900 }, Panel);
|
|
134
235
|
});
|
|
236
|
+
ctx.slots.inject('conversation.chat.node', function* () {
|
|
237
|
+
yield ctx.slots.register({ name: 'conversation.chat.node', id: 'live2d-say', order: 900 }, ChatProbe);
|
|
238
|
+
});
|
|
135
239
|
}
|
|
136
240
|
|
|
137
241
|
return { apply, inject, name };
|
|
138
242
|
};
|
|
139
243
|
|
|
140
|
-
window.__ModuleLoader__.load({ id: 'dsh-
|
|
244
|
+
window.__ModuleLoader__.load({ id: 'dsh-live2dwaifu', factory: makeFactory });
|
|
141
245
|
})();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-live2dwaifu",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "给 DSH 网页右下角请一个 Live2D
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "给 DSH 网页右下角请一个 Live2D 角色:会眨眼、会呼吸、视线跟着鼠标、点她有反应、说话时嘴动、心情变了眉毛跟着动。模型不用内置——谁装谁放自己的文件夹;支持自定义 .exp3.json 表情和 .motion3.json 动作,卡片能拖能缩,背景可换。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|