bongocat-react 1.0.0 → 1.2.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/README.md +54 -2
- package/assets/bongocat.gif +0 -0
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +71 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
A React component that adds an animated BongoCat overlay to your app. The cat reacts to keyboard typing and mouse clicks in real-time.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="assets/bongocat.gif" alt="BongoCat demo" width="300" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
Built using [BongoCat-mac](https://github.com/Gamma-Software/BongoCat-mac) as reference (MIT licensed).
|
|
6
10
|
|
|
7
11
|
## Install
|
|
8
12
|
|
|
@@ -45,9 +49,57 @@ function App() {
|
|
|
45
49
|
| `zIndex` | `number` | `9998` | z-index of the overlay |
|
|
46
50
|
| `pulse` | `boolean` | `true` | Scale pulse animation on input |
|
|
47
51
|
| `spriteMarginTop` | `string \| number` | `"37%"` | Margin-top on sprites to ground the cat visually |
|
|
52
|
+
| `clickTooltip` | `boolean` | `true` | Show a fun tooltip when the cat is clicked |
|
|
53
|
+
| `messages` | `string[]` | Built-in cat messages | Custom tooltip messages |
|
|
54
|
+
| `messageDuration` | `number` | `2000` | How long the tooltip stays visible (ms) |
|
|
48
55
|
| `className` | `string` | `""` | Additional CSS class |
|
|
49
56
|
| `style` | `CSSProperties` | — | Additional inline styles |
|
|
50
57
|
|
|
58
|
+
## Click Tooltip
|
|
59
|
+
|
|
60
|
+
Click on the cat and it'll say something fun! Enabled by default with 13 built-in messages. Customize with your own:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
{/* Use built-in messages (default) */}
|
|
64
|
+
<BongoCat />
|
|
65
|
+
|
|
66
|
+
{/* Custom messages */}
|
|
67
|
+
<BongoCat messages={["hire me!", "star this repo ⭐", "todo: take over world"]} />
|
|
68
|
+
|
|
69
|
+
{/* Disable tooltip */}
|
|
70
|
+
<BongoCat clickTooltip={false} />
|
|
71
|
+
|
|
72
|
+
{/* Longer display */}
|
|
73
|
+
<BongoCat messageDuration={5000} />
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Positioning
|
|
77
|
+
|
|
78
|
+
By default, the cat sits in the **bottom-right corner**. Use `bottom`, `right`, and `style` to place it wherever you want:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
{/* Bottom-right (default) */}
|
|
82
|
+
<BongoCat />
|
|
83
|
+
|
|
84
|
+
{/* Bottom-left */}
|
|
85
|
+
<BongoCat right="auto" style={{ left: 16 }} />
|
|
86
|
+
|
|
87
|
+
{/* Top-right */}
|
|
88
|
+
<BongoCat bottom="auto" style={{ top: 16 }} />
|
|
89
|
+
|
|
90
|
+
{/* Top-left */}
|
|
91
|
+
<BongoCat bottom="auto" right="auto" style={{ top: 16, left: 16 }} />
|
|
92
|
+
|
|
93
|
+
{/* Centered at bottom */}
|
|
94
|
+
<BongoCat right="auto" style={{ left: "50%", transform: "translateX(-50%)" }} />
|
|
95
|
+
|
|
96
|
+
{/* Custom offset */}
|
|
97
|
+
<BongoCat bottom={40} right={40} />
|
|
98
|
+
|
|
99
|
+
{/* Bigger cat */}
|
|
100
|
+
<BongoCat width={130} height={80} />
|
|
101
|
+
```
|
|
102
|
+
|
|
51
103
|
## Privacy
|
|
52
104
|
|
|
53
105
|
This package does not track, collect, or transmit any data. No analytics, no telemetry, no cookies. It's intentionally kept simple — just a lil kitty reacting to your keystrokes and clicks, entirely client-side.
|
|
@@ -57,7 +109,7 @@ This package does not track, collect, or transmit any data. No analytics, no tel
|
|
|
57
109
|
- Listens to `keydown`/`keyup` on `document` and `mousedown`/`mouseup` on `window` (capture phase)
|
|
58
110
|
- Maps physical key positions (`event.code`) to left/right paw — left-half keyboard keys move the left paw, right-half keys move the right paw
|
|
59
111
|
- Left mouse click → left paw, right click → right paw
|
|
60
|
-
-
|
|
112
|
+
- Click on the cat → random fun tooltip message
|
|
61
113
|
- Handles context menu stealing mouseup events
|
|
62
114
|
|
|
63
115
|
## Credits
|
|
Binary file
|
package/dist/index.d.mts
CHANGED
|
@@ -18,11 +18,17 @@ interface BongoCatProps {
|
|
|
18
18
|
pulse?: boolean;
|
|
19
19
|
/** Margin-top applied to each sprite image to ground the cat visually. Defaults to "37%" */
|
|
20
20
|
spriteMarginTop?: string | number;
|
|
21
|
+
/** Show a fun tooltip when the cat is clicked. Defaults to true */
|
|
22
|
+
clickTooltip?: boolean;
|
|
23
|
+
/** Custom tooltip messages. Uses built-in cat messages if not provided */
|
|
24
|
+
messages?: string[];
|
|
25
|
+
/** How long the tooltip stays visible in ms. Defaults to 2000 */
|
|
26
|
+
messageDuration?: number;
|
|
21
27
|
/** Additional className on the container */
|
|
22
28
|
className?: string;
|
|
23
29
|
/** Additional inline styles on the container */
|
|
24
30
|
style?: CSSProperties;
|
|
25
31
|
}
|
|
26
|
-
declare function BongoCat({ assetsPath, bottom, right, width, height, zIndex, pulse: pulseEnabled, spriteMarginTop, className, style: userStyle, }?: BongoCatProps): react_jsx_runtime.JSX.Element;
|
|
32
|
+
declare function BongoCat({ assetsPath, bottom, right, width, height, zIndex, pulse: pulseEnabled, spriteMarginTop, clickTooltip, messages, messageDuration, className, style: userStyle, }?: BongoCatProps): react_jsx_runtime.JSX.Element;
|
|
27
33
|
|
|
28
34
|
export { BongoCat, type BongoCatProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -18,11 +18,17 @@ interface BongoCatProps {
|
|
|
18
18
|
pulse?: boolean;
|
|
19
19
|
/** Margin-top applied to each sprite image to ground the cat visually. Defaults to "37%" */
|
|
20
20
|
spriteMarginTop?: string | number;
|
|
21
|
+
/** Show a fun tooltip when the cat is clicked. Defaults to true */
|
|
22
|
+
clickTooltip?: boolean;
|
|
23
|
+
/** Custom tooltip messages. Uses built-in cat messages if not provided */
|
|
24
|
+
messages?: string[];
|
|
25
|
+
/** How long the tooltip stays visible in ms. Defaults to 2000 */
|
|
26
|
+
messageDuration?: number;
|
|
21
27
|
/** Additional className on the container */
|
|
22
28
|
className?: string;
|
|
23
29
|
/** Additional inline styles on the container */
|
|
24
30
|
style?: CSSProperties;
|
|
25
31
|
}
|
|
26
|
-
declare function BongoCat({ assetsPath, bottom, right, width, height, zIndex, pulse: pulseEnabled, spriteMarginTop, className, style: userStyle, }?: BongoCatProps): react_jsx_runtime.JSX.Element;
|
|
32
|
+
declare function BongoCat({ assetsPath, bottom, right, width, height, zIndex, pulse: pulseEnabled, spriteMarginTop, clickTooltip, messages, messageDuration, className, style: userStyle, }?: BongoCatProps): react_jsx_runtime.JSX.Element;
|
|
27
33
|
|
|
28
34
|
export { BongoCat, type BongoCatProps };
|
package/dist/index.js
CHANGED
|
@@ -97,6 +97,35 @@ var RIGHT_KEYS = /* @__PURE__ */ new Set([
|
|
|
97
97
|
"ArrowRight"
|
|
98
98
|
]);
|
|
99
99
|
var MIN_ANIMATION_MS = 100;
|
|
100
|
+
var DEFAULT_MESSAGES = [
|
|
101
|
+
"meow~ keep typing! \u{1F431}",
|
|
102
|
+
"purrr... nice clicks!",
|
|
103
|
+
"i'm helping! \u{1F43E}",
|
|
104
|
+
"*bonk bonk bonk*",
|
|
105
|
+
"nyaa~ don't mind me~",
|
|
106
|
+
"cats make everything better \u2728",
|
|
107
|
+
"10/10 typing form \u{1F44F}",
|
|
108
|
+
"*purring intensifies*",
|
|
109
|
+
"you're doing great, hooman!",
|
|
110
|
+
"boop! \u{1F43E}",
|
|
111
|
+
"feed me... with keystrokes",
|
|
112
|
+
"i sit on keyboard now",
|
|
113
|
+
"you type, i bonk \u{1F3B9}"
|
|
114
|
+
];
|
|
115
|
+
var tooltipStyle = {
|
|
116
|
+
position: "absolute",
|
|
117
|
+
top: -36,
|
|
118
|
+
right: 0,
|
|
119
|
+
whiteSpace: "nowrap",
|
|
120
|
+
borderRadius: 8,
|
|
121
|
+
backgroundColor: "#18181b",
|
|
122
|
+
color: "#fafafa",
|
|
123
|
+
padding: "6px 12px",
|
|
124
|
+
fontSize: 12,
|
|
125
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
126
|
+
animation: "bongocat-fade-in 0.15s ease-out",
|
|
127
|
+
pointerEvents: "none"
|
|
128
|
+
};
|
|
100
129
|
var baseImgStyle = {
|
|
101
130
|
position: "absolute",
|
|
102
131
|
inset: 0,
|
|
@@ -114,13 +143,18 @@ function BongoCat({
|
|
|
114
143
|
zIndex = 9998,
|
|
115
144
|
pulse: pulseEnabled = true,
|
|
116
145
|
spriteMarginTop = "37%",
|
|
146
|
+
clickTooltip = true,
|
|
147
|
+
messages = DEFAULT_MESSAGES,
|
|
148
|
+
messageDuration = 2e3,
|
|
117
149
|
className = "",
|
|
118
150
|
style: userStyle
|
|
119
151
|
} = {}) {
|
|
120
152
|
const [state, setState] = (0, import_react.useState)("idle");
|
|
121
153
|
const [pulse, setPulse] = (0, import_react.useState)(false);
|
|
154
|
+
const [tooltipMsg, setTooltipMsg] = (0, import_react.useState)(null);
|
|
122
155
|
const pawDownTimeRef = (0, import_react.useRef)(0);
|
|
123
156
|
const idleTimerRef = (0, import_react.useRef)(void 0);
|
|
157
|
+
const tooltipTimerRef = (0, import_react.useRef)(void 0);
|
|
124
158
|
const basePath = assetsPath.replace(/\/$/, "");
|
|
125
159
|
const imgStyle = {
|
|
126
160
|
...baseImgStyle,
|
|
@@ -184,8 +218,15 @@ function BongoCat({
|
|
|
184
218
|
window.removeEventListener("mouseup", onMouseUp, true);
|
|
185
219
|
window.removeEventListener("contextmenu", onContextMenu, true);
|
|
186
220
|
clearTimeout(idleTimerRef.current);
|
|
221
|
+
clearTimeout(tooltipTimerRef.current);
|
|
187
222
|
};
|
|
188
223
|
}, [returnToIdle, triggerPulse]);
|
|
224
|
+
const handleCatClick = (0, import_react.useCallback)(() => {
|
|
225
|
+
if (!clickTooltip || messages.length === 0) return;
|
|
226
|
+
clearTimeout(tooltipTimerRef.current);
|
|
227
|
+
setTooltipMsg(messages[Math.floor(Math.random() * messages.length)]);
|
|
228
|
+
tooltipTimerRef.current = setTimeout(() => setTooltipMsg(null), messageDuration);
|
|
229
|
+
}, [clickTooltip, messages, messageDuration]);
|
|
189
230
|
const leftDown = state === "leftPawDown" || state === "bothPawsDown";
|
|
190
231
|
const rightDown = state === "rightPawDown" || state === "bothPawsDown";
|
|
191
232
|
const containerStyle = {
|
|
@@ -195,30 +236,42 @@ function BongoCat({
|
|
|
195
236
|
width,
|
|
196
237
|
height,
|
|
197
238
|
zIndex,
|
|
198
|
-
|
|
239
|
+
cursor: clickTooltip ? "pointer" : void 0,
|
|
199
240
|
userSelect: "none",
|
|
200
241
|
transform: pulse ? "scale(1.08)" : "scale(1)",
|
|
201
242
|
transition: "transform 0.1s ease-out",
|
|
202
243
|
...userStyle
|
|
203
244
|
};
|
|
204
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
205
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
206
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
207
|
-
"
|
|
208
|
-
{
|
|
209
|
-
src: leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`,
|
|
210
|
-
alt: "",
|
|
211
|
-
draggable: false,
|
|
212
|
-
style: imgStyle
|
|
213
|
-
}
|
|
214
|
-
),
|
|
215
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
216
|
-
"img",
|
|
245
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes bongocat-fade-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}` }),
|
|
247
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
248
|
+
"div",
|
|
217
249
|
{
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
className,
|
|
251
|
+
style: containerStyle,
|
|
252
|
+
onClick: handleCatClick,
|
|
253
|
+
children: [
|
|
254
|
+
tooltipMsg && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: tooltipStyle, children: tooltipMsg }),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: `${basePath}/base.png`, alt: "", draggable: false, style: imgStyle }),
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
257
|
+
"img",
|
|
258
|
+
{
|
|
259
|
+
src: leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`,
|
|
260
|
+
alt: "",
|
|
261
|
+
draggable: false,
|
|
262
|
+
style: imgStyle
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
266
|
+
"img",
|
|
267
|
+
{
|
|
268
|
+
src: rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`,
|
|
269
|
+
alt: "",
|
|
270
|
+
draggable: false,
|
|
271
|
+
style: imgStyle
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
]
|
|
222
275
|
}
|
|
223
276
|
)
|
|
224
277
|
] });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/bongo-cat.tsx"],"sourcesContent":["export { BongoCat } from \"./bongo-cat\";\nexport type { BongoCatProps } from \"./bongo-cat\";\n","import { useState, useEffect, useRef, useCallback, CSSProperties } from \"react\";\n\ntype CatState = \"idle\" | \"leftPawDown\" | \"rightPawDown\" | \"bothPawsDown\";\n\nexport interface BongoCatProps {\n /** Base URL path to the sprite assets directory (must contain base.png, left-up.png, left-down.png, right-up.png, right-down.png). Defaults to \"/bongo-cat\" */\n assetsPath?: string;\n /** CSS bottom offset. Defaults to 16 */\n bottom?: number | string;\n /** CSS right offset. Defaults to 16 */\n right?: number | string;\n /** Width in px. Defaults to 65 */\n width?: number;\n /** Height in px. Defaults to 40 */\n height?: number;\n /** z-index. Defaults to 9998 */\n zIndex?: number;\n /** Enable scale pulse on input. Defaults to true */\n pulse?: boolean;\n /** Margin-top applied to each sprite image to ground the cat visually. Defaults to \"37%\" */\n spriteMarginTop?: string | number;\n /** Additional className on the container */\n className?: string;\n /** Additional inline styles on the container */\n style?: CSSProperties;\n}\n\n// Physical key codes for the left side of the keyboard\nconst LEFT_KEYS = new Set([\n \"Digit1\", \"Digit2\", \"Digit3\", \"Digit4\", \"Digit5\",\n \"KeyQ\", \"KeyW\", \"KeyE\", \"KeyR\", \"KeyT\",\n \"KeyA\", \"KeyS\", \"KeyD\", \"KeyF\", \"KeyG\",\n \"KeyZ\", \"KeyX\", \"KeyC\", \"KeyV\", \"KeyB\",\n \"Tab\", \"CapsLock\", \"ShiftLeft\", \"ControlLeft\", \"AltLeft\", \"MetaLeft\",\n \"Backquote\", \"Escape\",\n]);\n\nconst RIGHT_KEYS = new Set([\n \"Digit6\", \"Digit7\", \"Digit8\", \"Digit9\", \"Digit0\",\n \"KeyY\", \"KeyU\", \"KeyI\", \"KeyO\", \"KeyP\",\n \"KeyH\", \"KeyJ\", \"KeyK\", \"KeyL\",\n \"KeyN\", \"KeyM\",\n \"Enter\", \"Backspace\", \"Delete\", \"ShiftRight\", \"ControlRight\", \"AltRight\", \"MetaRight\",\n \"BracketLeft\", \"BracketRight\", \"Backslash\", \"Semicolon\", \"Quote\",\n \"Comma\", \"Period\", \"Slash\", \"Minus\", \"Equal\",\n \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\",\n]);\n\nconst MIN_ANIMATION_MS = 100;\n\nconst baseImgStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n pointerEvents: \"none\",\n};\n\nexport function BongoCat({\n assetsPath = \"/bongo-cat\",\n bottom = 16,\n right = 16,\n width = 65,\n height = 40,\n zIndex = 9998,\n pulse: pulseEnabled = true,\n spriteMarginTop = \"37%\",\n className = \"\",\n style: userStyle,\n}: BongoCatProps = {}) {\n const [state, setState] = useState<CatState>(\"idle\");\n const [pulse, setPulse] = useState(false);\n const pawDownTimeRef = useRef(0);\n const idleTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n const basePath = assetsPath.replace(/\\/$/, \"\");\n\n const imgStyle: CSSProperties = {\n ...baseImgStyle,\n marginTop: typeof spriteMarginTop === \"number\" ? `${spriteMarginTop}px` : spriteMarginTop,\n };\n\n const returnToIdle = useCallback(() => {\n const elapsed = Date.now() - pawDownTimeRef.current;\n const remaining = Math.max(0, MIN_ANIMATION_MS - elapsed);\n\n clearTimeout(idleTimerRef.current);\n idleTimerRef.current = setTimeout(() => setState(\"idle\"), remaining);\n }, []);\n\n const triggerPulse = useCallback(() => {\n if (!pulseEnabled) return;\n setPulse(true);\n requestAnimationFrame(() => {\n setTimeout(() => setPulse(false), 150);\n });\n }, [pulseEnabled]);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.repeat) return;\n\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (LEFT_KEYS.has(e.code)) {\n setState(\"leftPawDown\");\n } else if (RIGHT_KEYS.has(e.code)) {\n setState(\"rightPawDown\");\n } else {\n setState(e.code.charCodeAt(0) % 2 === 0 ? \"leftPawDown\" : \"rightPawDown\");\n }\n };\n\n const onKeyUp = () => {\n returnToIdle();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (e.button === 2) {\n setState(\"rightPawDown\");\n } else {\n setState(\"leftPawDown\");\n }\n };\n\n const onMouseUp = () => {\n returnToIdle();\n };\n\n const onContextMenu = () => {\n returnToIdle();\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"mousedown\", onMouseDown, true);\n window.addEventListener(\"mouseup\", onMouseUp, true);\n window.addEventListener(\"contextmenu\", onContextMenu, true);\n\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"mousedown\", onMouseDown, true);\n window.removeEventListener(\"mouseup\", onMouseUp, true);\n window.removeEventListener(\"contextmenu\", onContextMenu, true);\n clearTimeout(idleTimerRef.current);\n };\n }, [returnToIdle, triggerPulse]);\n\n const leftDown = state === \"leftPawDown\" || state === \"bothPawsDown\";\n const rightDown = state === \"rightPawDown\" || state === \"bothPawsDown\";\n\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: typeof bottom === \"number\" ? `${bottom}px` : bottom,\n right: typeof right === \"number\" ? `${right}px` : right,\n width,\n height,\n zIndex,\n pointerEvents: \"none\",\n userSelect: \"none\",\n transform: pulse ? \"scale(1.08)\" : \"scale(1)\",\n transition: \"transform 0.1s ease-out\",\n ...userStyle,\n };\n\n return (\n <div className={className} style={containerStyle}>\n <img src={`${basePath}/base.png`} alt=\"\" draggable={false} style={imgStyle} />\n <img\n src={leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n <img\n src={rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAwE;AA8KpE;AAlJJ,IAAM,YAAY,oBAAI,IAAI;AAAA,EACxB;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EACxC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAChC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAChC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAChC;AAAA,EAAO;AAAA,EAAY;AAAA,EAAa;AAAA,EAAe;AAAA,EAAW;AAAA,EAC1D;AAAA,EAAa;AACf,CAAC;AAED,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EACxC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAChC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxB;AAAA,EAAQ;AAAA,EACR;AAAA,EAAS;AAAA,EAAa;AAAA,EAAU;AAAA,EAAc;AAAA,EAAgB;AAAA,EAAY;AAAA,EAC1E;AAAA,EAAe;AAAA,EAAgB;AAAA,EAAa;AAAA,EAAa;AAAA,EACzD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EACrC;AAAA,EAAW;AAAA,EAAa;AAAA,EAAa;AACvC,CAAC;AAED,IAAM,mBAAmB;AAEzB,IAAM,eAA8B;AAAA,EAClC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,eAAe;AACjB;AAEO,SAAS,SAAS;AAAA,EACvB,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO,eAAe;AAAA,EACtB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,OAAO;AACT,IAAmB,CAAC,GAAG;AACrB,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAmB,MAAM;AACnD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,KAAK;AACxC,QAAM,qBAAiB,qBAAO,CAAC;AAC/B,QAAM,mBAAe,qBAAsC,MAAS;AAEpE,QAAM,WAAW,WAAW,QAAQ,OAAO,EAAE;AAE7C,QAAM,WAA0B;AAAA,IAC9B,GAAG;AAAA,IACH,WAAW,OAAO,oBAAoB,WAAW,GAAG,eAAe,OAAO;AAAA,EAC5E;AAEA,QAAM,mBAAe,0BAAY,MAAM;AACrC,UAAM,UAAU,KAAK,IAAI,IAAI,eAAe;AAC5C,UAAM,YAAY,KAAK,IAAI,GAAG,mBAAmB,OAAO;AAExD,iBAAa,aAAa,OAAO;AACjC,iBAAa,UAAU,WAAW,MAAM,SAAS,MAAM,GAAG,SAAS;AAAA,EACrE,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAe,0BAAY,MAAM;AACrC,QAAI,CAAC,aAAc;AACnB,aAAS,IAAI;AACb,0BAAsB,MAAM;AAC1B,iBAAW,MAAM,SAAS,KAAK,GAAG,GAAG;AAAA,IACvC,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,CAAC;AAEjB,8BAAU,MAAM;AACd,UAAM,YAAY,CAAC,MAAqB;AACtC,UAAI,EAAE,OAAQ;AAEd,qBAAe,UAAU,KAAK,IAAI;AAClC,mBAAa,aAAa,OAAO;AACjC,mBAAa;AAEb,UAAI,UAAU,IAAI,EAAE,IAAI,GAAG;AACzB,iBAAS,aAAa;AAAA,MACxB,WAAW,WAAW,IAAI,EAAE,IAAI,GAAG;AACjC,iBAAS,cAAc;AAAA,MACzB,OAAO;AACL,iBAAS,EAAE,KAAK,WAAW,CAAC,IAAI,MAAM,IAAI,gBAAgB,cAAc;AAAA,MAC1E;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,mBAAa;AAAA,IACf;AAEA,UAAM,cAAc,CAAC,MAAkB;AACrC,qBAAe,UAAU,KAAK,IAAI;AAClC,mBAAa,aAAa,OAAO;AACjC,mBAAa;AAEb,UAAI,EAAE,WAAW,GAAG;AAClB,iBAAS,cAAc;AAAA,MACzB,OAAO;AACL,iBAAS,aAAa;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AACtB,mBAAa;AAAA,IACf;AAEA,UAAM,gBAAgB,MAAM;AAC1B,mBAAa;AAAA,IACf;AAEA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,aAAS,iBAAiB,SAAS,OAAO;AAC1C,WAAO,iBAAiB,aAAa,aAAa,IAAI;AACtD,WAAO,iBAAiB,WAAW,WAAW,IAAI;AAClD,WAAO,iBAAiB,eAAe,eAAe,IAAI;AAE1D,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,SAAS;AACjD,eAAS,oBAAoB,SAAS,OAAO;AAC7C,aAAO,oBAAoB,aAAa,aAAa,IAAI;AACzD,aAAO,oBAAoB,WAAW,WAAW,IAAI;AACrD,aAAO,oBAAoB,eAAe,eAAe,IAAI;AAC7D,mBAAa,aAAa,OAAO;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,cAAc,YAAY,CAAC;AAE/B,QAAM,WAAW,UAAU,iBAAiB,UAAU;AACtD,QAAM,YAAY,UAAU,kBAAkB,UAAU;AAExD,QAAM,iBAAgC;AAAA,IACpC,UAAU;AAAA,IACV,QAAQ,OAAO,WAAW,WAAW,GAAG,MAAM,OAAO;AAAA,IACrD,OAAO,OAAO,UAAU,WAAW,GAAG,KAAK,OAAO;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,WAAW,QAAQ,gBAAgB;AAAA,IACnC,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AAEA,SACE,6CAAC,SAAI,WAAsB,OAAO,gBAChC;AAAA,gDAAC,SAAI,KAAK,GAAG,QAAQ,aAAa,KAAI,IAAG,WAAW,OAAO,OAAO,UAAU;AAAA,IAC5E;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,WAAW,GAAG,QAAQ,mBAAmB,GAAG,QAAQ;AAAA,QACzD,KAAI;AAAA,QACJ,WAAW;AAAA,QACX,OAAO;AAAA;AAAA,IACT;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK,YAAY,GAAG,QAAQ,oBAAoB,GAAG,QAAQ;AAAA,QAC3D,KAAI;AAAA,QACJ,WAAW;AAAA,QACX,OAAO;AAAA;AAAA,IACT;AAAA,KACF;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/bongo-cat.tsx"],"sourcesContent":["export { BongoCat } from \"./bongo-cat\";\nexport type { BongoCatProps } from \"./bongo-cat\";\n","import { useState, useEffect, useRef, useCallback, CSSProperties } from \"react\";\n\ntype CatState = \"idle\" | \"leftPawDown\" | \"rightPawDown\" | \"bothPawsDown\";\n\nexport interface BongoCatProps {\n /** Base URL path to the sprite assets directory (must contain base.png, left-up.png, left-down.png, right-up.png, right-down.png). Defaults to \"/bongo-cat\" */\n assetsPath?: string;\n /** CSS bottom offset. Defaults to 16 */\n bottom?: number | string;\n /** CSS right offset. Defaults to 16 */\n right?: number | string;\n /** Width in px. Defaults to 65 */\n width?: number;\n /** Height in px. Defaults to 40 */\n height?: number;\n /** z-index. Defaults to 9998 */\n zIndex?: number;\n /** Enable scale pulse on input. Defaults to true */\n pulse?: boolean;\n /** Margin-top applied to each sprite image to ground the cat visually. Defaults to \"37%\" */\n spriteMarginTop?: string | number;\n /** Show a fun tooltip when the cat is clicked. Defaults to true */\n clickTooltip?: boolean;\n /** Custom tooltip messages. Uses built-in cat messages if not provided */\n messages?: string[];\n /** How long the tooltip stays visible in ms. Defaults to 2000 */\n messageDuration?: number;\n /** Additional className on the container */\n className?: string;\n /** Additional inline styles on the container */\n style?: CSSProperties;\n}\n\n// Physical key codes for the left side of the keyboard\nconst LEFT_KEYS = new Set([\n \"Digit1\", \"Digit2\", \"Digit3\", \"Digit4\", \"Digit5\",\n \"KeyQ\", \"KeyW\", \"KeyE\", \"KeyR\", \"KeyT\",\n \"KeyA\", \"KeyS\", \"KeyD\", \"KeyF\", \"KeyG\",\n \"KeyZ\", \"KeyX\", \"KeyC\", \"KeyV\", \"KeyB\",\n \"Tab\", \"CapsLock\", \"ShiftLeft\", \"ControlLeft\", \"AltLeft\", \"MetaLeft\",\n \"Backquote\", \"Escape\",\n]);\n\nconst RIGHT_KEYS = new Set([\n \"Digit6\", \"Digit7\", \"Digit8\", \"Digit9\", \"Digit0\",\n \"KeyY\", \"KeyU\", \"KeyI\", \"KeyO\", \"KeyP\",\n \"KeyH\", \"KeyJ\", \"KeyK\", \"KeyL\",\n \"KeyN\", \"KeyM\",\n \"Enter\", \"Backspace\", \"Delete\", \"ShiftRight\", \"ControlRight\", \"AltRight\", \"MetaRight\",\n \"BracketLeft\", \"BracketRight\", \"Backslash\", \"Semicolon\", \"Quote\",\n \"Comma\", \"Period\", \"Slash\", \"Minus\", \"Equal\",\n \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\",\n]);\n\nconst MIN_ANIMATION_MS = 100;\n\nconst DEFAULT_MESSAGES = [\n \"meow~ keep typing! 🐱\",\n \"purrr... nice clicks!\",\n \"i'm helping! 🐾\",\n \"*bonk bonk bonk*\",\n \"nyaa~ don't mind me~\",\n \"cats make everything better ✨\",\n \"10/10 typing form 👏\",\n \"*purring intensifies*\",\n \"you're doing great, hooman!\",\n \"boop! 🐾\",\n \"feed me... with keystrokes\",\n \"i sit on keyboard now\",\n \"you type, i bonk 🎹\",\n];\n\nconst tooltipStyle: CSSProperties = {\n position: \"absolute\",\n top: -36,\n right: 0,\n whiteSpace: \"nowrap\",\n borderRadius: 8,\n backgroundColor: \"#18181b\",\n color: \"#fafafa\",\n padding: \"6px 12px\",\n fontSize: 12,\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n animation: \"bongocat-fade-in 0.15s ease-out\",\n pointerEvents: \"none\",\n};\n\nconst baseImgStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n pointerEvents: \"none\",\n};\n\nexport function BongoCat({\n assetsPath = \"/bongo-cat\",\n bottom = 16,\n right = 16,\n width = 65,\n height = 40,\n zIndex = 9998,\n pulse: pulseEnabled = true,\n spriteMarginTop = \"37%\",\n clickTooltip = true,\n messages = DEFAULT_MESSAGES,\n messageDuration = 2000,\n className = \"\",\n style: userStyle,\n}: BongoCatProps = {}) {\n const [state, setState] = useState<CatState>(\"idle\");\n const [pulse, setPulse] = useState(false);\n const [tooltipMsg, setTooltipMsg] = useState<string | null>(null);\n const pawDownTimeRef = useRef(0);\n const idleTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n const tooltipTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n const basePath = assetsPath.replace(/\\/$/, \"\");\n\n const imgStyle: CSSProperties = {\n ...baseImgStyle,\n marginTop: typeof spriteMarginTop === \"number\" ? `${spriteMarginTop}px` : spriteMarginTop,\n };\n\n const returnToIdle = useCallback(() => {\n const elapsed = Date.now() - pawDownTimeRef.current;\n const remaining = Math.max(0, MIN_ANIMATION_MS - elapsed);\n\n clearTimeout(idleTimerRef.current);\n idleTimerRef.current = setTimeout(() => setState(\"idle\"), remaining);\n }, []);\n\n const triggerPulse = useCallback(() => {\n if (!pulseEnabled) return;\n setPulse(true);\n requestAnimationFrame(() => {\n setTimeout(() => setPulse(false), 150);\n });\n }, [pulseEnabled]);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.repeat) return;\n\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (LEFT_KEYS.has(e.code)) {\n setState(\"leftPawDown\");\n } else if (RIGHT_KEYS.has(e.code)) {\n setState(\"rightPawDown\");\n } else {\n setState(e.code.charCodeAt(0) % 2 === 0 ? \"leftPawDown\" : \"rightPawDown\");\n }\n };\n\n const onKeyUp = () => {\n returnToIdle();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (e.button === 2) {\n setState(\"rightPawDown\");\n } else {\n setState(\"leftPawDown\");\n }\n };\n\n const onMouseUp = () => {\n returnToIdle();\n };\n\n const onContextMenu = () => {\n returnToIdle();\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"mousedown\", onMouseDown, true);\n window.addEventListener(\"mouseup\", onMouseUp, true);\n window.addEventListener(\"contextmenu\", onContextMenu, true);\n\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"mousedown\", onMouseDown, true);\n window.removeEventListener(\"mouseup\", onMouseUp, true);\n window.removeEventListener(\"contextmenu\", onContextMenu, true);\n clearTimeout(idleTimerRef.current);\n clearTimeout(tooltipTimerRef.current);\n };\n }, [returnToIdle, triggerPulse]);\n\n const handleCatClick = useCallback(() => {\n if (!clickTooltip || messages.length === 0) return;\n clearTimeout(tooltipTimerRef.current);\n setTooltipMsg(messages[Math.floor(Math.random() * messages.length)]);\n tooltipTimerRef.current = setTimeout(() => setTooltipMsg(null), messageDuration);\n }, [clickTooltip, messages, messageDuration]);\n\n const leftDown = state === \"leftPawDown\" || state === \"bothPawsDown\";\n const rightDown = state === \"rightPawDown\" || state === \"bothPawsDown\";\n\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: typeof bottom === \"number\" ? `${bottom}px` : bottom,\n right: typeof right === \"number\" ? `${right}px` : right,\n width,\n height,\n zIndex,\n cursor: clickTooltip ? \"pointer\" : undefined,\n userSelect: \"none\",\n transform: pulse ? \"scale(1.08)\" : \"scale(1)\",\n transition: \"transform 0.1s ease-out\",\n ...userStyle,\n };\n\n return (\n <>\n <style>{`@keyframes bongocat-fade-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}`}</style>\n <div\n className={className}\n style={containerStyle}\n onClick={handleCatClick}\n >\n {tooltipMsg && <div style={tooltipStyle}>{tooltipMsg}</div>}\n <img src={`${basePath}/base.png`} alt=\"\" draggable={false} style={imgStyle} />\n <img\n src={leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n <img\n src={rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/bongo-cat.tsx
|
|
2
2
|
import { useState, useEffect, useRef, useCallback } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
var LEFT_KEYS = /* @__PURE__ */ new Set([
|
|
5
5
|
"Digit1",
|
|
6
6
|
"Digit2",
|
|
@@ -71,6 +71,35 @@ var RIGHT_KEYS = /* @__PURE__ */ new Set([
|
|
|
71
71
|
"ArrowRight"
|
|
72
72
|
]);
|
|
73
73
|
var MIN_ANIMATION_MS = 100;
|
|
74
|
+
var DEFAULT_MESSAGES = [
|
|
75
|
+
"meow~ keep typing! \u{1F431}",
|
|
76
|
+
"purrr... nice clicks!",
|
|
77
|
+
"i'm helping! \u{1F43E}",
|
|
78
|
+
"*bonk bonk bonk*",
|
|
79
|
+
"nyaa~ don't mind me~",
|
|
80
|
+
"cats make everything better \u2728",
|
|
81
|
+
"10/10 typing form \u{1F44F}",
|
|
82
|
+
"*purring intensifies*",
|
|
83
|
+
"you're doing great, hooman!",
|
|
84
|
+
"boop! \u{1F43E}",
|
|
85
|
+
"feed me... with keystrokes",
|
|
86
|
+
"i sit on keyboard now",
|
|
87
|
+
"you type, i bonk \u{1F3B9}"
|
|
88
|
+
];
|
|
89
|
+
var tooltipStyle = {
|
|
90
|
+
position: "absolute",
|
|
91
|
+
top: -36,
|
|
92
|
+
right: 0,
|
|
93
|
+
whiteSpace: "nowrap",
|
|
94
|
+
borderRadius: 8,
|
|
95
|
+
backgroundColor: "#18181b",
|
|
96
|
+
color: "#fafafa",
|
|
97
|
+
padding: "6px 12px",
|
|
98
|
+
fontSize: 12,
|
|
99
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
100
|
+
animation: "bongocat-fade-in 0.15s ease-out",
|
|
101
|
+
pointerEvents: "none"
|
|
102
|
+
};
|
|
74
103
|
var baseImgStyle = {
|
|
75
104
|
position: "absolute",
|
|
76
105
|
inset: 0,
|
|
@@ -88,13 +117,18 @@ function BongoCat({
|
|
|
88
117
|
zIndex = 9998,
|
|
89
118
|
pulse: pulseEnabled = true,
|
|
90
119
|
spriteMarginTop = "37%",
|
|
120
|
+
clickTooltip = true,
|
|
121
|
+
messages = DEFAULT_MESSAGES,
|
|
122
|
+
messageDuration = 2e3,
|
|
91
123
|
className = "",
|
|
92
124
|
style: userStyle
|
|
93
125
|
} = {}) {
|
|
94
126
|
const [state, setState] = useState("idle");
|
|
95
127
|
const [pulse, setPulse] = useState(false);
|
|
128
|
+
const [tooltipMsg, setTooltipMsg] = useState(null);
|
|
96
129
|
const pawDownTimeRef = useRef(0);
|
|
97
130
|
const idleTimerRef = useRef(void 0);
|
|
131
|
+
const tooltipTimerRef = useRef(void 0);
|
|
98
132
|
const basePath = assetsPath.replace(/\/$/, "");
|
|
99
133
|
const imgStyle = {
|
|
100
134
|
...baseImgStyle,
|
|
@@ -158,8 +192,15 @@ function BongoCat({
|
|
|
158
192
|
window.removeEventListener("mouseup", onMouseUp, true);
|
|
159
193
|
window.removeEventListener("contextmenu", onContextMenu, true);
|
|
160
194
|
clearTimeout(idleTimerRef.current);
|
|
195
|
+
clearTimeout(tooltipTimerRef.current);
|
|
161
196
|
};
|
|
162
197
|
}, [returnToIdle, triggerPulse]);
|
|
198
|
+
const handleCatClick = useCallback(() => {
|
|
199
|
+
if (!clickTooltip || messages.length === 0) return;
|
|
200
|
+
clearTimeout(tooltipTimerRef.current);
|
|
201
|
+
setTooltipMsg(messages[Math.floor(Math.random() * messages.length)]);
|
|
202
|
+
tooltipTimerRef.current = setTimeout(() => setTooltipMsg(null), messageDuration);
|
|
203
|
+
}, [clickTooltip, messages, messageDuration]);
|
|
163
204
|
const leftDown = state === "leftPawDown" || state === "bothPawsDown";
|
|
164
205
|
const rightDown = state === "rightPawDown" || state === "bothPawsDown";
|
|
165
206
|
const containerStyle = {
|
|
@@ -169,30 +210,42 @@ function BongoCat({
|
|
|
169
210
|
width,
|
|
170
211
|
height,
|
|
171
212
|
zIndex,
|
|
172
|
-
|
|
213
|
+
cursor: clickTooltip ? "pointer" : void 0,
|
|
173
214
|
userSelect: "none",
|
|
174
215
|
transform: pulse ? "scale(1.08)" : "scale(1)",
|
|
175
216
|
transition: "transform 0.1s ease-out",
|
|
176
217
|
...userStyle
|
|
177
218
|
};
|
|
178
|
-
return /* @__PURE__ */ jsxs(
|
|
179
|
-
/* @__PURE__ */ jsx("
|
|
180
|
-
/* @__PURE__ */
|
|
181
|
-
"
|
|
182
|
-
{
|
|
183
|
-
src: leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`,
|
|
184
|
-
alt: "",
|
|
185
|
-
draggable: false,
|
|
186
|
-
style: imgStyle
|
|
187
|
-
}
|
|
188
|
-
),
|
|
189
|
-
/* @__PURE__ */ jsx(
|
|
190
|
-
"img",
|
|
219
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
220
|
+
/* @__PURE__ */ jsx("style", { children: `@keyframes bongocat-fade-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}` }),
|
|
221
|
+
/* @__PURE__ */ jsxs(
|
|
222
|
+
"div",
|
|
191
223
|
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
224
|
+
className,
|
|
225
|
+
style: containerStyle,
|
|
226
|
+
onClick: handleCatClick,
|
|
227
|
+
children: [
|
|
228
|
+
tooltipMsg && /* @__PURE__ */ jsx("div", { style: tooltipStyle, children: tooltipMsg }),
|
|
229
|
+
/* @__PURE__ */ jsx("img", { src: `${basePath}/base.png`, alt: "", draggable: false, style: imgStyle }),
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
"img",
|
|
232
|
+
{
|
|
233
|
+
src: leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`,
|
|
234
|
+
alt: "",
|
|
235
|
+
draggable: false,
|
|
236
|
+
style: imgStyle
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
/* @__PURE__ */ jsx(
|
|
240
|
+
"img",
|
|
241
|
+
{
|
|
242
|
+
src: rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`,
|
|
243
|
+
alt: "",
|
|
244
|
+
draggable: false,
|
|
245
|
+
style: imgStyle
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
]
|
|
196
249
|
}
|
|
197
250
|
)
|
|
198
251
|
] });
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bongo-cat.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, CSSProperties } from \"react\";\n\ntype CatState = \"idle\" | \"leftPawDown\" | \"rightPawDown\" | \"bothPawsDown\";\n\nexport interface BongoCatProps {\n /** Base URL path to the sprite assets directory (must contain base.png, left-up.png, left-down.png, right-up.png, right-down.png). Defaults to \"/bongo-cat\" */\n assetsPath?: string;\n /** CSS bottom offset. Defaults to 16 */\n bottom?: number | string;\n /** CSS right offset. Defaults to 16 */\n right?: number | string;\n /** Width in px. Defaults to 65 */\n width?: number;\n /** Height in px. Defaults to 40 */\n height?: number;\n /** z-index. Defaults to 9998 */\n zIndex?: number;\n /** Enable scale pulse on input. Defaults to true */\n pulse?: boolean;\n /** Margin-top applied to each sprite image to ground the cat visually. Defaults to \"37%\" */\n spriteMarginTop?: string | number;\n /** Additional className on the container */\n className?: string;\n /** Additional inline styles on the container */\n style?: CSSProperties;\n}\n\n// Physical key codes for the left side of the keyboard\nconst LEFT_KEYS = new Set([\n \"Digit1\", \"Digit2\", \"Digit3\", \"Digit4\", \"Digit5\",\n \"KeyQ\", \"KeyW\", \"KeyE\", \"KeyR\", \"KeyT\",\n \"KeyA\", \"KeyS\", \"KeyD\", \"KeyF\", \"KeyG\",\n \"KeyZ\", \"KeyX\", \"KeyC\", \"KeyV\", \"KeyB\",\n \"Tab\", \"CapsLock\", \"ShiftLeft\", \"ControlLeft\", \"AltLeft\", \"MetaLeft\",\n \"Backquote\", \"Escape\",\n]);\n\nconst RIGHT_KEYS = new Set([\n \"Digit6\", \"Digit7\", \"Digit8\", \"Digit9\", \"Digit0\",\n \"KeyY\", \"KeyU\", \"KeyI\", \"KeyO\", \"KeyP\",\n \"KeyH\", \"KeyJ\", \"KeyK\", \"KeyL\",\n \"KeyN\", \"KeyM\",\n \"Enter\", \"Backspace\", \"Delete\", \"ShiftRight\", \"ControlRight\", \"AltRight\", \"MetaRight\",\n \"BracketLeft\", \"BracketRight\", \"Backslash\", \"Semicolon\", \"Quote\",\n \"Comma\", \"Period\", \"Slash\", \"Minus\", \"Equal\",\n \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\",\n]);\n\nconst MIN_ANIMATION_MS = 100;\n\nconst baseImgStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n pointerEvents: \"none\",\n};\n\nexport function BongoCat({\n assetsPath = \"/bongo-cat\",\n bottom = 16,\n right = 16,\n width = 65,\n height = 40,\n zIndex = 9998,\n pulse: pulseEnabled = true,\n spriteMarginTop = \"37%\",\n className = \"\",\n style: userStyle,\n}: BongoCatProps = {}) {\n const [state, setState] = useState<CatState>(\"idle\");\n const [pulse, setPulse] = useState(false);\n const pawDownTimeRef = useRef(0);\n const idleTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n const basePath = assetsPath.replace(/\\/$/, \"\");\n\n const imgStyle: CSSProperties = {\n ...baseImgStyle,\n marginTop: typeof spriteMarginTop === \"number\" ? `${spriteMarginTop}px` : spriteMarginTop,\n };\n\n const returnToIdle = useCallback(() => {\n const elapsed = Date.now() - pawDownTimeRef.current;\n const remaining = Math.max(0, MIN_ANIMATION_MS - elapsed);\n\n clearTimeout(idleTimerRef.current);\n idleTimerRef.current = setTimeout(() => setState(\"idle\"), remaining);\n }, []);\n\n const triggerPulse = useCallback(() => {\n if (!pulseEnabled) return;\n setPulse(true);\n requestAnimationFrame(() => {\n setTimeout(() => setPulse(false), 150);\n });\n }, [pulseEnabled]);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.repeat) return;\n\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (LEFT_KEYS.has(e.code)) {\n setState(\"leftPawDown\");\n } else if (RIGHT_KEYS.has(e.code)) {\n setState(\"rightPawDown\");\n } else {\n setState(e.code.charCodeAt(0) % 2 === 0 ? \"leftPawDown\" : \"rightPawDown\");\n }\n };\n\n const onKeyUp = () => {\n returnToIdle();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (e.button === 2) {\n setState(\"rightPawDown\");\n } else {\n setState(\"leftPawDown\");\n }\n };\n\n const onMouseUp = () => {\n returnToIdle();\n };\n\n const onContextMenu = () => {\n returnToIdle();\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"mousedown\", onMouseDown, true);\n window.addEventListener(\"mouseup\", onMouseUp, true);\n window.addEventListener(\"contextmenu\", onContextMenu, true);\n\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"mousedown\", onMouseDown, true);\n window.removeEventListener(\"mouseup\", onMouseUp, true);\n window.removeEventListener(\"contextmenu\", onContextMenu, true);\n clearTimeout(idleTimerRef.current);\n };\n }, [returnToIdle, triggerPulse]);\n\n const leftDown = state === \"leftPawDown\" || state === \"bothPawsDown\";\n const rightDown = state === \"rightPawDown\" || state === \"bothPawsDown\";\n\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: typeof bottom === \"number\" ? `${bottom}px` : bottom,\n right: typeof right === \"number\" ? `${right}px` : right,\n width,\n height,\n zIndex,\n pointerEvents: \"none\",\n userSelect: \"none\",\n transform: pulse ? \"scale(1.08)\" : \"scale(1)\",\n transition: \"transform 0.1s ease-out\",\n ...userStyle,\n };\n\n return (\n <div className={className} style={containerStyle}>\n <img src={`${basePath}/base.png`} alt=\"\" draggable={false} style={imgStyle} />\n <img\n src={leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n <img\n src={rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/bongo-cat.tsx"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, CSSProperties } from \"react\";\n\ntype CatState = \"idle\" | \"leftPawDown\" | \"rightPawDown\" | \"bothPawsDown\";\n\nexport interface BongoCatProps {\n /** Base URL path to the sprite assets directory (must contain base.png, left-up.png, left-down.png, right-up.png, right-down.png). Defaults to \"/bongo-cat\" */\n assetsPath?: string;\n /** CSS bottom offset. Defaults to 16 */\n bottom?: number | string;\n /** CSS right offset. Defaults to 16 */\n right?: number | string;\n /** Width in px. Defaults to 65 */\n width?: number;\n /** Height in px. Defaults to 40 */\n height?: number;\n /** z-index. Defaults to 9998 */\n zIndex?: number;\n /** Enable scale pulse on input. Defaults to true */\n pulse?: boolean;\n /** Margin-top applied to each sprite image to ground the cat visually. Defaults to \"37%\" */\n spriteMarginTop?: string | number;\n /** Show a fun tooltip when the cat is clicked. Defaults to true */\n clickTooltip?: boolean;\n /** Custom tooltip messages. Uses built-in cat messages if not provided */\n messages?: string[];\n /** How long the tooltip stays visible in ms. Defaults to 2000 */\n messageDuration?: number;\n /** Additional className on the container */\n className?: string;\n /** Additional inline styles on the container */\n style?: CSSProperties;\n}\n\n// Physical key codes for the left side of the keyboard\nconst LEFT_KEYS = new Set([\n \"Digit1\", \"Digit2\", \"Digit3\", \"Digit4\", \"Digit5\",\n \"KeyQ\", \"KeyW\", \"KeyE\", \"KeyR\", \"KeyT\",\n \"KeyA\", \"KeyS\", \"KeyD\", \"KeyF\", \"KeyG\",\n \"KeyZ\", \"KeyX\", \"KeyC\", \"KeyV\", \"KeyB\",\n \"Tab\", \"CapsLock\", \"ShiftLeft\", \"ControlLeft\", \"AltLeft\", \"MetaLeft\",\n \"Backquote\", \"Escape\",\n]);\n\nconst RIGHT_KEYS = new Set([\n \"Digit6\", \"Digit7\", \"Digit8\", \"Digit9\", \"Digit0\",\n \"KeyY\", \"KeyU\", \"KeyI\", \"KeyO\", \"KeyP\",\n \"KeyH\", \"KeyJ\", \"KeyK\", \"KeyL\",\n \"KeyN\", \"KeyM\",\n \"Enter\", \"Backspace\", \"Delete\", \"ShiftRight\", \"ControlRight\", \"AltRight\", \"MetaRight\",\n \"BracketLeft\", \"BracketRight\", \"Backslash\", \"Semicolon\", \"Quote\",\n \"Comma\", \"Period\", \"Slash\", \"Minus\", \"Equal\",\n \"ArrowUp\", \"ArrowDown\", \"ArrowLeft\", \"ArrowRight\",\n]);\n\nconst MIN_ANIMATION_MS = 100;\n\nconst DEFAULT_MESSAGES = [\n \"meow~ keep typing! 🐱\",\n \"purrr... nice clicks!\",\n \"i'm helping! 🐾\",\n \"*bonk bonk bonk*\",\n \"nyaa~ don't mind me~\",\n \"cats make everything better ✨\",\n \"10/10 typing form 👏\",\n \"*purring intensifies*\",\n \"you're doing great, hooman!\",\n \"boop! 🐾\",\n \"feed me... with keystrokes\",\n \"i sit on keyboard now\",\n \"you type, i bonk 🎹\",\n];\n\nconst tooltipStyle: CSSProperties = {\n position: \"absolute\",\n top: -36,\n right: 0,\n whiteSpace: \"nowrap\",\n borderRadius: 8,\n backgroundColor: \"#18181b\",\n color: \"#fafafa\",\n padding: \"6px 12px\",\n fontSize: 12,\n boxShadow: \"0 4px 12px rgba(0,0,0,0.15)\",\n animation: \"bongocat-fade-in 0.15s ease-out\",\n pointerEvents: \"none\",\n};\n\nconst baseImgStyle: CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n pointerEvents: \"none\",\n};\n\nexport function BongoCat({\n assetsPath = \"/bongo-cat\",\n bottom = 16,\n right = 16,\n width = 65,\n height = 40,\n zIndex = 9998,\n pulse: pulseEnabled = true,\n spriteMarginTop = \"37%\",\n clickTooltip = true,\n messages = DEFAULT_MESSAGES,\n messageDuration = 2000,\n className = \"\",\n style: userStyle,\n}: BongoCatProps = {}) {\n const [state, setState] = useState<CatState>(\"idle\");\n const [pulse, setPulse] = useState(false);\n const [tooltipMsg, setTooltipMsg] = useState<string | null>(null);\n const pawDownTimeRef = useRef(0);\n const idleTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n const tooltipTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n const basePath = assetsPath.replace(/\\/$/, \"\");\n\n const imgStyle: CSSProperties = {\n ...baseImgStyle,\n marginTop: typeof spriteMarginTop === \"number\" ? `${spriteMarginTop}px` : spriteMarginTop,\n };\n\n const returnToIdle = useCallback(() => {\n const elapsed = Date.now() - pawDownTimeRef.current;\n const remaining = Math.max(0, MIN_ANIMATION_MS - elapsed);\n\n clearTimeout(idleTimerRef.current);\n idleTimerRef.current = setTimeout(() => setState(\"idle\"), remaining);\n }, []);\n\n const triggerPulse = useCallback(() => {\n if (!pulseEnabled) return;\n setPulse(true);\n requestAnimationFrame(() => {\n setTimeout(() => setPulse(false), 150);\n });\n }, [pulseEnabled]);\n\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.repeat) return;\n\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (LEFT_KEYS.has(e.code)) {\n setState(\"leftPawDown\");\n } else if (RIGHT_KEYS.has(e.code)) {\n setState(\"rightPawDown\");\n } else {\n setState(e.code.charCodeAt(0) % 2 === 0 ? \"leftPawDown\" : \"rightPawDown\");\n }\n };\n\n const onKeyUp = () => {\n returnToIdle();\n };\n\n const onMouseDown = (e: MouseEvent) => {\n pawDownTimeRef.current = Date.now();\n clearTimeout(idleTimerRef.current);\n triggerPulse();\n\n if (e.button === 2) {\n setState(\"rightPawDown\");\n } else {\n setState(\"leftPawDown\");\n }\n };\n\n const onMouseUp = () => {\n returnToIdle();\n };\n\n const onContextMenu = () => {\n returnToIdle();\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"keyup\", onKeyUp);\n window.addEventListener(\"mousedown\", onMouseDown, true);\n window.addEventListener(\"mouseup\", onMouseUp, true);\n window.addEventListener(\"contextmenu\", onContextMenu, true);\n\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"keyup\", onKeyUp);\n window.removeEventListener(\"mousedown\", onMouseDown, true);\n window.removeEventListener(\"mouseup\", onMouseUp, true);\n window.removeEventListener(\"contextmenu\", onContextMenu, true);\n clearTimeout(idleTimerRef.current);\n clearTimeout(tooltipTimerRef.current);\n };\n }, [returnToIdle, triggerPulse]);\n\n const handleCatClick = useCallback(() => {\n if (!clickTooltip || messages.length === 0) return;\n clearTimeout(tooltipTimerRef.current);\n setTooltipMsg(messages[Math.floor(Math.random() * messages.length)]);\n tooltipTimerRef.current = setTimeout(() => setTooltipMsg(null), messageDuration);\n }, [clickTooltip, messages, messageDuration]);\n\n const leftDown = state === \"leftPawDown\" || state === \"bothPawsDown\";\n const rightDown = state === \"rightPawDown\" || state === \"bothPawsDown\";\n\n const containerStyle: CSSProperties = {\n position: \"fixed\",\n bottom: typeof bottom === \"number\" ? `${bottom}px` : bottom,\n right: typeof right === \"number\" ? `${right}px` : right,\n width,\n height,\n zIndex,\n cursor: clickTooltip ? \"pointer\" : undefined,\n userSelect: \"none\",\n transform: pulse ? \"scale(1.08)\" : \"scale(1)\",\n transition: \"transform 0.1s ease-out\",\n ...userStyle,\n };\n\n return (\n <>\n <style>{`@keyframes bongocat-fade-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}`}</style>\n <div\n className={className}\n style={containerStyle}\n onClick={handleCatClick}\n >\n {tooltipMsg && <div style={tooltipStyle}>{tooltipMsg}</div>}\n <img src={`${basePath}/base.png`} alt=\"\" draggable={false} style={imgStyle} />\n <img\n src={leftDown ? `${basePath}/left-down.png` : `${basePath}/left-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n <img\n src={rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`}\n alt=\"\"\n draggable={false}\n style={imgStyle}\n />\n </div>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED