@mingxy/opencode-mascot 0.2.8 → 0.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/opencode-mascot",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
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
  >
@@ -36,21 +36,14 @@ const DEFAULT_ANIM = {
36
36
 
37
37
  const WALK_PATH = [1, 2, 3, 4, 3, 2, 1, 0, -1, -2, -3, -2, -1, 0];
38
38
 
39
+ const FLASH_COLORS = ["#FF006E", "#FFBE0B", "#8338EC", "#3A86FF", "#FB5607", "#06FFA5", "#FF4081", "#00E5FF"];
40
+ const DRAG_MSGS = ["ᶠᵃⁿᵍ!..", "ᵏᵃⁱ~..", "ᵇᵘᶠᵃⁿᵍ~..", "ʷᵒ~..", "ⁿⁱᵘ~..", "ᵃᵃ~.."];
41
+
39
42
  function getFrameLines(pack: MascotPack, frameName: string): string[] {
40
43
  const frames = pack.frames as Record<string, string[] | undefined>;
41
44
  return frames[frameName] ?? frames["default"] ?? [];
42
45
  }
43
46
 
44
- function renderLines(lines: string[], fg?: string): JSX.Element {
45
- return (
46
- <box flexDirection="column">
47
- {lines.map((line: string) => (
48
- <text fg={fg}>{line}</text>
49
- ))}
50
- </box>
51
- );
52
- }
53
-
54
47
  export function createAnimatedRenderer(pack: MascotPack): {
55
48
  element: () => JSX.Element;
56
49
  setState: (s: MascotState) => void;
@@ -72,6 +65,19 @@ export function createAnimatedRenderer(pack: MascotPack): {
72
65
  const [walkEnabled, setWalkEnabled] = createSignal(anim.walkEnabled ?? true);
73
66
  const [dragging, setDraggingSignal] = createSignal(false);
74
67
  const [celebrate, setCelebrate] = createSignal<{ text: string; count: number } | null>(null);
68
+ const [flashColor, setFlashColor] = createSignal<string | null>(null);
69
+ const [dragMsg, setDragMsg] = createSignal<string | null>(null);
70
+
71
+ let flashTimer: ReturnType<typeof setInterval> | null = null;
72
+ let dragMsgTimer: ReturnType<typeof setInterval> | null = null;
73
+
74
+ const stopFlash = () => {
75
+ if (flashTimer) { clearInterval(flashTimer); flashTimer = null; }
76
+ };
77
+ const stopDragMsg = () => {
78
+ if (dragMsgTimer) { clearInterval(dragMsgTimer); dragMsgTimer = null; }
79
+ setDragMsg(null);
80
+ };
75
81
 
76
82
  let idleSleepTimeout: ReturnType<typeof setTimeout> | null = null;
77
83
 
@@ -230,6 +236,8 @@ export function createAnimatedRenderer(pack: MascotPack): {
230
236
  if (idleSleepTimeout) clearTimeout(idleSleepTimeout);
231
237
  if (walkInterval) clearInterval(walkInterval);
232
238
  for (const t of effectTimers) clearInterval(t);
239
+ stopFlash();
240
+ stopDragMsg();
233
241
  });
234
242
 
235
243
  // ─── Render ───
@@ -241,6 +249,8 @@ export function createAnimatedRenderer(pack: MascotPack): {
241
249
  currentState();
242
250
  dragging();
243
251
  celebrate();
252
+ flashColor();
253
+ dragMsg();
244
254
 
245
255
  for (const [, [get]] of extraSignals) {
246
256
  get();
@@ -276,11 +286,17 @@ export function createAnimatedRenderer(pack: MascotPack): {
276
286
  const top = jumpOffset();
277
287
  const left = offset > 0 ? offset : 0;
278
288
  const cel = celebrate();
289
+ const dm = dragMsg();
279
290
 
280
291
  return (
281
292
  <box flexDirection="column" left={left} top={top}>
282
- {cel ? <text fg={fg}>{cel.text}</text> : null}
283
- {renderLines(lines, fg)}
293
+ {cel ? <text fg={flashColor() ?? fg}>{cel.text}</text> : null}
294
+ {dm ? <text fg="#FF4081">{dm}</text> : null}
295
+ <box flexDirection="column">
296
+ {lines.map((line: string) => (
297
+ <text fg={flashColor() ?? fg}>{line}</text>
298
+ ))}
299
+ </box>
284
300
  </box>
285
301
  );
286
302
  };
@@ -297,6 +313,15 @@ export function createAnimatedRenderer(pack: MascotPack): {
297
313
  walkTimeout = scheduleNextWalk();
298
314
  jumpTimeout = scheduleNextJump();
299
315
  }
316
+
317
+ if (s === "thinking" || s === "busy") {
318
+ stopFlash();
319
+ flashTimer = setInterval(() => {
320
+ setFlashColor(FLASH_COLORS[Math.floor(Math.random() * FLASH_COLORS.length)]);
321
+ }, 120);
322
+ } else {
323
+ stopFlash();
324
+ }
300
325
  };
301
326
 
302
327
  const toggleWalk = () => {
@@ -312,13 +337,23 @@ export function createAnimatedRenderer(pack: MascotPack): {
312
337
  const setDragging = (v: boolean) => {
313
338
  setDraggingSignal(v);
314
339
  if (v) {
315
- // 睡着时被拖拽 → 惊醒到 idle,切回 default 帧后手臂 ┃███┃ 才能被扇手渲染匹配
316
340
  if (currentState() === "sleeping") {
317
341
  setState("idle");
318
342
  }
319
343
  setJumpOffset(-1);
344
+ stopDragMsg();
345
+ setDragMsg(DRAG_MSGS[Math.floor(Math.random() * DRAG_MSGS.length)]);
346
+ dragMsgTimer = setInterval(() => {
347
+ setDragMsg(DRAG_MSGS[Math.floor(Math.random() * DRAG_MSGS.length)]);
348
+ }, 800);
349
+ stopFlash();
350
+ flashTimer = setInterval(() => {
351
+ setFlashColor(FLASH_COLORS[Math.floor(Math.random() * FLASH_COLORS.length)]);
352
+ }, 100);
320
353
  } else {
321
354
  setJumpOffset(0);
355
+ stopDragMsg();
356
+ stopFlash();
322
357
  }
323
358
  };
324
359