dsh-plugin-vrmpet3d 0.1.0 → 0.1.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 +2 -0
- package/desktop/main.js +45 -3
- package/desktop/preload.js +6 -2
- package/lib/client.core.js +209 -13
- package/lib/client.js +278 -42
- package/lib/config-page.html +71 -45
- package/lib/config-page.js +1120 -229
- package/lib/core.js +209 -13
- package/lib/desktop-page.js +144 -8
- package/lib/global-page.html +140 -99
- package/lib/index.js +150 -33
- package/lib/model-picker.js +139 -49
- package/lib/picker-page.html +34 -8
- package/lib/picker-page.js +192 -11
- package/lib/setup-page.html +68 -49
- package/lib/setup-page.js +395 -42
- package/lib/whale.js +191 -0
- package/package.json +1 -1
package/lib/global-page.html
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
|
|
6
|
+
<!--
|
|
7
|
+
The title, the nav and every row below carry `data-i18n*`, and the Chinese text stays INLINE as the no-JS
|
|
8
|
+
fallback: this page's script can fail to load (that is what the watchdog at the bottom reports), and a page
|
|
9
|
+
that failed should still read in one language rather than show nothing. `lib/config-page.js` owns the table
|
|
10
|
+
and `core.applyI18n` writes these places; a key the table lacks leaves the markup alone.
|
|
11
|
+
-->
|
|
12
|
+
<title data-i18n="titleGlobal">vrmpet3d · 通用设置</title>
|
|
7
13
|
<meta name="color-scheme" content="dark light">
|
|
8
14
|
<!--
|
|
9
15
|
The stylesheet both settings pages share. One file rather than a copy in each page: the two pages render
|
|
@@ -26,118 +32,130 @@
|
|
|
26
32
|
-->
|
|
27
33
|
<div class="page-loading" role="status" aria-live="polite">
|
|
28
34
|
<span class="page-loading-spinner" aria-hidden="true"></span>
|
|
29
|
-
<span>正在加载设置…</span>
|
|
35
|
+
<span data-i18n="loading">正在加载设置…</span>
|
|
30
36
|
</div>
|
|
31
37
|
|
|
32
|
-
<header class="page-header"><div class="eyebrow">VRMPET3D · SETTINGS</div><h1>通用设置</h1><div id="status" role="status" aria-live="polite">正在读取…</div></header>
|
|
33
|
-
<nav class="section-nav" aria-label="设置分组"><a href="#settings-0">显示与外观</a><a href="#settings-1">视线</a><a href="#settings-2">鼠标互动与物理</a><a href="#settings-3">状态与动作</a><a href="#settings-4">碎碎念</a><a href="#settings-5">模型导入</a><a href="#settings-6">动作导入</a><a href="#settings-7">其他</a></nav>
|
|
38
|
+
<header class="page-header"><div class="eyebrow">VRMPET3D · SETTINGS</div><h1 data-i18n="headingGlobal">通用设置</h1><div id="status" role="status" aria-live="polite" data-i18n="reading">正在读取…</div></header>
|
|
39
|
+
<nav class="section-nav" aria-label="设置分组" data-i18n-aria="navSections"><a href="#settings-0" data-i18n="sectionDisplay">显示与外观</a><a href="#settings-1" data-i18n="sectionGaze">视线</a><a href="#settings-2" data-i18n="sectionMouse">鼠标互动与物理</a><a href="#settings-3" data-i18n="sectionState">状态与动作</a><a href="#settings-4" data-i18n="sectionSpeech">碎碎念</a><a href="#settings-5" data-i18n="sectionModelImport">模型导入</a><a href="#settings-6" data-i18n="sectionMotionImport">动作导入</a><a href="#settings-7" data-i18n="sectionOther">其他</a></nav>
|
|
34
40
|
<main>
|
|
35
41
|
<section class="settings-section" id="settings-0">
|
|
36
|
-
<h2>显示与外观</h2>
|
|
42
|
+
<h2 data-i18n="sectionDisplay">显示与外观</h2>
|
|
37
43
|
<div class="card">
|
|
38
44
|
|
|
45
|
+
<!--
|
|
46
|
+
界面语言 — the FIRST row, because everything under it is written in whichever answer it holds.
|
|
47
|
+
-->
|
|
48
|
+
<div class="row">
|
|
49
|
+
<label class="k" for="pref-language" data-i18n="language">界面语言</label>
|
|
50
|
+
<select id="pref-language">
|
|
51
|
+
<option value="auto" data-i18n="languageAuto">自动</option>
|
|
52
|
+
<option value="zh" data-i18n="languageZh">中文</option>
|
|
53
|
+
<option value="en" data-i18n="languageEn">English</option>
|
|
54
|
+
</select>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
39
57
|
<div class="row" id="pref-surface-row" hidden>
|
|
40
|
-
<span class="k">桌宠界面</span>
|
|
41
|
-
<label class="inline"><input type="radio" name="pref-surface" value="app">
|
|
42
|
-
<label class="inline"><input type="radio" name="pref-surface" value="auto">
|
|
58
|
+
<span class="k" data-i18n="petSurface">桌宠界面</span>
|
|
59
|
+
<label class="inline"><input type="radio" name="pref-surface" value="app"> <span data-i18n="surfaceApp">内置宠物</span></label>
|
|
60
|
+
<label class="inline"><input type="radio" name="pref-surface" value="auto"> <span data-i18n="auto">自动</span></label>
|
|
43
61
|
</div>
|
|
44
62
|
<div class="row">
|
|
45
|
-
<label class="k" for="pref-size">应用内大小</label>
|
|
63
|
+
<label class="k" for="pref-size" data-i18n="sizeApp">应用内大小</label>
|
|
46
64
|
<input type="range" id="pref-size" min="40" max="260" step="1">
|
|
47
65
|
<span class="value" id="pref-size-value">100%</span>
|
|
48
|
-
<button id="pref-size-reset" class="ghost">复位</button>
|
|
66
|
+
<button id="pref-size-reset" class="ghost" data-i18n="reset">复位</button>
|
|
49
67
|
</div>
|
|
50
68
|
<div class="row">
|
|
51
|
-
<label class="k" for="pref-window-size">悬浮窗大小</label>
|
|
69
|
+
<label class="k" for="pref-window-size" data-i18n="sizeWindow">悬浮窗大小</label>
|
|
52
70
|
<input type="range" id="pref-window-size" min="10" max="400" step="1">
|
|
53
71
|
<span class="value" id="pref-window-size-value">100%</span>
|
|
54
|
-
<button id="pref-window-size-reset" class="ghost">复位</button>
|
|
72
|
+
<button id="pref-window-size-reset" class="ghost" data-i18n="reset">复位</button>
|
|
55
73
|
</div>
|
|
56
74
|
<!--
|
|
57
75
|
Legacy nap control: retained hidden and disabled for compatibility. Idle stays resting.
|
|
58
76
|
-->
|
|
59
77
|
<div class="row" hidden>
|
|
60
|
-
<label class="k" for="pref-sleep">空闲休眠</label>
|
|
78
|
+
<label class="k" for="pref-sleep" data-i18n="idleNap">空闲休眠</label>
|
|
61
79
|
<select id="pref-sleep" disabled>
|
|
62
|
-
<option value="30000">30 秒</option>
|
|
63
|
-
<option value="120000">2 分钟</option>
|
|
64
|
-
<option value="300000">5 分钟</option>
|
|
80
|
+
<option value="30000" data-i18n="sleep30">30 秒</option>
|
|
81
|
+
<option value="120000" data-i18n="sleep120">2 分钟</option>
|
|
82
|
+
<option value="300000" data-i18n="sleep300">5 分钟</option>
|
|
65
83
|
</select>
|
|
66
84
|
</div>
|
|
67
85
|
<div class="row">
|
|
68
|
-
<label class="k" for="pref-hidden">视角</label>
|
|
69
|
-
<button id="pref-view-reset">复位视角</button>
|
|
70
|
-
<span class="k sub">宠物</span>
|
|
71
|
-
<label class="inline"><input type="checkbox" id="pref-hidden">
|
|
86
|
+
<label class="k" for="pref-hidden" data-i18n="view">视角</label>
|
|
87
|
+
<button id="pref-view-reset" data-i18n="viewReset">复位视角</button>
|
|
88
|
+
<span class="k sub" data-i18n="pet">宠物</span>
|
|
89
|
+
<label class="inline"><input type="checkbox" id="pref-hidden"> <span data-i18n="hide">隐藏</span></label>
|
|
72
90
|
</div>
|
|
73
91
|
</div>
|
|
74
92
|
|
|
75
93
|
</section>
|
|
76
94
|
|
|
77
95
|
<section class="settings-section" id="settings-1">
|
|
78
|
-
<h2>视线</h2>
|
|
96
|
+
<h2 data-i18n="sectionGaze">视线</h2>
|
|
79
97
|
<div class="card" id="gaze-body">
|
|
80
|
-
<div class="row"><label class="k" for="pref-look">看向光标</label><label class="inline"><input type="checkbox" id="pref-look">
|
|
98
|
+
<div class="row"><label class="k" for="pref-look" data-i18n="lookAtPointer">看向光标</label><label class="inline"><input type="checkbox" id="pref-look"> <span data-i18n="on">开启</span></label></div>
|
|
81
99
|
|
|
82
100
|
<div class="row">
|
|
83
|
-
<label class="k" for="pref-gaze-curve">曲率</label>
|
|
101
|
+
<label class="k" for="pref-gaze-curve" data-i18n="curve">曲率</label>
|
|
84
102
|
<input type="range" id="pref-gaze-curve" min="5" max="100" step="5">
|
|
85
103
|
<span class="value" id="pref-gaze-curve-value">0.10</span>
|
|
86
|
-
<button id="pref-gaze-curve-reset" class="ghost">复位</button>
|
|
104
|
+
<button id="pref-gaze-curve-reset" class="ghost" data-i18n="reset">复位</button>
|
|
87
105
|
</div>
|
|
88
106
|
<div class="row">
|
|
89
|
-
<label class="k" for="pref-gaze-span-x">满偏距离</label>
|
|
90
|
-
<input type="number" id="pref-gaze-span-x" step="10" min="0" max="6000"> px
|
|
91
|
-
<input type="number" id="pref-gaze-span-y" step="10" min="0" max="6000"> px
|
|
92
|
-
<button id="pref-gaze-span-reset" class="ghost">自动</button>
|
|
107
|
+
<label class="k" for="pref-gaze-span-x" data-i18n="span">满偏距离</label>
|
|
108
|
+
<input type="number" id="pref-gaze-span-x" step="10" min="0" max="6000"> <span data-i18n="pxHorizontal">px 横向</span>
|
|
109
|
+
<input type="number" id="pref-gaze-span-y" step="10" min="0" max="6000"> <span data-i18n="pxVertical">px 纵向</span>
|
|
110
|
+
<button id="pref-gaze-span-reset" class="ghost" data-i18n="auto">自动</button>
|
|
93
111
|
</div>
|
|
94
112
|
<div class="row">
|
|
95
|
-
<label class="k" for="pref-gaze-idle">静止停看</label>
|
|
96
|
-
<input type="number" id="pref-gaze-idle" step="1" min="0" max="600">
|
|
97
|
-
<button id="pref-gaze-idle-reset" class="ghost">复位</button>
|
|
113
|
+
<label class="k" for="pref-gaze-idle" data-i18n="gazeIdle">静止停看</label>
|
|
114
|
+
<input type="number" id="pref-gaze-idle" step="1" min="0" max="600"> <span data-i18n="secondsUnit">秒</span>
|
|
115
|
+
<button id="pref-gaze-idle-reset" class="ghost" data-i18n="reset">复位</button>
|
|
98
116
|
</div>
|
|
99
117
|
<p class="hint" id="pref-gaze-span-auto"></p>
|
|
100
|
-
<p class="hint applied" id="pref-gaze-applied">(正在读取悬浮窗…)</p>
|
|
118
|
+
<p class="hint applied" id="pref-gaze-applied" data-i18n="readingWindow">(正在读取悬浮窗…)</p>
|
|
101
119
|
</div>
|
|
102
120
|
|
|
103
121
|
</section>
|
|
104
122
|
|
|
105
123
|
<section class="settings-section" id="settings-2">
|
|
106
|
-
<h2>鼠标互动与物理</h2>
|
|
124
|
+
<h2 data-i18n="sectionMouse">鼠标互动与物理</h2>
|
|
107
125
|
<div class="card">
|
|
108
|
-
<div class="row"><label class="k" for="pref-physics">弹簧骨物理</label><label class="inline"><input type="checkbox" id="pref-physics">
|
|
126
|
+
<div class="row"><label class="k" for="pref-physics" data-i18n="springPhysics">弹簧骨物理</label><label class="inline"><input type="checkbox" id="pref-physics"> <span data-i18n="on">开启</span></label></div>
|
|
109
127
|
|
|
110
128
|
<div class="row">
|
|
111
|
-
<label class="k" for="pref-wind-power">划过强度</label>
|
|
129
|
+
<label class="k" for="pref-wind-power" data-i18n="windPower">划过强度</label>
|
|
112
130
|
<input type="range" id="pref-wind-power" min="0" max="200" step="5">
|
|
113
131
|
<span class="value" id="pref-wind-power-value">1.00</span>
|
|
114
|
-
<button id="pref-wind-power-reset" class="ghost">复位</button>
|
|
132
|
+
<button id="pref-wind-power-reset" class="ghost" data-i18n="reset">复位</button>
|
|
115
133
|
</div>
|
|
116
134
|
<div class="row">
|
|
117
|
-
<label class="k" for="pref-wind-radius">作用范围</label>
|
|
135
|
+
<label class="k" for="pref-wind-radius" data-i18n="windRadius">作用范围</label>
|
|
118
136
|
<input type="range" id="pref-wind-radius" min="0" max="100" step="5">
|
|
119
137
|
<span class="value" id="pref-wind-radius-value">15%</span>
|
|
120
|
-
<button id="pref-wind-radius-reset" class="ghost">复位</button>
|
|
138
|
+
<button id="pref-wind-radius-reset" class="ghost" data-i18n="reset">复位</button>
|
|
121
139
|
</div>
|
|
122
140
|
<div class="row">
|
|
123
|
-
<label class="k" for="pref-wind-decay">余韵</label>
|
|
124
|
-
<input type="number" id="pref-wind-decay" step="0.02" min="0.02" max="2">
|
|
125
|
-
<button id="pref-wind-decay-reset" class="ghost">复位</button>
|
|
141
|
+
<label class="k" for="pref-wind-decay" data-i18n="windDecay">余韵</label>
|
|
142
|
+
<input type="number" id="pref-wind-decay" step="0.02" min="0.02" max="2"> <span data-i18n="secondsUnit">秒</span>
|
|
143
|
+
<button id="pref-wind-decay-reset" class="ghost" data-i18n="reset">复位</button>
|
|
126
144
|
</div>
|
|
127
145
|
<div class="row">
|
|
128
|
-
<label class="k" for="pref-wind-speed">满力速度</label>
|
|
129
|
-
<input type="number" id="pref-wind-speed" step="100" min="100" max="6000"> px
|
|
130
|
-
<button id="pref-wind-speed-reset" class="ghost">复位</button>
|
|
146
|
+
<label class="k" for="pref-wind-speed" data-i18n="windSpeed">满力速度</label>
|
|
147
|
+
<input type="number" id="pref-wind-speed" step="100" min="100" max="6000"> <span data-i18n="pxPerSecond">px/秒</span>
|
|
148
|
+
<button id="pref-wind-speed-reset" class="ghost" data-i18n="reset">复位</button>
|
|
131
149
|
</div>
|
|
132
|
-
<p class="hint applied" id="pref-wind-applied">(正在读取悬浮窗…)</p>
|
|
150
|
+
<p class="hint applied" id="pref-wind-applied" data-i18n="readingWindow">(正在读取悬浮窗…)</p>
|
|
133
151
|
</div>
|
|
134
152
|
|
|
135
153
|
</section>
|
|
136
154
|
|
|
137
155
|
<section class="settings-section" id="settings-3">
|
|
138
|
-
<h2
|
|
156
|
+
<h2><span data-i18n="sectionState">状态与动作</span> <span id="motion-count"></span></h2>
|
|
139
157
|
<div class="card">
|
|
140
|
-
<div class="row"><label class="k" for="pref-motion">播放动画</label><label class="inline"><input type="checkbox" id="pref-motion">
|
|
158
|
+
<div class="row"><label class="k" for="pref-motion" data-i18n="playMotion">播放动画</label><label class="inline"><input type="checkbox" id="pref-motion"> <span data-i18n="on">开启</span></label></div>
|
|
141
159
|
|
|
142
160
|
<div id="motion-list"></div>
|
|
143
161
|
<p class="hint" id="motion-note"></p>
|
|
@@ -146,13 +164,19 @@
|
|
|
146
164
|
</section>
|
|
147
165
|
|
|
148
166
|
<section class="settings-section" id="settings-4">
|
|
149
|
-
<h2>碎碎念</h2>
|
|
167
|
+
<h2 data-i18n="sectionSpeech">碎碎念</h2>
|
|
150
168
|
<!--
|
|
151
169
|
碎碎念的免责说明 (operator, 2026-10-15): 「碎碎念说的话都是玩笑,请勿当真,该插件并不会进行任何实际操作,也不会干扰dsh运行,
|
|
152
170
|
在配置较差的机器上可能轻微影响性能」.
|
|
153
171
|
|
|
154
172
|
设置页这份是**精简版**(操作者同一条消息里也说了「设置页面精简一些」):引导页讲全,因为那是用户第一次决定"让不让它说话"
|
|
155
173
|
的地方;这里只留一句提醒加一句性能提示,压在碎碎念这一段的最上面 —— 改台词来源、调频率的人都会先看到它。
|
|
174
|
+
|
|
175
|
+
This paragraph's TAG is not touched, and that is deliberate: `verify-desktop.mjs` reads it by matching its
|
|
176
|
+
opening tag with the class and id written in that exact order and in that exact spelling (so an extra attribute
|
|
177
|
+
or a wrapping `<span>` would hide the copy from the check), and it then compares the copy's length against the
|
|
178
|
+
guide's longer one. `lib/config-page.js` gives the paragraph `data-i18n-html` at boot instead (see
|
|
179
|
+
`tagFixedRows`), which is enough for every later pass.
|
|
156
180
|
-->
|
|
157
181
|
<p class="hint" id="speech-disclaimer">
|
|
158
182
|
<strong>碎碎念只是玩笑,请勿当真。</strong>它不会对你的项目或文件做任何实际操作,也不会干扰 DSH 的运行;低配机器上
|
|
@@ -160,50 +184,50 @@
|
|
|
160
184
|
</p>
|
|
161
185
|
<div class="card">
|
|
162
186
|
<div class="row">
|
|
163
|
-
<label class="k" for="pref-speech">开启</label>
|
|
164
|
-
<label class="inline"><input type="checkbox" id="pref-speech">
|
|
187
|
+
<label class="k" for="pref-speech" data-i18n="speechEnable">开启</label>
|
|
188
|
+
<label class="inline"><input type="checkbox" id="pref-speech"> <span data-i18n="speechTalk">说话</span></label>
|
|
165
189
|
</div>
|
|
166
190
|
<div class="row">
|
|
167
|
-
<label class="k" for="pref-speech-hold">每句显示</label>
|
|
168
|
-
<input type="number" id="pref-speech-hold" min="1" max="30" step="1">
|
|
169
|
-
<button id="pref-speech-hold-reset" class="ghost">复位</button>
|
|
191
|
+
<label class="k" for="pref-speech-hold" data-i18n="speechHold">每句显示</label>
|
|
192
|
+
<input type="number" id="pref-speech-hold" min="1" max="30" step="1"> <span data-i18n="secondsUnit">秒</span>
|
|
193
|
+
<button id="pref-speech-hold-reset" class="ghost" data-i18n="reset">复位</button>
|
|
170
194
|
</div>
|
|
171
195
|
<div class="row">
|
|
172
|
-
<label class="k" for="pref-speech-gap">最短间隔</label>
|
|
173
|
-
<input type="number" id="pref-speech-gap" min="0" max="600" step="1">
|
|
174
|
-
<button id="pref-speech-gap-reset" class="ghost">复位</button>
|
|
196
|
+
<label class="k" for="pref-speech-gap" data-i18n="speechGap">最短间隔</label>
|
|
197
|
+
<input type="number" id="pref-speech-gap" min="0" max="600" step="1"> <span data-i18n="secondsUnit">秒</span>
|
|
198
|
+
<button id="pref-speech-gap-reset" class="ghost" data-i18n="reset">复位</button>
|
|
175
199
|
</div>
|
|
176
200
|
<div class="row">
|
|
177
|
-
<label class="k" for="pref-speech-idle-min">空闲嘀咕间隔</label>
|
|
201
|
+
<label class="k" for="pref-speech-idle-min" data-i18n="speechIdleInterval">空闲嘀咕间隔</label>
|
|
178
202
|
<input type="number" id="pref-speech-idle-min" min="0" max="3600" step="5"> –
|
|
179
|
-
<input type="number" id="pref-speech-idle-max" min="0" max="3600" step="5">
|
|
180
|
-
<button id="pref-speech-idle-reset" class="ghost">复位</button>
|
|
203
|
+
<input type="number" id="pref-speech-idle-max" min="0" max="3600" step="5"> <span data-i18n="secondsUnit">秒</span>
|
|
204
|
+
<button id="pref-speech-idle-reset" class="ghost" data-i18n="reset">复位</button>
|
|
181
205
|
</div>
|
|
182
206
|
<!--
|
|
183
207
|
工作中 talks more often than 静息态 does (operator, 2026-10-13: 「工作中的嘀咕可以增加频率到12-36秒一次」), so it
|
|
184
208
|
gets its own window rather than a share of the idle one — the two are different remarks on different clocks.
|
|
185
209
|
-->
|
|
186
210
|
<div class="row">
|
|
187
|
-
<label class="k" for="pref-speech-work-min">工作嘀咕间隔</label>
|
|
211
|
+
<label class="k" for="pref-speech-work-min" data-i18n="speechWorkInterval">工作嘀咕间隔</label>
|
|
188
212
|
<input type="number" id="pref-speech-work-min" min="0" max="3600" step="5"> –
|
|
189
|
-
<input type="number" id="pref-speech-work-max" min="0" max="3600" step="5">
|
|
190
|
-
<button id="pref-speech-work-reset" class="ghost">复位</button>
|
|
213
|
+
<input type="number" id="pref-speech-work-max" min="0" max="3600" step="5"> <span data-i18n="secondsUnit">秒</span>
|
|
214
|
+
<button id="pref-speech-work-reset" class="ghost" data-i18n="reset">复位</button>
|
|
191
215
|
</div>
|
|
192
216
|
<div class="row">
|
|
193
|
-
<label class="k" for="pref-speech-source">台词来源</label>
|
|
217
|
+
<label class="k" for="pref-speech-source" data-i18n="speechSource">台词来源</label>
|
|
194
218
|
<select id="pref-speech-source">
|
|
195
|
-
<option value="builtin">内置短语</option>
|
|
196
|
-
<option value="custom">自定义短语</option>
|
|
197
|
-
<option value="ai">AI 生成</option>
|
|
219
|
+
<option value="builtin" data-i18n="sourceBuiltin">内置短语</option>
|
|
220
|
+
<option value="custom" data-i18n="sourceCustom">自定义短语</option>
|
|
221
|
+
<option value="ai" data-i18n="sourceAi">AI 生成</option>
|
|
198
222
|
</select>
|
|
199
223
|
</div>
|
|
200
224
|
<div id="speech-states"></div>
|
|
201
225
|
<div id="speech-ai">
|
|
202
226
|
<div class="row">
|
|
203
|
-
<label class="k" for="pref-speech-provider">AI 来源</label>
|
|
227
|
+
<label class="k" for="pref-speech-provider" data-i18n="speechAiFrom">AI 来源</label>
|
|
204
228
|
<select id="pref-speech-provider">
|
|
205
|
-
<option value="dsh">DSH 的 Provider</option>
|
|
206
|
-
<option value="custom">自定义 Provider</option>
|
|
229
|
+
<option value="dsh" data-i18n="providerDsh">DSH 的 Provider</option>
|
|
230
|
+
<option value="custom" data-i18n="providerCustom">自定义 Provider</option>
|
|
207
231
|
</select>
|
|
208
232
|
</div>
|
|
209
233
|
<div class="row" id="speech-dsh-provider-row">
|
|
@@ -211,7 +235,7 @@
|
|
|
211
235
|
<select id="pref-speech-dsh-provider"></select>
|
|
212
236
|
</div>
|
|
213
237
|
<div class="row" id="speech-dsh-model-row">
|
|
214
|
-
<label class="k" for="pref-speech-dsh-model">模型</label>
|
|
238
|
+
<label class="k" for="pref-speech-dsh-model" data-i18n="speechModel">模型</label>
|
|
215
239
|
<select id="pref-speech-dsh-model"></select>
|
|
216
240
|
</div>
|
|
217
241
|
<div class="row" id="speech-custom-url-row">
|
|
@@ -219,24 +243,24 @@
|
|
|
219
243
|
<input type="text" id="pref-speech-base-url" placeholder="https://api.example.com/v1" spellcheck="false">
|
|
220
244
|
</div>
|
|
221
245
|
<div class="row" id="speech-custom-model-row">
|
|
222
|
-
<label class="k" for="pref-speech-model">模型</label>
|
|
246
|
+
<label class="k" for="pref-speech-model" data-i18n="speechModel">模型</label>
|
|
223
247
|
<input type="text" id="pref-speech-model" placeholder="deepseek-chat" spellcheck="false">
|
|
224
248
|
</div>
|
|
225
249
|
<div class="row" id="speech-custom-key-row">
|
|
226
250
|
<label class="k" for="pref-speech-api-key">API Key</label>
|
|
227
251
|
<input type="password" id="pref-speech-api-key" spellcheck="false" autocomplete="off">
|
|
228
|
-
<button id="pref-speech-key-clear" class="ghost">清除</button>
|
|
252
|
+
<button id="pref-speech-key-clear" class="ghost" data-i18n="speechKeyClear">清除</button>
|
|
229
253
|
</div>
|
|
230
|
-
<div class="row"><span class="k">人格</span></div>
|
|
254
|
+
<div class="row"><span class="k" data-i18n="persona">人格</span></div>
|
|
231
255
|
<textarea id="pref-speech-persona" class="speech-persona" rows="3" spellcheck="false"></textarea>
|
|
232
256
|
<div class="row">
|
|
233
|
-
<label class="k" for="pref-speech-temperature">温度</label>
|
|
257
|
+
<label class="k" for="pref-speech-temperature" data-i18n="temperature">温度</label>
|
|
234
258
|
<input type="range" id="pref-speech-temperature" min="0" max="20" step="1">
|
|
235
259
|
<span class="value" id="pref-speech-temperature-value">0.50</span>
|
|
236
260
|
</div>
|
|
237
261
|
<div class="row">
|
|
238
|
-
<label class="k" for="pref-speech-timeout">超时</label>
|
|
239
|
-
<input type="number" id="pref-speech-timeout" min="1" max="60" step="1">
|
|
262
|
+
<label class="k" for="pref-speech-timeout" data-i18n="timeout">超时</label>
|
|
263
|
+
<input type="number" id="pref-speech-timeout" min="1" max="60" step="1"> <span data-i18n="secondsUnit">秒</span>
|
|
240
264
|
</div>
|
|
241
265
|
<!--
|
|
242
266
|
试一句 and its own answer, in ONE row.
|
|
@@ -246,9 +270,9 @@
|
|
|
246
270
|
sibling of 生成 now, on a full line of its own under it, and gold while it is a refusal.
|
|
247
271
|
-->
|
|
248
272
|
<div class="row">
|
|
249
|
-
<label class="k" for="pref-speech-test-state">试一句</label>
|
|
273
|
+
<label class="k" for="pref-speech-test-state" data-i18n="speechTest">试一句</label>
|
|
250
274
|
<select id="pref-speech-test-state"></select>
|
|
251
|
-
<button id="pref-speech-test" class="ghost">生成</button>
|
|
275
|
+
<button id="pref-speech-test" class="ghost" data-i18n="generate">生成</button>
|
|
252
276
|
<p class="hint" id="pref-speech-test-result" role="status" aria-live="polite"></p>
|
|
253
277
|
</div>
|
|
254
278
|
<p class="hint" id="speech-llm-note"></p>
|
|
@@ -258,17 +282,17 @@
|
|
|
258
282
|
</section>
|
|
259
283
|
|
|
260
284
|
<section class="settings-section" id="settings-5">
|
|
261
|
-
<h2>模型导入</h2>
|
|
285
|
+
<h2 data-i18n="sectionModelImport">模型导入</h2>
|
|
262
286
|
<div class="card">
|
|
263
287
|
<div class="row">
|
|
264
|
-
<span class="k">导入模型</span>
|
|
265
|
-
<button id="model-import-pick">选择模型文件…</button>
|
|
266
|
-
<button id="model-import-manual" class="ghost">手动输入路径</button>
|
|
288
|
+
<span class="k" data-i18n="importModel">导入模型</span>
|
|
289
|
+
<button id="model-import-pick" data-i18n="pickModelFile">选择模型文件…</button>
|
|
290
|
+
<button id="model-import-manual" class="ghost" data-i18n="manualPath">手动输入路径</button>
|
|
267
291
|
</div>
|
|
268
292
|
<div class="row" id="model-import-manual-row" hidden>
|
|
269
|
-
<label class="k" for="model-import-path">文件路径</label>
|
|
293
|
+
<label class="k" for="model-import-path" data-i18n="filePath">文件路径</label>
|
|
270
294
|
<input type="text" id="model-import-path" class="grow" placeholder="D:\Models\my.vrm">
|
|
271
|
-
<button id="model-import-add">导入</button>
|
|
295
|
+
<button id="model-import-add" data-i18n="import">导入</button>
|
|
272
296
|
</div>
|
|
273
297
|
<div id="model-import-list"></div>
|
|
274
298
|
<p class="hint" id="model-import-note"></p>
|
|
@@ -277,17 +301,17 @@
|
|
|
277
301
|
</section>
|
|
278
302
|
|
|
279
303
|
<section class="settings-section" id="settings-6">
|
|
280
|
-
<h2>动作导入</h2>
|
|
304
|
+
<h2 data-i18n="sectionMotionImport">动作导入</h2>
|
|
281
305
|
<div class="card">
|
|
282
306
|
<div class="row">
|
|
283
|
-
<span class="k">导入目录</span>
|
|
284
|
-
<button id="motion-import-pick">选择文件夹…</button>
|
|
285
|
-
<button id="motion-import-manual" class="ghost">手动输入路径</button>
|
|
307
|
+
<span class="k" data-i18n="importFolder">导入目录</span>
|
|
308
|
+
<button id="motion-import-pick" data-i18n="pickMotionDir">选择文件夹…</button>
|
|
309
|
+
<button id="motion-import-manual" class="ghost" data-i18n="manualPath">手动输入路径</button>
|
|
286
310
|
</div>
|
|
287
311
|
<div class="row" id="motion-import-manual-row" hidden>
|
|
288
|
-
<label class="k" for="motion-import-path">路径</label>
|
|
312
|
+
<label class="k" for="motion-import-path" data-i18n="path">路径</label>
|
|
289
313
|
<input type="text" id="motion-import-path" class="grow" placeholder="D:\Motions">
|
|
290
|
-
<button id="motion-import-add">添加</button>
|
|
314
|
+
<button id="motion-import-add" data-i18n="add">添加</button>
|
|
291
315
|
</div>
|
|
292
316
|
<div id="motion-import-list"></div>
|
|
293
317
|
<p class="hint" id="motion-import-note"></p>
|
|
@@ -296,11 +320,11 @@
|
|
|
296
320
|
</section>
|
|
297
321
|
|
|
298
322
|
<section class="settings-section" id="settings-7">
|
|
299
|
-
<h2>其他</h2>
|
|
323
|
+
<h2 data-i18n="sectionOther">其他</h2>
|
|
300
324
|
<div class="card">
|
|
301
325
|
<div class="row">
|
|
302
|
-
<span class="k">初次安装引导</span>
|
|
303
|
-
<button id="pref-setup-open">重新运行引导</button>
|
|
326
|
+
<span class="k" data-i18n="setupSection">初次安装引导</span>
|
|
327
|
+
<button id="pref-setup-open" data-i18n="setupRerun">重新运行引导</button>
|
|
304
328
|
<span class="note" id="pref-setup-note"></span>
|
|
305
329
|
</div>
|
|
306
330
|
</div>
|
|
@@ -312,23 +336,40 @@
|
|
|
312
336
|
A settings page that can only say "正在读取…" is useless when something fails: the text is the HTML's
|
|
313
337
|
own placeholder, and a script that never ran, 404'd or threw at its first line looks exactly like a
|
|
314
338
|
slow host. So this runs FIRST, catches what it can, and after two seconds says what is actually wrong.
|
|
339
|
+
|
|
340
|
+
It runs BEFORE `lib/core.js`, which is the whole point — the case it exists for is a core that never
|
|
341
|
+
loaded — so it cannot use `core.textFor` and carries its own tiny map instead: three sentences plus the
|
|
342
|
+
brackets a filename goes in, picked from `navigator.language` in one line. Chinese is the fallback,
|
|
343
|
+
because the markup around it is Chinese. Keep it small: this is a watchdog, not a second i18n system.
|
|
315
344
|
-->
|
|
316
345
|
<script>
|
|
317
346
|
(function () {
|
|
347
|
+
var ZH = /^(zh|cn|chinese)/i.test(navigator.language || '');
|
|
348
|
+
var WORDS = ZH
|
|
349
|
+
? {
|
|
350
|
+
error: "页面脚本出错:",
|
|
351
|
+
file: "(来自 {name})",
|
|
352
|
+
coreMissing: "初始化卡住:core.js 没有加载(检查 /vrmpet3d/window/lib/core.js 是否 404)。",
|
|
353
|
+
stuck: "初始化卡住:脚本已加载但没有完成初始化(看看窗口的控制台,或宿主是否提供 /vrmpet3d/settings)。",
|
|
354
|
+
}
|
|
355
|
+
: {
|
|
356
|
+
error: "Page script error: ",
|
|
357
|
+
file: "(from {name})",
|
|
358
|
+
coreMissing: "Stuck at startup: core.js did not load (check whether /vrmpet3d/window/lib/core.js is a 404).",
|
|
359
|
+
stuck: "Stuck at startup: the script loaded but never finished initialising (check the window console, or whether the host serves /vrmpet3d/settings).",
|
|
360
|
+
};
|
|
318
361
|
function show(message) {
|
|
319
362
|
var status = document.getElementById("status");
|
|
320
363
|
if (status) { status.textContent = message; status.className = "warn"; }
|
|
321
364
|
}
|
|
322
365
|
window.addEventListener("error", function (event) {
|
|
323
|
-
show(
|
|
366
|
+
show(WORDS.error + (event.message || event.type)
|
|
367
|
+
+ (event.filename ? " " + WORDS.file.replace("{name}", event.filename) : ""));
|
|
324
368
|
}, true);
|
|
325
369
|
window.setTimeout(function () {
|
|
326
370
|
if (window.__VRMPET3D_CONFIG_STATE__) return;
|
|
327
|
-
if (!window.__VRMPET3D_CORE__) {
|
|
328
|
-
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
show("初始化卡住:脚本已加载但没有完成初始化(看看窗口的控制台,或宿主是否提供 /vrmpet3d/settings)。");
|
|
371
|
+
if (!window.__VRMPET3D_CORE__) { show(WORDS.coreMissing); return; }
|
|
372
|
+
show(WORDS.stuck);
|
|
332
373
|
}, 2000);
|
|
333
374
|
})();
|
|
334
375
|
</script>
|