@mingxy/opencode-mascot 0.2.6 → 0.2.8
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
|
@@ -4,7 +4,7 @@ import { createSignal } from "solid-js";
|
|
|
4
4
|
import type { JSX } from "@opentui/solid";
|
|
5
5
|
import type { MascotPack } from "../core/types";
|
|
6
6
|
import { createAnimatedRenderer } from "../core/ascii-renderer";
|
|
7
|
-
import { onCelebrate } from "../core/celebration-bus";
|
|
7
|
+
import { onCelebrate, onVersion } from "../core/celebration-bus";
|
|
8
8
|
|
|
9
9
|
interface HomeMascotProps {
|
|
10
10
|
mascots: Record<string, MascotPack>;
|
|
@@ -44,6 +44,10 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
|
|
|
44
44
|
renderers[currentName()].celebrateUpdate(newVersion);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
onVersion((version) => {
|
|
48
|
+
renderers[currentName()].showVersion(version);
|
|
49
|
+
});
|
|
50
|
+
|
|
47
51
|
return (
|
|
48
52
|
<box
|
|
49
53
|
left={posX()}
|
|
@@ -4,7 +4,7 @@ import { createSignal, onCleanup } from "solid-js";
|
|
|
4
4
|
import type { JSX } from "@opentui/solid";
|
|
5
5
|
import type { MascotPack, MascotState } from "../core/types";
|
|
6
6
|
import { createAnimatedRenderer } from "../core/ascii-renderer";
|
|
7
|
-
import { onCelebrate } from "../core/celebration-bus";
|
|
7
|
+
import { onCelebrate, onVersion } from "../core/celebration-bus";
|
|
8
8
|
|
|
9
9
|
interface SidebarMascotProps {
|
|
10
10
|
mascots: Record<string, MascotPack>;
|
|
@@ -28,13 +28,9 @@ const DEFAULT_STATE_MAP: Partial<Record<MascotState, string>> = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
const MASCOT_WIDTH = 10;
|
|
31
|
-
const MASCOT_HEIGHT = 5;
|
|
32
31
|
const PEEK = 2;
|
|
33
32
|
const PEEK_INTERVAL = 1200;
|
|
34
|
-
|
|
35
|
-
function termWidth(): number {
|
|
36
|
-
return (typeof process !== "undefined" && process.stdout?.columns) || 80;
|
|
37
|
-
}
|
|
33
|
+
const EDGE_THRESHOLD = 3;
|
|
38
34
|
|
|
39
35
|
export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
40
36
|
const names = Object.keys(props.mascots);
|
|
@@ -46,6 +42,7 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
46
42
|
const [currentName, setCurrentName] = createSignal(initialName);
|
|
47
43
|
const [posX, setPosX] = createSignal(20);
|
|
48
44
|
const [posY, setPosY] = createSignal(2);
|
|
45
|
+
const [containerWidth, setContainerWidth] = createSignal(0);
|
|
49
46
|
let dragStartX = 0;
|
|
50
47
|
let dragStartY = 0;
|
|
51
48
|
let dragAnchorX = 0;
|
|
@@ -76,17 +73,40 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
76
73
|
setCurrentName(names[(idx + 1) % names.length]);
|
|
77
74
|
};
|
|
78
75
|
|
|
76
|
+
const getCw = () => containerWidth() || 30;
|
|
77
|
+
|
|
78
|
+
const clampX = (rawX: number): number => {
|
|
79
|
+
const cw = getCw();
|
|
80
|
+
return Math.max(-(MASCOT_WIDTH - PEEK), Math.min(rawX, cw - PEEK));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const clampY = (rawY: number): number => {
|
|
84
|
+
return Math.max(0, rawY);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const checkEdge = () => {
|
|
88
|
+
const cw = getCw();
|
|
89
|
+
const x = posX();
|
|
90
|
+
if (x <= -(MASCOT_WIDTH - PEEK) + EDGE_THRESHOLD) {
|
|
91
|
+
hideSide = "left";
|
|
92
|
+
startPeek();
|
|
93
|
+
} else if (x >= cw - PEEK - EDGE_THRESHOLD) {
|
|
94
|
+
hideSide = "right";
|
|
95
|
+
startPeek();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
79
99
|
const startPeek = () => {
|
|
80
100
|
stopPeek();
|
|
81
101
|
let phase = false;
|
|
82
102
|
peekTimer = setInterval(() => {
|
|
83
103
|
phase = !phase;
|
|
84
104
|
const stretch = phase ? PEEK : 0;
|
|
85
|
-
const
|
|
105
|
+
const cw = getCw();
|
|
86
106
|
if (hideSide === "left") {
|
|
87
107
|
setPosX(-(MASCOT_WIDTH - PEEK) + stretch);
|
|
88
108
|
} else if (hideSide === "right") {
|
|
89
|
-
setPosX(
|
|
109
|
+
setPosX(cw - PEEK - stretch);
|
|
90
110
|
}
|
|
91
111
|
}, PEEK_INTERVAL);
|
|
92
112
|
};
|
|
@@ -94,13 +114,14 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
94
114
|
const returnToView = () => {
|
|
95
115
|
if (!hideSide) return;
|
|
96
116
|
stopPeek();
|
|
97
|
-
const
|
|
117
|
+
const cw = getCw();
|
|
98
118
|
const cur = posX();
|
|
99
|
-
const targetX = hideSide === "left" ? 0 : Math.max(0,
|
|
100
|
-
const step = targetX > cur ?
|
|
119
|
+
const targetX = hideSide === "left" ? 0 : Math.max(0, cw - MASCOT_WIDTH);
|
|
120
|
+
const step = targetX > cur ? 1 : -1;
|
|
121
|
+
|
|
101
122
|
returnTimer = setInterval(() => {
|
|
102
123
|
const now = posX();
|
|
103
|
-
if (Math.abs(now - targetX) <=
|
|
124
|
+
if (Math.abs(now - targetX) <= 1) {
|
|
104
125
|
setPosX(targetX);
|
|
105
126
|
stopReturn();
|
|
106
127
|
hideSide = null;
|
|
@@ -111,30 +132,6 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
111
132
|
}, 16);
|
|
112
133
|
};
|
|
113
134
|
|
|
114
|
-
const checkEdge = () => {
|
|
115
|
-
const w = termWidth();
|
|
116
|
-
const x = posX();
|
|
117
|
-
if (x <= -(MASCOT_WIDTH - PEEK) + 1) {
|
|
118
|
-
hideSide = "left";
|
|
119
|
-
startPeek();
|
|
120
|
-
} else if (x >= w - PEEK - 1) {
|
|
121
|
-
hideSide = "right";
|
|
122
|
-
startPeek();
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const clampX = (rawX: number): number => {
|
|
127
|
-
const w = termWidth();
|
|
128
|
-
const minX = -(MASCOT_WIDTH - PEEK);
|
|
129
|
-
const maxX = w - PEEK;
|
|
130
|
-
return Math.max(minX, Math.min(rawX, maxX));
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const clampY = (rawY: number): number => {
|
|
134
|
-
const h = (typeof process !== "undefined" && process.stdout?.rows) || 24;
|
|
135
|
-
return Math.max(0, Math.min(rawY, h - MASCOT_HEIGHT));
|
|
136
|
-
};
|
|
137
|
-
|
|
138
135
|
const setStateWithSwitch = (s: MascotState) => {
|
|
139
136
|
const cur = currentName();
|
|
140
137
|
renderers[cur].setState(s);
|
|
@@ -179,6 +176,10 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
179
176
|
renderers[currentName()].celebrateUpdate(newVersion);
|
|
180
177
|
});
|
|
181
178
|
|
|
179
|
+
onVersion((version) => {
|
|
180
|
+
renderers[currentName()].showVersion(version);
|
|
181
|
+
});
|
|
182
|
+
|
|
182
183
|
return (
|
|
183
184
|
<box
|
|
184
185
|
position="absolute"
|
|
@@ -187,6 +188,16 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
187
188
|
alignItems="center"
|
|
188
189
|
zIndex={100}
|
|
189
190
|
flexDirection="column"
|
|
191
|
+
ref={(node: any) => {
|
|
192
|
+
if (node) {
|
|
193
|
+
setContainerWidth(node.width || 0);
|
|
194
|
+
if (node.onSizeChange !== undefined) {
|
|
195
|
+
node.onSizeChange = () => {
|
|
196
|
+
setContainerWidth(node.width || 0);
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}}
|
|
190
201
|
onMouseDown={(e: any) => {
|
|
191
202
|
if (hideSide) { returnToView(); return; }
|
|
192
203
|
|
|
@@ -4,6 +4,15 @@ import { createSignal, onCleanup } from "solid-js";
|
|
|
4
4
|
import type { JSX } from "@opentui/solid";
|
|
5
5
|
import type { MascotPack, MascotState, EffectTimerCtx, EffectRenderCtx } from "./types";
|
|
6
6
|
|
|
7
|
+
const SUPERSCRIPT: Record<string, string> = {
|
|
8
|
+
"0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴",
|
|
9
|
+
"5": "⁵", "6": "⁶", "7": "⁷", "8": "⁸", "9": "⁹", ".": "·",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function toSuperscript(s: string): string {
|
|
13
|
+
return s.split("").map(c => SUPERSCRIPT[c] ?? c).join("");
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
const STATE_TO_FRAME: Record<MascotState, string> = {
|
|
8
17
|
idle: "default",
|
|
9
18
|
busy: "default",
|
|
@@ -49,6 +58,7 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
49
58
|
setDragging: (v: boolean) => void;
|
|
50
59
|
celebrateUpdate: (newVersion: string) => void;
|
|
51
60
|
bounce: () => void;
|
|
61
|
+
showVersion: (version: string) => void;
|
|
52
62
|
} {
|
|
53
63
|
const anim = { ...DEFAULT_ANIM, ...pack.animations };
|
|
54
64
|
const fg = pack.colors?.defaultFg || undefined;
|
|
@@ -345,5 +355,10 @@ export function createAnimatedRenderer(pack: MascotPack): {
|
|
|
345
355
|
setTimeout(() => setJumpOffset(0), 450);
|
|
346
356
|
};
|
|
347
357
|
|
|
348
|
-
|
|
358
|
+
const showVersion = (version: string) => {
|
|
359
|
+
setCelebrate({ text: `ᵛ${toSuperscript(version)}`, count: 0 });
|
|
360
|
+
setTimeout(() => setCelebrate(null), 3000);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
return { element, setState, toggleWalk, setDragging, celebrateUpdate, bounce, showVersion };
|
|
349
364
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const bus = new EventTarget();
|
|
2
2
|
|
|
3
3
|
const CELEBRATE_EVENT = "mascot:celebrate";
|
|
4
|
+
const VERSION_EVENT = "mascot:version";
|
|
4
5
|
|
|
5
6
|
export function emitCelebrate(newVersion: string): void {
|
|
6
7
|
bus.dispatchEvent(new CustomEvent(CELEBRATE_EVENT, { detail: { newVersion } }));
|
|
@@ -14,3 +15,16 @@ export function onCelebrate(handler: (newVersion: string) => void): () => void {
|
|
|
14
15
|
bus.addEventListener(CELEBRATE_EVENT, listener);
|
|
15
16
|
return () => bus.removeEventListener(CELEBRATE_EVENT, listener);
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
export function emitVersion(version: string): void {
|
|
20
|
+
bus.dispatchEvent(new CustomEvent(VERSION_EVENT, { detail: { version } }));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function onVersion(handler: (version: string) => void): () => void {
|
|
24
|
+
const listener = (e: Event) => {
|
|
25
|
+
const detail = (e as CustomEvent).detail as { version: string };
|
|
26
|
+
handler(detail.version);
|
|
27
|
+
};
|
|
28
|
+
bus.addEventListener(VERSION_EVENT, listener);
|
|
29
|
+
return () => bus.removeEventListener(VERSION_EVENT, listener);
|
|
30
|
+
}
|
package/tui.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { loadAllMascots } from "./src/core/mascot-loader"
|
|
|
7
7
|
import { SidebarMascot } from "./src/components/sidebar-mascot"
|
|
8
8
|
import { HomeMascot } from "./src/components/home-mascot"
|
|
9
9
|
import { checkAndUpdate } from "./src/core/updater"
|
|
10
|
-
import { emitCelebrate } from "./src/core/celebration-bus"
|
|
10
|
+
import { emitCelebrate, emitVersion } from "./src/core/celebration-bus"
|
|
11
11
|
|
|
12
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
13
|
const __dirname = dirname(__filename);
|
|
@@ -37,6 +37,8 @@ const tui: TuiPlugin = async (api, _options) => {
|
|
|
37
37
|
checkAndUpdate(pluginVersion, (newVersion) => {
|
|
38
38
|
emitCelebrate(newVersion);
|
|
39
39
|
}).catch(() => {});
|
|
40
|
+
|
|
41
|
+
setTimeout(() => emitVersion(pluginVersion), 1500);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const plugin: TuiPluginModule = {
|