@mingxy/opencode-mascot 0.2.9 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/opencode-mascot",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "OpenCode TUI mascot plugin framework - customizable ASCII mascots for your terminal",
5
5
  "author": "mingxy",
6
6
  "license": "MIT",
@@ -81,17 +81,18 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
81
81
  setPosX(dragAnchorX + (e.x - dragStartX));
82
82
  setPosY(dragAnchorY + (e.y - dragStartY));
83
83
  e.preventDefault();
84
- e.stopPropagation();
85
84
  props.api.renderer.clearSelection();
86
85
  }
87
86
  }}
88
87
  onMouseUp={() => {
89
- if (isDragging) {
90
- isDragging = false;
91
- renderers[currentName()].setDragging(false);
92
- }
88
+ isDragging = false;
89
+ renderers[currentName()].setDragging(false);
93
90
  }}
94
91
  onMouseDragEnd={() => {
92
+ isDragging = false;
93
+ renderers[currentName()].setDragging(false);
94
+ }}
95
+ onMouseOut={() => {
95
96
  if (isDragging) {
96
97
  isDragging = false;
97
98
  renderers[currentName()].setDragging(false);
@@ -226,22 +226,23 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
226
226
  setPosX(clampX(dragAnchorX + (e.x - dragStartX)));
227
227
  setPosY(clampY(dragAnchorY + (e.y - dragStartY)));
228
228
  e.preventDefault();
229
- e.stopPropagation();
230
229
  props.api.renderer.clearSelection();
231
230
  }
232
231
  }}
233
232
  onMouseUp={() => {
234
- if (isDragging) {
235
- isDragging = false;
236
- renderers[currentName()].setDragging(false);
237
- checkEdge();
238
- }
233
+ isDragging = false;
234
+ renderers[currentName()].setDragging(false);
235
+ checkEdge();
239
236
  }}
240
237
  onMouseDragEnd={() => {
238
+ isDragging = false;
239
+ renderers[currentName()].setDragging(false);
240
+ checkEdge();
241
+ }}
242
+ onMouseOut={() => {
241
243
  if (isDragging) {
242
244
  isDragging = false;
243
245
  renderers[currentName()].setDragging(false);
244
- checkEdge();
245
246
  }
246
247
  }}
247
248
  >
@@ -44,16 +44,6 @@ function getFrameLines(pack: MascotPack, frameName: string): string[] {
44
44
  return frames[frameName] ?? frames["default"] ?? [];
45
45
  }
46
46
 
