mocode-pet-app 0.1.0 → 1.0.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/assets/mascot.svg +0 -2
- package/assets/pets/01-robo-cat.svg +56 -0
- package/assets/pets/02-robo-dog.svg +53 -0
- package/assets/pets/03-robo-fox.svg +53 -0
- package/assets/pets/04-robo-panda.svg +52 -0
- package/assets/pets/05-robo-owl.svg +46 -0
- package/assets/pets/06-robo-bunny.svg +52 -0
- package/assets/pets/07-robo-frog.svg +39 -0
- package/assets/pets/08-robo-bear.svg +48 -0
- package/assets/pets/09-robo-penguin.svg +49 -0
- package/assets/pets/10-robo-dino.svg +56 -0
- package/assets/pets/11-slime-blob.svg +43 -0
- package/assets/pets/12-ghost-byte.svg +41 -0
- package/assets/pets/13-cactus-bot.svg +52 -0
- package/assets/pets/14-crystal-bot.svg +32 -0
- package/assets/pets/15-satellite-bot.svg +55 -0
- package/assets/pets/16-jellyfish-bot.svg +47 -0
- package/assets/pets/17-mushroom-bot.svg +47 -0
- package/assets/pets/18-star-bot.svg +45 -0
- package/assets/pets/manifest.json +25 -0
- package/assets/signal-light.svg +55 -0
- package/assets/tray-icon.png +0 -0
- package/dist/assets/mascot.svg +0 -2
- package/dist/assets/pets/01-robo-cat.svg +56 -0
- package/dist/assets/pets/02-robo-dog.svg +53 -0
- package/dist/assets/pets/03-robo-fox.svg +53 -0
- package/dist/assets/pets/04-robo-panda.svg +52 -0
- package/dist/assets/pets/05-robo-owl.svg +46 -0
- package/dist/assets/pets/06-robo-bunny.svg +52 -0
- package/dist/assets/pets/07-robo-frog.svg +39 -0
- package/dist/assets/pets/08-robo-bear.svg +48 -0
- package/dist/assets/pets/09-robo-penguin.svg +49 -0
- package/dist/assets/pets/10-robo-dino.svg +56 -0
- package/dist/assets/pets/11-slime-blob.svg +43 -0
- package/dist/assets/pets/12-ghost-byte.svg +41 -0
- package/dist/assets/pets/13-cactus-bot.svg +52 -0
- package/dist/assets/pets/14-crystal-bot.svg +32 -0
- package/dist/assets/pets/15-satellite-bot.svg +55 -0
- package/dist/assets/pets/16-jellyfish-bot.svg +47 -0
- package/dist/assets/pets/17-mushroom-bot.svg +47 -0
- package/dist/assets/pets/18-star-bot.svg +45 -0
- package/dist/assets/pets/manifest.json +25 -0
- package/dist/assets/signal-light.svg +55 -0
- package/dist/assets/tray-icon.png +0 -0
- package/dist/config.js +42 -0
- package/dist/main.js +158 -16
- package/dist/protocol.js +25 -0
- package/dist/renderer/index.html +4 -1
- package/dist/renderer/preload.js +32 -3
- package/dist/renderer/renderer.js +74 -10
- package/dist/renderer/style.css +114 -9
- package/dist/skins.js +55 -0
- package/package.json +2 -2
|
@@ -9,6 +9,7 @@ const ALL_STATE_CLASSES = [
|
|
|
9
9
|
'pet-done',
|
|
10
10
|
'pet-aborted',
|
|
11
11
|
'pet-error',
|
|
12
|
+
'pet-waiting-human',
|
|
12
13
|
];
|
|
13
14
|
/** PetState → CSS class 映射表(design.md 动画映射表)。 */
|
|
14
15
|
const STATE_CLASS = {
|
|
@@ -19,28 +20,91 @@ const STATE_CLASS = {
|
|
|
19
20
|
done: 'pet-done',
|
|
20
21
|
aborted: 'pet-aborted',
|
|
21
22
|
error: 'pet-error',
|
|
23
|
+
waiting_human: 'pet-waiting-human',
|
|
22
24
|
};
|
|
23
25
|
function applyState(container, state) {
|
|
24
26
|
container.classList.remove(...ALL_STATE_CLASSES);
|
|
25
27
|
container.classList.add(STATE_CLASS[state] ?? 'pet-idle');
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!container)
|
|
30
|
-
return;
|
|
31
|
-
applyState(container, 'idle');
|
|
32
|
-
// inline mascot.svg(而非 <img>/<object>)以保留其内部 id,供 style.css 的
|
|
33
|
-
// #pet-antenna / #pet-body-group / #pet-mouth / #pet-arm-left / #pet-arm-right 选择器生效
|
|
34
|
-
// (外部引用的 SVG 内容不可被外部 CSS 选中,必须 inline 进同一文档)。
|
|
29
|
+
/** 把 assets/ 下的 SVG 文本 inline 插入指定容器(保留内部 id,供 CSS 选择器跨状态切换样式)。 */
|
|
30
|
+
async function inlineSvgInto(container, assetPath) {
|
|
35
31
|
try {
|
|
36
32
|
// 构建后 assets/ 与 renderer/ 同级复制到 dist/(见 scripts/copy-static.mjs),故用 ../assets。
|
|
37
|
-
const res = await fetch(
|
|
33
|
+
const res = await fetch(assetPath);
|
|
38
34
|
const svgText = await res.text();
|
|
39
35
|
container.insertAdjacentHTML('afterbegin', svgText);
|
|
40
36
|
}
|
|
41
37
|
catch {
|
|
42
38
|
// 素材加载失败:容器保持空白,不影响状态机/IPC 正常工作(降级为无形象但仍可运行)
|
|
43
39
|
}
|
|
44
|
-
|
|
40
|
+
}
|
|
41
|
+
/** 切换皮肤:清空宠物容器后重新 inline 新素材(信号灯/状态 class 均不受影响,独立于宠物素材本身)。 */
|
|
42
|
+
async function swapSkin(petContainer, assetPath) {
|
|
43
|
+
petContainer.innerHTML = '';
|
|
44
|
+
await inlineSvgInto(petContainer, assetPath);
|
|
45
|
+
}
|
|
46
|
+
window.addEventListener('DOMContentLoaded', async () => {
|
|
47
|
+
// 状态 class 加在 #pet-stage 上(mascot 与信号灯共同的祖先容器),两者的动画/灯光规则
|
|
48
|
+
// 均以 `.pet-xxx #element-id` 描述,不要求元素是直接子节点。
|
|
49
|
+
const stage = document.getElementById('pet-stage');
|
|
50
|
+
const petContainer = document.getElementById('pet-container');
|
|
51
|
+
const lampContainer = document.getElementById('signal-light-container');
|
|
52
|
+
if (!stage || !petContainer || !lampContainer)
|
|
53
|
+
return;
|
|
54
|
+
applyState(stage, 'idle');
|
|
55
|
+
// 启动时的初始皮肤:主动向主进程 invoke 拉取(而非等主进程 send 推送)——
|
|
56
|
+
// 消除 did-finish-load 推送与本文件异步注册监听器之间的时序竞争(见 main.ts pushSkinToRenderer 注释)。
|
|
57
|
+
const initialAssetPath = await window.petBridge
|
|
58
|
+
.getInitialSkin()
|
|
59
|
+
.then((r) => r.assetPath)
|
|
60
|
+
.catch(() => '../assets/mascot.svg');
|
|
61
|
+
await Promise.all([
|
|
62
|
+
inlineSvgInto(petContainer, initialAssetPath),
|
|
63
|
+
inlineSvgInto(lampContainer, '../assets/signal-light.svg'),
|
|
64
|
+
]);
|
|
65
|
+
window.petBridge.onState((state) => applyState(stage, state));
|
|
66
|
+
window.petBridge.onSkin((assetPath) => swapSkin(petContainer, assetPath));
|
|
67
|
+
// 拖拽放置:窗口默认鼠标穿透(见 main.ts setIgnoreMouseEvents(true,{forward:true})),
|
|
68
|
+
// 鼠标悬停到宠物身上时取消穿透(可交互),离开后恢复穿透(不遮挡桌面下层点击)。这是 Electron
|
|
69
|
+
// 官方推荐的 hover 点击穿透模式
|
|
70
|
+
// (https://www.electronjs.org/docs/latest/tutorial/custom-window-interactions#forward-mouse-events-macos-windows)。
|
|
71
|
+
//
|
|
72
|
+
// 拖动本身不使用 -webkit-app-region:drag——该 CSS 属性在 Windows 上会导致其覆盖区域吞掉所有指针事件
|
|
73
|
+
// (见 https://www.electronjs.org/docs/latest/tutorial/custom-window-interactions 的说明:
|
|
74
|
+
// "draggable areas ignore all pointer events"),使 mouseenter/mouseleave 永远不会触发。
|
|
75
|
+
//
|
|
76
|
+
// 位移量必须是"相对拖拽起点的累计值",不能是"相对上一帧的增量"——原因见 main.ts dragStartBounds
|
|
77
|
+
// 注释:Windows 下 setBounds 存在 DPI 舍入误差,若主进程每次都读当前窗口尺寸再原样传回,
|
|
78
|
+
// 误差会不断累加导致窗口越拖越大。用累计位移 + 固定起始 bounds 可以彻底避免这个问题
|
|
79
|
+
// (每次都是"起点+累计位移",不依赖任何中间状态)。
|
|
80
|
+
let dragging = false;
|
|
81
|
+
let startX = 0;
|
|
82
|
+
let startY = 0;
|
|
83
|
+
stage.addEventListener('mouseenter', () => window.petBridge.setIgnoreMouseEvents(false));
|
|
84
|
+
stage.addEventListener('mouseleave', () => {
|
|
85
|
+
if (!dragging)
|
|
86
|
+
window.petBridge.setIgnoreMouseEvents(true);
|
|
87
|
+
});
|
|
88
|
+
stage.addEventListener('mousedown', (e) => {
|
|
89
|
+
dragging = true;
|
|
90
|
+
startX = e.screenX;
|
|
91
|
+
startY = e.screenY;
|
|
92
|
+
window.petBridge.dragStart();
|
|
93
|
+
});
|
|
94
|
+
window.addEventListener('mousemove', (e) => {
|
|
95
|
+
if (!dragging)
|
|
96
|
+
return;
|
|
97
|
+
window.petBridge.dragMove(e.screenX - startX, e.screenY - startY);
|
|
98
|
+
});
|
|
99
|
+
window.addEventListener('mouseup', () => {
|
|
100
|
+
// 只清 dragging 标志,不在这里恢复鼠标穿透——鼠标此时通常仍悬停在宠物上方(没有离开过 #pet-stage),
|
|
101
|
+
// 若这里强行 setIgnoreMouseEvents(true),会导致窗口提前变回穿透态,而 mouseenter 只在"进入"
|
|
102
|
+
// 时触发一次、不会因为穿透状态变化而重新触发,于是后续再也收不到 mousedown——表现为"只能拖动一次"。
|
|
103
|
+
// 穿透状态改由 mouseleave 统一负责:真正移出宠物范围时才恢复穿透。
|
|
104
|
+
if (!dragging)
|
|
105
|
+
return;
|
|
106
|
+
dragging = false;
|
|
107
|
+
window.petBridge.dragEnd();
|
|
108
|
+
});
|
|
45
109
|
});
|
|
46
110
|
export {};
|
package/dist/renderer/style.css
CHANGED
|
@@ -9,15 +9,31 @@ html, body {
|
|
|
9
9
|
height: 100%;
|
|
10
10
|
background: transparent;
|
|
11
11
|
overflow: hidden;
|
|
12
|
-
-webkit-app-region:
|
|
12
|
+
/* 拖拽放置:不使用 -webkit-app-region:drag(Windows 上带 app-region:drag 的区域会吞掉所有指针事件,
|
|
13
|
+
* 详见 https://www.electronjs.org/docs/latest/tutorial/custom-window-interactions ,
|
|
14
|
+
* 这会导致 #pet-stage 的 mouseenter/mouseleave 永远不触发,setIgnoreMouseEvents(false) 也就永远不会被调用,
|
|
15
|
+
* 窗口会一直停留在鼠标穿透状态——这正是"拖不动"的根因)。改为在 renderer.ts 里手动用
|
|
16
|
+
* mousedown/mousemove/mouseup 算位移量,经 IPC 通知主进程 setPosition 移动窗口。 */
|
|
17
|
+
user-select: none; /* 拖拽时避免误选中文本 */
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
#pet-
|
|
20
|
+
#pet-stage {
|
|
16
21
|
width: 100%;
|
|
17
22
|
height: 100%;
|
|
18
23
|
display: flex;
|
|
19
|
-
|
|
24
|
+
/* 底部对齐(而非居中):宠物脚部和信号灯杆底端需要落在同一条"地面线"上,
|
|
25
|
+
* 居中对齐会让二者按各自盒子的几何中心对齐,脚和杆底就对不上了。 */
|
|
26
|
+
align-items: flex-end;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#pet-container {
|
|
31
|
+
flex: 1 1 auto;
|
|
32
|
+
height: 100%;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: flex-end;
|
|
20
35
|
justify-content: center;
|
|
36
|
+
min-width: 0;
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
#pet-container svg {
|
|
@@ -25,6 +41,23 @@ html, body {
|
|
|
25
41
|
height: 90%;
|
|
26
42
|
}
|
|
27
43
|
|
|
44
|
+
/* 信号灯挂在宠物右侧,尺寸远小于主体,不抢视觉重心。 */
|
|
45
|
+
#signal-light-container {
|
|
46
|
+
flex: 0 0 auto;
|
|
47
|
+
height: 100%;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: flex-end;
|
|
50
|
+
justify-content: center;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#signal-light-container svg {
|
|
54
|
+
/* signal-light.svg viewBox 由 90x220 改为 90x456(灯杆加长到与宠物脚部同一地平线),
|
|
55
|
+
* 高度与 #pet-container svg 保持一致的 90% 比例,两者又都用 flex-end 贴底对齐,
|
|
56
|
+
* 这样杆的底端和宠物脚部的落地线基本重合。 */
|
|
57
|
+
width: 30px;
|
|
58
|
+
height: 90%;
|
|
59
|
+
}
|
|
60
|
+
|
|
28
61
|
/* ── idle:轻微上下浮动呼吸,机身静止 ── */
|
|
29
62
|
@keyframes pet-float {
|
|
30
63
|
0%, 100% { transform: translateY(0); }
|
|
@@ -112,20 +145,92 @@ html, body {
|
|
|
112
145
|
opacity: 0;
|
|
113
146
|
}
|
|
114
147
|
|
|
115
|
-
/* ── error:机身横向抖动 +
|
|
148
|
+
/* ── error:机身横向抖动 + 天线灯变红常亮(不闪——error 不要求用户立即处理,与 waiting_human 的
|
|
149
|
+
* 闪烁区分开:闪烁只留给"需要人工介入"这类必须响应的时刻) ── */
|
|
116
150
|
@keyframes pet-error-shake {
|
|
117
151
|
0%, 100% { transform: translateX(0); }
|
|
118
152
|
25% { transform: translateX(-6px); }
|
|
119
153
|
75% { transform: translateX(6px); }
|
|
120
154
|
}
|
|
121
|
-
@keyframes pet-error-blink-red {
|
|
122
|
-
0%, 100% { opacity: 1; }
|
|
123
|
-
50% { opacity: 0.3; }
|
|
124
|
-
}
|
|
125
155
|
.pet-error #pet-body-group {
|
|
126
156
|
animation: pet-error-shake 0.3s ease-in-out 1;
|
|
127
157
|
}
|
|
128
158
|
.pet-error #pet-antenna {
|
|
129
159
|
fill: #f87171;
|
|
130
|
-
animation:
|
|
160
|
+
animation: none;
|
|
161
|
+
opacity: 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ── waiting_human:等待人工介入(ask_human 面板 / plan 审批面板弹出期间)。
|
|
165
|
+
* 头部维持静止(面板本身已是强提示,机身不需要再抖动分散注意力),天线灯变红并快闪——
|
|
166
|
+
* 这是唯一"必须响应"的红灯状态,闪烁频率与 error 常亮形成对比。 ── */
|
|
167
|
+
@keyframes pet-waiting-human-blink-red {
|
|
168
|
+
0%, 100% { opacity: 1; }
|
|
169
|
+
50% { opacity: 0.25; }
|
|
170
|
+
}
|
|
171
|
+
.pet-waiting-human #pet-antenna {
|
|
172
|
+
fill: #f87171;
|
|
173
|
+
animation: pet-waiting-human-blink-red 0.5s ease-in-out infinite;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ── 信号灯状态映射表(方案C:三色+闪烁区分,呼应 design.md 动画映射表) ──
|
|
177
|
+
* 灯泡默认态(未点亮)由 assets/signal-light.svg 内置 fill-opacity=0.22 定义,
|
|
178
|
+
* 以下规则只在对应状态下覆盖 fill-opacity 把目标灯泡点亮,不影响另外两个灯泡。
|
|
179
|
+
*
|
|
180
|
+
* | PetState | 灯 | 行为 |
|
|
181
|
+
* |---------------|------|----------------------------------------|
|
|
182
|
+
* | idle | 绿 | 常亮(空闲/安全) |
|
|
183
|
+
* | thinking | 黄 | 慢速呼吸(等待 LLM 响应) |
|
|
184
|
+
* | speaking | 黄 | 常亮不闪(已在流式输出) |
|
|
185
|
+
* | tool_call | 黄 | 快闪(正在执行工具动作) |
|
|
186
|
+
* | done | 绿 | 短暂高亮闪一下后回落 |
|
|
187
|
+
* | aborted | 红 | 常亮不闪(用户主动中断) |
|
|
188
|
+
* | error | 红 | 常亮不闪(报错,与 waiting_human 区分) |
|
|
189
|
+
* | waiting_human | 红 | 快闪(等待人工介入,最高优先级) |
|
|
190
|
+
*/
|
|
191
|
+
|
|
192
|
+
@keyframes lamp-pulse-slow {
|
|
193
|
+
0%, 100% { fill-opacity: 0.9; }
|
|
194
|
+
50% { fill-opacity: 0.45; }
|
|
195
|
+
}
|
|
196
|
+
@keyframes lamp-blink-fast {
|
|
197
|
+
0%, 100% { fill-opacity: 0.95; }
|
|
198
|
+
50% { fill-opacity: 0.15; }
|
|
199
|
+
}
|
|
200
|
+
@keyframes lamp-flash-in {
|
|
201
|
+
0% { fill-opacity: 0.22; }
|
|
202
|
+
30% { fill-opacity: 1; }
|
|
203
|
+
100% { fill-opacity: 0.85; }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.pet-idle #pet-lamp-green {
|
|
207
|
+
fill-opacity: 0.85;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.pet-thinking #pet-lamp-yellow {
|
|
211
|
+
animation: lamp-pulse-slow 1.4s ease-in-out infinite;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.pet-speaking #pet-lamp-yellow {
|
|
215
|
+
fill-opacity: 0.95;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.pet-tool #pet-lamp-yellow {
|
|
219
|
+
animation: lamp-blink-fast 0.4s ease-in-out infinite;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.pet-done #pet-lamp-green {
|
|
223
|
+
animation: lamp-flash-in 0.4s ease-out 1;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.pet-aborted #pet-lamp-red {
|
|
227
|
+
fill-opacity: 0.85;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.pet-error #pet-lamp-red {
|
|
231
|
+
fill-opacity: 0.85;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.pet-waiting-human #pet-lamp-red {
|
|
235
|
+
animation: lamp-blink-fast 0.3s ease-in-out infinite;
|
|
131
236
|
}
|
package/dist/skins.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// 皮肤(选宠物)相关的静态读取逻辑:解析 assets/pets/manifest.json,提供 id → 文件路径的查找。
|
|
2
|
+
// 找不到/解析失败均返回空列表——选皮是可选增强,不能影响桌宠核心状态展示功能启动。
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
let cached = null;
|
|
8
|
+
/** assets/pets/ 目录的绝对路径。dist/skins.js 与 dist/assets/ 是同级(见 scripts/copy-static.mjs
|
|
9
|
+
* 把源码 assets/ 整体复制到 dist/assets/),故用 __dirname/assets 而非 __dirname/../assets。 */
|
|
10
|
+
export function skinsDir() {
|
|
11
|
+
return path.join(__dirname, 'assets', 'pets');
|
|
12
|
+
}
|
|
13
|
+
/** 读取 manifest.json 里的候选皮肤列表(带内存缓存,manifest 在运行期不会变化)。 */
|
|
14
|
+
export function listSkinEntries() {
|
|
15
|
+
if (cached)
|
|
16
|
+
return cached;
|
|
17
|
+
try {
|
|
18
|
+
const manifestPath = path.join(skinsDir(), 'manifest.json');
|
|
19
|
+
if (!existsSync(manifestPath)) {
|
|
20
|
+
cached = [];
|
|
21
|
+
return cached;
|
|
22
|
+
}
|
|
23
|
+
const raw = readFileSync(manifestPath, 'utf8');
|
|
24
|
+
const obj = JSON.parse(raw);
|
|
25
|
+
if (typeof obj !== 'object' || obj === null || !Array.isArray(obj.pets)) {
|
|
26
|
+
cached = [];
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
const pets = obj.pets;
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (const p of pets) {
|
|
32
|
+
if (!p || typeof p !== 'object')
|
|
33
|
+
continue;
|
|
34
|
+
const pp = p;
|
|
35
|
+
if (typeof pp.id === 'string' && typeof pp.file === 'string' && typeof pp.name === 'string') {
|
|
36
|
+
entries.push({ id: pp.id, file: pp.file, name: pp.name });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
cached = entries;
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
cached = [];
|
|
44
|
+
return cached;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** 给定 skinId 解析出对应 SVG 的绝对路径;'default'/未知 id/空字符串均返回 null(表示用默认 mascot.svg)。 */
|
|
48
|
+
export function resolveSkinPath(skinId) {
|
|
49
|
+
if (!skinId || skinId === 'default')
|
|
50
|
+
return null;
|
|
51
|
+
const entry = listSkinEntries().find((e) => e.id === skinId);
|
|
52
|
+
if (!entry)
|
|
53
|
+
return null;
|
|
54
|
+
return path.join(skinsDir(), entry.file);
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mocode-pet-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "mocode 桌宠:独立 Electron 悬浮窗,展示 agent 执行状态动画。作为 mocode-ai 的可选子包,不随主包安装强制拉取。",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "tsc -p tsconfig.json && node scripts/copy-static.mjs",
|
|
20
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.preload.json && node scripts/copy-static.mjs",
|
|
21
21
|
"start": "electron dist/main.js",
|
|
22
22
|
"typecheck": "tsc --noEmit"
|
|
23
23
|
},
|