arona-agent 1.2.3 → 1.2.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arona-agent",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Terminal AI Agent with desktop pet Arona — eye-tracking pupils, voice cloning, Computer Use, TTS/STT, MCP.",
5
5
  "keywords": [
6
6
  "voice-clone",
@@ -11,6 +11,8 @@
11
11
  // 摸头/dizzy 手势级互斥(interactionDone):同一次按住内任一触发过,另一项禁用直到松开。
12
12
  // 空闲注视:瞳孔跟随光标(spine_layer.js 内部实现),头部不歪。
13
13
  // 眨眼:Eye_Close_01 独立轨道(track5,预设之上),2~6s 均匀随机,15% 概率双眨;闭眼预设期间禁止。
14
+ // 悬停渐隐:光标在桌宠不透明像素停留 ≥2s 且未触发摸头/dizzy → canvas opacity 渐隐揭示背后内容,
15
+ // 移开渐入;会话内触发过摸头/dizzy 则移开前不再判断(状态机见下方悬停渐隐区块)。
14
16
  const spineCanvas = document.getElementById("spine");
15
17
 
16
18
  const IS_IDLE_DEBUG = new URLSearchParams(location.search).has("idledebug");
@@ -112,6 +114,10 @@ const HEAD_BOX = { xMin: 0.26, xMax: 0.78, yMin: 0.06, yMax: 0.29 };
112
114
  // 摸头期间"离开头部"判定缓冲(px):用户反馈 16px 太严苛,先放宽 50px、再要求更宽 → 90px;
113
115
  // 摸头时手在头部附近大幅游移不打断;离开很远(躯干以下/窗口外)才结束 enjoy
114
116
  const HEAD_EXIT_MARGIN = 90;
117
+ // ---- 悬停渐隐:光标在桌宠不透明像素停留 ≥2s 且未触发摸头/dizzy → 渐隐揭示背后内容,移开渐入 ----
118
+ const HOVER_PEEK_MS = 2000; // 悬停时长阈值
119
+ const PEEK_FADE_MS = 300; // 渐隐/渐入过渡时长
120
+ const HOVER_EVAL_MS = 500; // 悬停状态评估周期(光标轮询静止不重发事件,需周期评估防入口竞态)
115
121
 
116
122
  let dragging = false;
117
123
  let shaking = false; // 摸头态(摇动触发)
@@ -126,6 +132,68 @@ let dizzyTimer = null; // 松手后的延迟恢复定时器
126
132
  // 堵"摸头触发→离开头部区退出→继续大幅晃又触发 dizzy"(及反向)的串扰。
127
133
  let interactionDone = false;
128
134
 
135
+ // ---- 悬停渐隐状态机 ----
136
+ // onPet 判定复用动态穿透的 alpha 滞回(进入 ≥CLICK_ALPHA_ON / 退出 <CLICK_ALPHA_OFF);
137
+ // hoverOn 只在 500ms 周期评估(evalHover)里翻转——主进程光标轮询静止时(位移 <1px)不重发
138
+ // pet:cursor,若只在事件里评估,光标刚停进桌宠边缘的那次事件拿到的仍是旧帧 alpha,会漏启动计时。
139
+ let hoverOn = false; // 光标在桌宠不透明像素上
140
+ let peeked = false; // 渐隐揭示态(canvas opacity 0)
141
+ let peekBlocked = false; // 本次悬停会话内触发过摸头/dizzy → 移开前不再判断
142
+ let hoverTimer = null; // 2s 悬停计时
143
+ let lastCursor = null; // 主进程光标轮询的最后坐标(窗口本地 CSS px)
144
+
145
+ function isCursorOnPet() {
146
+ const c = lastCursor;
147
+ const cw = spineCanvas.clientWidth, ch = spineCanvas.clientHeight;
148
+ if (!c || c.x < 0 || c.y < 0 || c.x >= cw || c.y >= ch) return false;
149
+ const a = window.SpineLayer.getPickAlpha();
150
+ return hoverOn ? a >= CLICK_ALPHA_OFF : a >= CLICK_ALPHA_ON;
151
+ }
152
+
153
+ function setPeeked(on) {
154
+ if (on === peeked) return;
155
+ peeked = on;
156
+ spineCanvas.style.transition = `opacity ${PEEK_FADE_MS}ms ease`;
157
+ spineCanvas.style.opacity = on ? 0 : 1;
158
+ updateClickable(); // 渐隐期间强制穿透;渐入后按 alpha 重估
159
+ }
160
+
161
+ function clearHoverTimer() {
162
+ if (hoverTimer) {
163
+ clearTimeout(hoverTimer);
164
+ hoverTimer = null;
165
+ }
166
+ }
167
+
168
+ function scheduleHoverTimer() {
169
+ clearHoverTimer();
170
+ hoverTimer = setTimeout(checkHover, HOVER_PEEK_MS);
171
+ }
172
+
173
+ function checkHover() {
174
+ hoverTimer = null;
175
+ if (!hoverOn) return;
176
+ if (peekBlocked) return; // 会话内触发过摸头/dizzy → 移开前不再判断
177
+ if (dragging || shaking || dizzy) { // 手势进行中:推迟,结束后继续评估
178
+ hoverTimer = setTimeout(checkHover, HOVER_EVAL_MS);
179
+ return;
180
+ }
181
+ setPeeked(true);
182
+ }
183
+
184
+ function evalHover() {
185
+ const on = isCursorOnPet();
186
+ if (on === hoverOn) return;
187
+ hoverOn = on;
188
+ if (on) {
189
+ peekBlocked = false; // 重新进入:重置会话阻断
190
+ scheduleHoverTimer();
191
+ } else {
192
+ clearHoverTimer();
193
+ setPeeked(false); // 移开 → 渐入
194
+ }
195
+ }
196
+
129
197
  // ---- 动态鼠标穿透(窗口 480 宽,透明区放行点击到下层应用)----
130
198
  // 光标处 alpha(spine_layer 渲染循环内采样,经 onCursor 时机读取)滞回阈值防角色边缘抖动:
131
199
  // 进入可点 ≥24,退出 <10;拖动/摸头手势期间强制可点(松手瞬间的 mouseup 不能丢)。
@@ -134,7 +202,9 @@ const CLICK_ALPHA_OFF = 10;
134
202
  let clickable = false;
135
203
  function updateClickable() {
136
204
  let want;
137
- if (dragging || shaking) {
205
+ if (peeked) {
206
+ want = false; // 渐隐揭示期间点击穿透到背后内容
207
+ } else if (dragging || shaking) {
138
208
  want = true;
139
209
  } else {
140
210
  const a = window.SpineLayer.getPickAlpha ? window.SpineLayer.getPickAlpha() : 255;
@@ -155,6 +225,8 @@ function pushSample(x, y) {
155
225
 
156
226
  function enterPat() {
157
227
  interactionDone = true; // 手势级互斥:本手势内 dizzy 不再触发
228
+ if (hoverOn) peekBlocked = true; // 悬停会话内触发摸头 → 移开前不再渐隐
229
+ clearHoverTimer();
158
230
  window.SpineLayer.startPat();
159
231
  window.petAPI.shake();
160
232
  }
@@ -167,6 +239,8 @@ function exitPat() {
167
239
  // 窗口不锁死:dizzy 状态下仍走现有拖动路径(桌宠跟随鼠标被拎着晃),松手 dragEnd 落盘位置。
168
240
  function enterDizzy() {
169
241
  interactionDone = true; // 手势级互斥:本手势内摸头不再触发
242
+ if (hoverOn) peekBlocked = true; // 悬停会话内触发 dizzy → 移开前不再渐隐
243
+ clearHoverTimer();
170
244
  dizzy = true;
171
245
  window.petAPI.dizzy(); // 上报(与 pet:shake 对称,主进程仅日志)
172
246
  routeTo("dizzy"); // 幂等;闭眼预设(Arona 30 / Plana 13)自动禁眨眼/关注视
@@ -354,6 +428,7 @@ async function init() {
354
428
  // mousemove 断流(换向/位移凑不齐 → 摸头"没反应"),主进程 16ms 轮询不断流,
355
429
  // 轻微出窗的晃动也能采到 → 轻微划出窗口仍视为摸头手势的一部分。
356
430
  window.petAPI.onCursor((x, y, gx, gy) => {
431
+ lastCursor = { x, y };
357
432
  window.SpineLayer.setCursor(x, y);
358
433
  if (dragging) pushSample(gx, gy);
359
434
  updateClickable();
@@ -374,6 +449,7 @@ async function init() {
374
449
  document.addEventListener("mouseup", onMouseUp);
375
450
 
376
451
  scheduleBlink();
452
+ setInterval(evalHover, HOVER_EVAL_MS);
377
453
  }
378
454
 
379
455
  init();