47
- function renderLines(lines: string[], fg?: string): JSX.Element {
48
- return (
49
- <box flexDirection="column">
50
- {lines.map((line: string) => (
51
- <text fg={fg}>{line}</text>
52
- ))}
53
- </box>
54
- );
55
- }
56
-
57
47
  export function createAnimatedRenderer(pack: MascotPack): {
58
48
  element: () => JSX.Element;
59
49
  setState: (s: MascotState) => void;
@@ -80,6 +70,9 @@ export function createAnimatedRenderer(pack: MascotPack): {
80
70
 
81
71
  let flashTimer: ReturnType<typeof setInterval> | null = null;
82
72
  let dragMsgTimer: ReturnType<typeof setInterval> | null = null;
73
+ let bounceTimers: ReturnType<typeof setTimeout>[] = [];
74
+ let celebrateTimers: ReturnType<typeof setTimeout>[] = [];
75
+ let versionTimer: ReturnType<typeof setTimeout> | null = null;
83
76
 
84
77
  const stopFlash = () => {
85
78
  if (flashTimer) { clearInterval(flashTimer); flashTimer = null; }
@@ -88,6 +81,26 @@ export function createAnimatedRenderer(pack: MascotPack): {
88
81
  if (dragMsgTimer) { clearInterval(dragMsgTimer); dragMsgTimer = null; }
89
82
  setDragMsg(null);
90
83
  };
84
+ const stopBounce = () => {
85
+ bounceTimers.forEach(t => { clearTimeout(t); });
86
+ bounceTimers = [];
87
+ };
88
+ const stopCelebrate = () => {
89
+ celebrateTimers.forEach(t => { clearTimeout(t); });
90
+ celebrateTimers = [];
91
+ };
92
+ const stopVersion = () => {
93
+ if (versionTimer) { clearTimeout(versionTimer); versionTimer = null; }
94
+ setCelebrate(null);
95
+ };
96
+
97
+ const stopAllAnimations = () => {
98
+ stopFlash();
99
+ stopBounce();
100
+ stopCelebrate();
101
+ stopVersion();
102
+ setJumpOffset(0);
103
+ };
91
104
 
92
105
  let idleSleepTimeout: ReturnType<typeof setTimeout> | null = null;
93
106
 
@@ -248,6 +261,9 @@ export function createAnimatedRenderer(pack: MascotPack): {
248
261
  for (const t of effectTimers) clearInterval(t);
249
262
  stopFlash();
250
263
  stopDragMsg();
264
+ stopBounce();
265
+ stopCelebrate();
266
+ stopVersion();
251
267
  });
252
268
 
253
269
  // ─── Render ───
@@ -297,13 +313,16 @@ export function createAnimatedRenderer(pack: MascotPack): {
297
313
  const left = offset > 0 ? offset : 0;
298
314
  const cel = celebrate();
299
315
  const dm = dragMsg();
300
- const color = flashColor() ?? fg;
301
316
 
302
317
  return (
303
318
  <box flexDirection="column" left={left} top={top}>
304
- {cel ? <text fg={color}>{cel.text}</text> : null}
319
+ {cel ? <text fg={flashColor() ?? fg}>{cel.text}</text> : null}
305
320
  {dm ? <text fg="#FF4081">{dm}</text> : null}
306
- {renderLines(lines, color)}
321
+ <box flexDirection="column">
322
+ {lines.map((line: string) => (
323
+ <text fg={flashColor() ?? fg}>{line}</text>
324
+ ))}
325
+ </box>
307
326
  </box>
308
327
  );
309
328
  };
@@ -353,15 +372,21 @@ export function createAnimatedRenderer(pack: MascotPack): {
353
372
  dragMsgTimer = setInterval(() => {
354
373
  setDragMsg(DRAG_MSGS[Math.floor(Math.random() * DRAG_MSGS.length)]);
355
374
  }, 800);
375
+ stopFlash();
376
+ flashTimer = setInterval(() => {
377
+ setFlashColor(FLASH_COLORS[Math.floor(Math.random() * FLASH_COLORS.length)]);
378
+ }, 100);
356
379
  } else {
357
380
  setJumpOffset(0);
358
381
  stopDragMsg();
382
+ stopFlash();
359
383
  }
360
384
  };
361
385
 
362
386
  // 连续跳跃 + 吐火星文泡泡庆祝更新成功
363
387
  const celebrateUpdate = (newVersion: string) => {
364
388
  const bubbles = ["ᵘᵖ~", "ⁿᵉʷ!", "ʸᵉ~", "ᵍᵒ~", "ᵒᵏ~"];
389
+ stopAllAnimations();
365
390
  setState("happy");
366
391
  setFrameOverride("happy");
367
392
 
@@ -373,28 +398,32 @@ export function createAnimatedRenderer(pack: MascotPack): {
373
398
  setCelebrate(null);
374
399
  setFrameOverride(null);
375
400
  setState("idle");
401
+ celebrateTimers = [];
376
402
  return;
377
403
  }
378
404
  setJumpOffset(step % 2 === 0 ? -2 : 0);
379
405
  const word = bubbles[Math.floor(Math.random() * bubbles.length)];
380
406
  setCelebrate({ text: `${word} ᵘᵖ→ᵛ${newVersion}`, count: step });
381
407
  step++;
382
- setTimeout(tick, 600);
408
+ celebrateTimers.push(setTimeout(tick, 600));
383
409
  };
384
410
  tick();
385
411
  };
386
412
 
387
413
  const bounce = () => {
414
+ stopBounce();
415
+ stopCelebrate();
388
416
  if (currentState() === "sleeping") setState("idle");
389
417
  setJumpOffset(-3);
390
- setTimeout(() => setJumpOffset(-2), 150);
391
- setTimeout(() => setJumpOffset(-1), 300);
392
- setTimeout(() => setJumpOffset(0), 450);
418
+ bounceTimers.push(setTimeout(() => setJumpOffset(-2), 150));
419
+ bounceTimers.push(setTimeout(() => setJumpOffset(-1), 300));
420
+ bounceTimers.push(setTimeout(() => { setJumpOffset(0); bounceTimers = []; }, 450));
393
421
  };
394
422
 
395
423
  const showVersion = (version: string) => {
424
+ stopVersion();
396
425
  setCelebrate({ text: `ᵛ${toSuperscript(version)}`, count: 0 });
397
- setTimeout(() => setCelebrate(null), 3000);
426
+ versionTimer = setTimeout(() => { setCelebrate(null); versionTimer = null; }, 3000);
398
427
  };
399
428
 
400
429
  return { element, setState, toggleWalk, setDragging, celebrateUpdate, bounce, showVersion };