bongocat-react 1.1.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 CHANGED
@@ -49,9 +49,30 @@ function App() {
49
49
  | `zIndex` | `number` | `9998` | z-index of the overlay |
50
50
  | `pulse` | `boolean` | `true` | Scale pulse animation on input |
51
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) |
52
55
  | `className` | `string` | `""` | Additional CSS class |
53
56
  | `style` | `CSSProperties` | — | Additional inline styles |
54
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
+
55
76
  ## Positioning
56
77
 
57
78
  By default, the cat sits in the **bottom-right corner**. Use `bottom`, `right`, and `style` to place it wherever you want:
@@ -88,7 +109,7 @@ This package does not track, collect, or transmit any data. No analytics, no tel
88
109
  - Listens to `keydown`/`keyup` on `document` and `mousedown`/`mouseup` on `window` (capture phase)
89
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
90
111
  - Left mouse click → left paw, right click → right paw
91
- - `pointer-events: none` the cat never intercepts your clicks
112
+ - Click on the cat random fun tooltip message
92
113
  - Handles context menu stealing mouseup events
93
114
 
94
115
  ## Credits
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
- pointerEvents: "none",
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)("div", { className, style: containerStyle, children: [
205
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: `${basePath}/base.png`, alt: "", draggable: false, style: imgStyle }),
206
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
207
- "img",
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
- src: rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`,
219
- alt: "",
220
- draggable: false,
221
- style: imgStyle
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":"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","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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAwE;AAgOpE;AA9LJ,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,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,eAA8B;AAAA,EAClC,UAAU;AAAA,EACV,KAAK;AAAA,EACL,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,WAAW;AAAA,EACX,WAAW;AAAA,EACX,eAAe;AACjB;AAEA,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,eAAe;AAAA,EACf,WAAW;AAAA,EACX,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,CAAC,YAAY,aAAa,QAAI,uBAAwB,IAAI;AAChE,QAAM,qBAAiB,qBAAO,CAAC;AAC/B,QAAM,mBAAe,qBAAsC,MAAS;AACpE,QAAM,sBAAkB,qBAAsC,MAAS;AAEvE,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;AACjC,mBAAa,gBAAgB,OAAO;AAAA,IACtC;AAAA,EACF,GAAG,CAAC,cAAc,YAAY,CAAC;AAE/B,QAAM,qBAAiB,0BAAY,MAAM;AACvC,QAAI,CAAC,gBAAgB,SAAS,WAAW,EAAG;AAC5C,iBAAa,gBAAgB,OAAO;AACpC,kBAAc,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,SAAS,MAAM,CAAC,CAAC;AACnE,oBAAgB,UAAU,WAAW,MAAM,cAAc,IAAI,GAAG,eAAe;AAAA,EACjF,GAAG,CAAC,cAAc,UAAU,eAAe,CAAC;AAE5C,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,QAAQ,eAAe,YAAY;AAAA,IACnC,YAAY;AAAA,IACZ,WAAW,QAAQ,gBAAgB;AAAA,IACnC,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AAEA,SACE,4EACE;AAAA,gDAAC,WAAO,yHAA8G;AAAA,IACtH;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAO;AAAA,QACP,SAAS;AAAA,QAER;AAAA,wBAAc,4CAAC,SAAI,OAAO,cAAe,sBAAW;AAAA,UACrD,4CAAC,SAAI,KAAK,GAAG,QAAQ,aAAa,KAAI,IAAG,WAAW,OAAO,OAAO,UAAU;AAAA,UAC5E;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,WAAW,GAAG,QAAQ,mBAAmB,GAAG,QAAQ;AAAA,cACzD,KAAI;AAAA,cACJ,WAAW;AAAA,cACX,OAAO;AAAA;AAAA,UACT;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,YAAY,GAAG,QAAQ,oBAAoB,GAAG,QAAQ;AAAA,cAC3D,KAAI;AAAA,cACJ,WAAW;AAAA,cACX,OAAO;AAAA;AAAA,UACT;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","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
- pointerEvents: "none",
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("div", { className, style: containerStyle, children: [
179
- /* @__PURE__ */ jsx("img", { src: `${basePath}/base.png`, alt: "", draggable: false, style: imgStyle }),
180
- /* @__PURE__ */ jsx(
181
- "img",
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
- src: rightDown ? `${basePath}/right-down.png` : `${basePath}/right-up.png`,
193
- alt: "",
194
- draggable: false,
195
- style: imgStyle
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
  ] });
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bongocat-react",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A React component that adds an animated BongoCat to your app — reacts to typing and mouse clicks",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",