@stanko/kaplay-inspector 0.1.6 → 0.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.
Files changed (51) hide show
  1. package/README.md +10 -5
  2. package/dist/components/anchor-control.d.ts +6 -0
  3. package/dist/components/anchor-control.js +18 -0
  4. package/dist/components/blend-control.d.ts +5 -0
  5. package/dist/components/blend-control.js +12 -0
  6. package/dist/components/color-control.d.ts +6 -0
  7. package/dist/components/color-control.js +24 -0
  8. package/dist/components/game-object.d.ts +1 -3
  9. package/dist/components/game-object.js +5 -4
  10. package/dist/components/hold-button.d.ts +2 -1
  11. package/dist/components/hold-button.js +51 -28
  12. package/dist/components/hp-control.d.ts +7 -0
  13. package/dist/components/hp-control.js +8 -0
  14. package/dist/components/inspector.d.ts +2 -3
  15. package/dist/components/inspector.js +3 -2
  16. package/dist/components/number-control.d.ts +7 -0
  17. package/dist/components/number-control.js +7 -0
  18. package/dist/components/number-input.d.ts +8 -0
  19. package/dist/components/number-input.js +51 -0
  20. package/dist/components/search-results.d.ts +1 -3
  21. package/dist/components/search-results.js +2 -2
  22. package/dist/components/sprite-control.d.ts +6 -0
  23. package/dist/components/sprite-control.js +6 -0
  24. package/dist/components/text-control.d.ts +6 -0
  25. package/dist/components/text-control.js +8 -0
  26. package/dist/components/text-input.d.ts +7 -0
  27. package/dist/components/text-input.js +26 -0
  28. package/dist/components/vector-control.d.ts +7 -0
  29. package/dist/components/vector-control.js +10 -0
  30. package/dist/init.d.ts +1 -2
  31. package/dist/init.js +4 -1
  32. package/dist/k.d.ts +4 -0
  33. package/dist/k.js +4 -0
  34. package/dist/lib/draw-bbox.d.ts +1 -2
  35. package/dist/lib/draw-bbox.js +3 -2
  36. package/dist/lib/inspect-comps.js +22 -8
  37. package/dist/lib/to-fixed.d.ts +1 -0
  38. package/dist/lib/to-fixed.js +3 -0
  39. package/dist/styles.css +75 -19
  40. package/package.json +4 -3
  41. package/public/screenshot.png +0 -0
  42. package/dist/components/color-controls.d.ts +0 -6
  43. package/dist/components/color-controls.js +0 -24
  44. package/dist/components/position-controls.d.ts +0 -6
  45. package/dist/components/position-controls.js +0 -10
  46. package/dist/components/sprite-controls.d.ts +0 -6
  47. package/dist/components/sprite-controls.js +0 -6
  48. package/dist/components/text-controls.d.ts +0 -6
  49. package/dist/components/text-controls.js +0 -16
  50. package/dist/lib/round-to-decimal.d.ts +0 -1
  51. package/dist/lib/round-to-decimal.js +0 -4
package/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  A dev tool for [Kaplay](https://kaplayjs.com/) which allows you to explore and inspect the game object tree real time.
4
4
 
5
- ![Kaplay inspector in action](./public/screenshot.png)
5
+ Check the demo: [muffinman.io/kaplay-inspector/](https://muffinman.io/kaplay-inspector/).
6
+
7
+ [![Kaplay inspector in action](./public/screenshot.png)](https://muffinman.io/kaplay-inspector/)
6
8
 
7
9
  ## Features
8
10
 
@@ -11,7 +13,12 @@ A dev tool for [Kaplay](https://kaplayjs.com/) which allows you to explore and i
11
13
  - Hover an object to draw it's area, anchor and bounding box
12
14
  - Inspect object's component and custom props
13
15
  - Log an object to console
14
- - Tweak position, text or color
16
+ - Tweak object properties live
17
+ - position, scale, rotate, skew, z-index
18
+ - opacity, color, blend mode
19
+ - text
20
+ - anchor
21
+ - health
15
22
  - Pause objects
16
23
  - Hide objects
17
24
  - Search for tags or comps
@@ -132,6 +139,4 @@ Same as with colors, be sure to have a higher specificity selector if inspector'
132
139
  ## TODO
133
140
 
134
141
  * [ ] Controllable theme - system/light/dark. At the moment it is always matching the system.
135
- * [ ] Filter/search
136
- * [ ] Persist search in URL or local storage
137
- * [ ] Collapse/Expand all button
142
+ * [ ] Persist search/options in URL or local storage
@@ -0,0 +1,6 @@
1
+ import type { GameObj } from "kaplay";
2
+ export interface AnchorControlProps {
3
+ className?: string;
4
+ obj: GameObj;
5
+ }
6
+ export declare const AnchorControl: ({ className, obj }: AnchorControlProps) => import("preact").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
+ import { cx } from "../lib/cx";
3
+ import { k } from "../k";
4
+ import { VectorControl } from "./vector-control";
5
+ const strings = [
6
+ ["topleft", "top", "topright"],
7
+ ["left", "center", "right"],
8
+ ["botleft", "bot", "botright"],
9
+ ];
10
+ export const AnchorControl = ({ className = "", obj }) => {
11
+ const object = obj;
12
+ const isString = typeof obj.anchor === "string";
13
+ return (_jsx("div", { class: cx(className, "anchor-control"), children: isString ? (_jsxs(_Fragment, { children: [_jsxs("div", { class: "anchor-radios", children: [strings.map((row, i) => {
14
+ return (_jsx("div", { class: "anchor-row", children: row.map((anchor) => {
15
+ return (_jsx("label", { children: _jsx("input", { type: "radio", name: "anchor", value: anchor, checked: anchor === object.anchor, onChange: () => (object.anchor = anchor) }) }, anchor));
16
+ }) }, i));
17
+ }), _jsx("div", { children: isString && object.anchor })] }), _jsx("button", { class: "ki-btn", onClick: () => (object.anchor = k.vec2(0, 0)), children: "Use a vector" })] })) : (_jsxs(_Fragment, { children: [_jsx(VectorControl, { obj: obj, property: "anchor", step: 0.1 }), _jsx("button", { class: "ki-btn", onClick: () => (object.anchor = "center"), children: "Use a named location" })] })) }));
18
+ };
@@ -0,0 +1,5 @@
1
+ export interface BlendControlProps {
2
+ obj: Record<string, any>;
3
+ className?: string;
4
+ }
5
+ export declare const BlendControl: ({ className, obj }: BlendControlProps) => import("preact").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { cx } from "../lib/cx";
3
+ const MODES = [
4
+ { label: "normal", value: 0 },
5
+ { label: "add", value: 1 },
6
+ { label: "multiply", value: 2 },
7
+ { label: "screen", value: 3 },
8
+ { label: "overlay", value: 4 },
9
+ ];
10
+ export const BlendControl = ({ className = "", obj }) => {
11
+ return (_jsx("div", { class: cx(className, "blend-control"), children: MODES.map((mode) => (_jsxs("label", { class: "ki-flex", children: [_jsx("input", { type: "radio", name: `blend-${obj.id}`, value: mode.value, checked: obj.blend === mode.value, onChange: () => (obj.blend = mode.value) }), mode.label] }, mode.value))) }));
12
+ };
@@ -0,0 +1,6 @@
1
+ import type { GameObj } from "kaplay";
2
+ export interface ColorControlProps {
3
+ className?: string;
4
+ obj: GameObj;
5
+ }
6
+ export declare const ColorControl: ({ obj }: ColorControlProps) => import("preact").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { useEffect, useState } from "preact/hooks";
3
+ import { cx } from "../lib/cx";
4
+ const ColorSlider = ({ onChange, value, channel }) => {
5
+ const handleChange = (e) => {
6
+ const target = e.target;
7
+ onChange(channel, parseInt(target.value));
8
+ };
9
+ return (_jsx("input", { className: cx("color-control__slider-input", `color-control__slider-input--${channel}`), type: "range", min: "0", max: "255", value: value, onInput: handleChange }));
10
+ };
11
+ export const ColorControl = ({ obj }) => {
12
+ const object = obj;
13
+ const { r, g, b } = object.color;
14
+ const [color, setColor] = useState({ r, g, b });
15
+ useEffect(() => {
16
+ setColor({ r, g, b });
17
+ }, [r, g, b]);
18
+ const updateColor = (channel, value) => {
19
+ const newColor = { ...color, [channel]: value };
20
+ setColor(newColor);
21
+ object.color[channel] = value;
22
+ };
23
+ return (_jsxs("div", { class: "color-control", children: [_jsx("div", { class: "color-control__swatch", style: { background: `rgb(${color.r} ${color.g} ${color.b})` } }), _jsxs("div", { children: [_jsx(ColorSlider, { value: color.r, onChange: updateColor, channel: "r" }), _jsx(ColorSlider, { value: color.g, onChange: updateColor, channel: "g" }), _jsx(ColorSlider, { value: color.b, onChange: updateColor, channel: "b" })] }), _jsxs("div", { children: ["rgb(", color.r, ", ", color.g, ", ", color.b, ")"] })] }));
24
+ };
@@ -1,5 +1,4 @@
1
1
  import type { GameObj } from "kaplay";
2
- import type { KAPLAYCtxType } from "../init";
3
2
  export interface GameObjectProps {
4
3
  className?: string;
5
4
  obj: GameObj;
@@ -7,6 +6,5 @@ export interface GameObjectProps {
7
6
  isExpanded?: boolean;
8
7
  isRenderRoot?: boolean;
9
8
  shouldDrawInspect: boolean;
10
- k: KAPLAYCtxType;
11
9
  }
12
- export declare const GameObject: ({ obj, className, isExpanded: isExpandedExternal, isRenderRoot, setRenderRoot, shouldDrawInspect, k, }: GameObjectProps) => import("preact").JSX.Element | null;
10
+ export declare const GameObject: ({ obj, className, isExpanded: isExpandedExternal, isRenderRoot, setRenderRoot, shouldDrawInspect, }: GameObjectProps) => import("preact").JSX.Element | null;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
2
  import { useCallback, useEffect, useRef, useState } from "preact/hooks";
3
3
  import { MinusIcon, PlusIcon } from "../components/icons";
4
4
  import { cx } from "../lib/cx";
@@ -6,7 +6,8 @@ import { drawBoundingBox } from "../lib/draw-bbox";
6
6
  import { getObjectInfo } from "../lib/get-object-info";
7
7
  import { Breadcrumbs } from "./breadcrumbs";
8
8
  import { BooleanComp } from "./boolean-comp";
9
- export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal = false, isRenderRoot, setRenderRoot, shouldDrawInspect, k, }) => {
9
+ import { k } from "../k";
10
+ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal = false, isRenderRoot, setRenderRoot, shouldDrawInspect, }) => {
10
11
  const [isExpanded, setIsExpanded] = useState(isExpandedExternal);
11
12
  const updateControllers = useRef([]);
12
13
  const { compsData, tags, compsLabel } = getObjectInfo(obj);
@@ -24,7 +25,7 @@ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal
24
25
  const drawInspect = useCallback((obj) => {
25
26
  if (!obj.hidden) {
26
27
  const updateController = k.onDraw(() => {
27
- drawBoundingBox(obj, k);
28
+ drawBoundingBox(obj);
28
29
  obj.drawInspect();
29
30
  });
30
31
  updateControllers.current.push(updateController);
@@ -54,5 +55,5 @@ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal
54
55
  "game-object--no-children": !hasChildren,
55
56
  }), children: [isInspecting && _jsx(Breadcrumbs, { setRenderRoot: setRenderRoot, obj: obj }), _jsxs("div", { class: "game-object__content", onMouseEnter: handleMouseEnter, onMouseLeave: cancelUpdateControllers, children: [_jsxs("button", { class: cx("game-object__header", {
56
57
  "game-object__header--expandable": showExpandTree,
57
- }), onClick: handleToggleClick, children: [isExpanded ? (_jsx(MinusIcon, { className: "game-object__expand-icon" })) : (_jsx(PlusIcon, { className: "game-object__expand-icon" })), _jsxs("div", { class: "game-object__id", children: ["ID ", obj.id, ":"] }), tags ? (_jsx("div", { class: "game-object__tags", children: isRootObject ? "Root" : tags })) : (_jsx("div", { class: "game-object__comp-names", children: compsLabel })), obj.children.length > 0 && _jsxs("div", { children: ["(", obj.children.length, ")"] }), isObjectDestroyed && (_jsx("div", { class: "game-object__destroyed", children: "DESTROYED" }))] }), _jsxs("div", { class: "game-object__buttons", children: [!isRenderRoot && (_jsx("button", { class: "ki-btn", onClick: () => setRenderRoot(obj), children: "inspect" })), _jsx("button", { class: "ki-btn ", onClick: () => console.log(obj), children: "log" })] }), isExpanded && (_jsx("div", { class: "game-object__comps-wrapper", children: _jsxs("div", { class: "game-object__comps", children: [_jsx(BooleanComp, { obj: obj, propName: "paused" }), _jsx(BooleanComp, { obj: obj, propName: "hidden" }), hasSize && (_jsxs("div", { class: "game-object__comps-row", children: [_jsx("b", { children: "size" }), _jsxs("div", { children: [obj.width, " x ", obj.height] })] })), compsData.map((comp) => (_jsxs("div", { class: "game-object__comps-row", children: [_jsx("b", { children: comp.tag }), _jsx("div", { children: comp.value })] }, comp.tag)))] }) }))] }), isExpanded && hasChildren && (_jsx("div", { class: "game-object__children", style: { display: isExpanded ? "block" : "none" }, children: obj.children.map((child) => (_jsx(GameObject, { k: k, obj: child, setRenderRoot: setRenderRoot, shouldDrawInspect: shouldDrawInspect }, child.id))) }))] }, obj.id));
58
+ }), onClick: handleToggleClick, children: [isExpanded ? (_jsx(MinusIcon, { className: "game-object__expand-icon" })) : (_jsx(PlusIcon, { className: "game-object__expand-icon" })), _jsxs("div", { class: "game-object__id", children: ["ID ", obj.id, ":"] }), tags ? (_jsx("div", { class: "game-object__tags", children: isRootObject ? "Root" : tags })) : (_jsx("div", { class: "game-object__comp-names", children: compsLabel })), obj.children.length > 0 && _jsxs("div", { children: ["(", obj.children.length, ")"] }), isObjectDestroyed && (_jsx("div", { class: "game-object__destroyed", children: "DESTROYED" }))] }), _jsxs("div", { class: "game-object__buttons", children: [!isRenderRoot && (_jsxs(_Fragment, { children: [_jsx("button", { class: "ki-btn ki-btn--red", onClick: () => obj.destroy(), children: "destroy" }), _jsx("button", { class: "ki-btn", onClick: () => setRenderRoot(obj), children: "inspect" })] })), _jsx("button", { class: "ki-btn ", onClick: () => console.log(obj), children: "log" })] }), isExpanded && (_jsx("div", { class: "game-object__comps-wrapper", children: _jsxs("div", { class: "game-object__comps", children: [_jsx(BooleanComp, { obj: obj, propName: "paused" }), _jsx(BooleanComp, { obj: obj, propName: "hidden" }), hasSize && (_jsxs("div", { class: "game-object__comps-row", children: [_jsx("b", { children: "size" }), _jsxs("div", { children: [obj.width, " x ", obj.height] })] })), compsData.map((comp) => (_jsxs("div", { class: "game-object__comps-row", children: [_jsx("b", { children: comp.tag }), _jsx("div", { children: comp.value })] }, comp.tag)))] }) }))] }), isExpanded && hasChildren && (_jsx("div", { class: "game-object__children", style: { display: isExpanded ? "block" : "none" }, children: obj.children.map((child) => (_jsx(GameObject, { obj: child, setRenderRoot: setRenderRoot, shouldDrawInspect: shouldDrawInspect }, child.id))) }))] }, obj.id));
58
59
  };
@@ -3,7 +3,8 @@ export type HoldButtonProps = {
3
3
  children?: JSX.Element | string | number;
4
4
  className?: string;
5
5
  onClickAndHold: () => void;
6
+ onHoldEnd?: () => void;
6
7
  interval?: number;
7
8
  startRepeatingDelay?: number;
8
9
  };
9
- export declare const HoldButton: ({ children, className, onClickAndHold, interval, startRepeatingDelay, }: HoldButtonProps) => JSX.Element;
10
+ export declare const HoldButton: ({ children, className, onClickAndHold, interval, startRepeatingDelay, onHoldEnd, }: HoldButtonProps) => JSX.Element;
@@ -1,33 +1,56 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
- import { useEffect, useRef, useState } from "preact/hooks";
3
- export const HoldButton = ({ children, className, onClickAndHold, interval = 50, startRepeatingDelay = 200, }) => {
4
- const intervalRef = useRef();
5
- const timeoutRef = useRef();
6
- const [isMouseDown, setIsMouseDown] = useState(false);
2
+ import { useCallback, useEffect, useRef } from "preact/hooks";
3
+ export const HoldButton = ({ children, className, onClickAndHold, interval = 50, startRepeatingDelay = 200, onHoldEnd, }) => {
4
+ // const [isActive, setIsActive] = useState(false);
5
+ const timerRef = useRef();
6
+ const onClickAndHoldRef = useRef(onClickAndHold);
7
+ const onHoldEndRef = useRef(onHoldEnd);
8
+ const startTimer = useCallback(() => {
9
+ stopTimer();
10
+ // setIsActive(true);
11
+ // Trigger once immediately
12
+ onClickAndHoldRef.current();
13
+ // Wait before repeating
14
+ timerRef.current = setTimeout(() => {
15
+ // Repeat
16
+ onClickAndHoldRef.current();
17
+ timerRef.current = setInterval(() => {
18
+ onClickAndHoldRef.current();
19
+ }, interval);
20
+ }, startRepeatingDelay);
21
+ }, [interval, startRepeatingDelay]);
22
+ const stopTimer = useCallback(() => {
23
+ // setIsActive(false);
24
+ clearTimeout(timerRef.current);
25
+ clearInterval(timerRef.current);
26
+ }, []);
27
+ const handleHoldEnd = useCallback(() => {
28
+ // console.log("---------end", isActive);
29
+ // if (isActive) {
30
+ stopTimer();
31
+ onHoldEndRef.current?.();
32
+ // }
33
+ }, [
34
+ stopTimer,
35
+ // , isActive
36
+ ]);
37
+ // Keep references updated to the latest callbacks
38
+ // This ensures the interval always calls the latest version
39
+ useEffect(() => {
40
+ onClickAndHoldRef.current = onClickAndHold;
41
+ }, [onClickAndHold]);
42
+ useEffect(() => {
43
+ onClickAndHoldRef.current = onClickAndHold;
44
+ }, [onClickAndHold]);
45
+ // Use document on mouseup and touchend for nicer UX
7
46
  useEffect(() => {
8
- document.addEventListener("mouseup", handleMouseUp);
47
+ document.addEventListener("mouseup", handleHoldEnd);
48
+ document.addEventListener("touchend", handleHoldEnd);
9
49
  return () => {
10
- clearTimeout(timeoutRef.current);
11
- clearInterval(intervalRef.current);
12
- document.removeEventListener("mouseup", handleMouseUp);
50
+ stopTimer();
51
+ document.removeEventListener("mouseup", handleHoldEnd);
52
+ document.removeEventListener("touchend", handleHoldEnd);
13
53
  };
14
- }, []);
15
- useEffect(() => {
16
- clearTimeout(timeoutRef.current);
17
- clearInterval(intervalRef.current);
18
- if (isMouseDown) {
19
- timeoutRef.current = setTimeout(() => {
20
- intervalRef.current = setInterval(() => {
21
- onClickAndHold();
22
- }, interval);
23
- }, startRepeatingDelay);
24
- }
25
- }, [isMouseDown]);
26
- const handleMouseDown = () => {
27
- setIsMouseDown(true);
28
- };
29
- const handleMouseUp = () => {
30
- setIsMouseDown(false);
31
- };
32
- return (_jsx("button", { className: className, onMouseDown: handleMouseDown, onClick: onClickAndHold, children: children }));
54
+ }, [handleHoldEnd]);
55
+ return (_jsx("button", { className: className, onMouseDown: startTimer, onTouchStart: startTimer, children: children }));
33
56
  };
@@ -0,0 +1,7 @@
1
+ import type { GameObj } from "kaplay";
2
+ export interface HpControlProps {
3
+ className?: string;
4
+ obj: GameObj;
5
+ step?: number;
6
+ }
7
+ export declare const HpControl: ({ className, obj, step }: HpControlProps) => import("preact").JSX.Element | null;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { NumberControl } from "./number-control";
3
+ export const HpControl = ({ className = "", obj, step }) => {
4
+ if (obj.hp === undefined) {
5
+ return null;
6
+ }
7
+ return (_jsxs("div", { class: "hp-control ki-flex", children: [_jsx(NumberControl, { className: className, obj: obj, property: "hp", step: step }), " ", "(Max: ", obj.maxHP, ")"] }));
8
+ };
@@ -1,5 +1,4 @@
1
- import type { InspectorOptions, KAPLAYCtxType } from "../init";
1
+ import type { InspectorOptions } from "../init";
2
2
  export interface InspectorProps extends InspectorOptions {
3
- k: KAPLAYCtxType;
4
3
  }
5
- export declare const Inspector: ({ initUpdateTimeout, isVisibleOnLoad, initDrawInspectOnHover, k, }: InspectorProps) => import("preact").JSX.Element;
4
+ export declare const Inspector: ({ initUpdateTimeout, isVisibleOnLoad, initDrawInspectOnHover, }: InspectorProps) => import("preact").JSX.Element;
@@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
3
3
  import { GameObject } from "./game-object";
4
4
  import { useObjectBoolean } from "./boolean-comp";
5
5
  import { SearchResults } from "./search-results";
6
+ import { k } from "../k";
6
7
  const INTERVAL_OPTIONS = [
7
8
  { value: 100, label: "100ms" },
8
9
  { value: 250, label: "250ms" },
@@ -10,7 +11,7 @@ const INTERVAL_OPTIONS = [
10
11
  { value: 1000, label: "1s" },
11
12
  ];
12
13
  const TYPING_TIMEOUT = 250;
13
- export const Inspector = ({ initUpdateTimeout = 250, isVisibleOnLoad = true, initDrawInspectOnHover = true, k, }) => {
14
+ export const Inspector = ({ initUpdateTimeout = 250, isVisibleOnLoad = true, initDrawInspectOnHover = true, }) => {
14
15
  const [updateTimeout, setUpdateTimeout] = useState(initUpdateTimeout);
15
16
  const [shouldDrawInspect, setShouldDrawInspect] = useState(initDrawInspectOnHover);
16
17
  const [root, setRoot] = useState(k.getTreeRoot());
@@ -44,5 +45,5 @@ export const Inspector = ({ initUpdateTimeout = 250, isVisibleOnLoad = true, ini
44
45
  if (!isVisible) {
45
46
  return (_jsx("button", { class: "ki-btn k-inspector__show", onClick: toggleVisibility, children: "Show Inspector" }));
46
47
  }
47
- return (_jsxs(_Fragment, { children: [_jsxs("div", { class: "k-inspector__header", children: [_jsx("button", { class: "ki-btn", onClick: () => paused.onChange(!paused.checked), children: paused.checked ? "Resume Game" : "Pause Game" }), "\u2022", _jsx("input", { placeholder: "Search tags or comps", type: "text", class: "ki-input", onInput: handleSearchInput }), "\u2022", _jsxs("div", { children: [k.get("*", { recursive: true }).length, " objects"] }), "\u2022", _jsxs("div", { children: [Math.round(k.debug.fps()), " fps"] }), "\u2022", _jsxs("div", { class: "k-inspector__interval", children: ["Update:", INTERVAL_OPTIONS.map((option) => (_jsxs("label", { children: [_jsx("input", { type: "radio", name: "interval", value: option.value, checked: updateTimeout === option.value, onChange: () => setUpdateTimeout(option.value) }), option.label] }, option.value)))] }), "\u2022", _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: shouldDrawInspect, onChange: () => setShouldDrawInspect(!shouldDrawInspect) }), "Draw bbox on hover"] }), _jsx("button", { class: "ki-btn k-inspector__hide", onClick: toggleVisibility, children: "Hide" })] }), _jsx("div", { class: "k-inspector__objects", children: searchTerm.length > 0 ? (_jsx(SearchResults, { k: k, results: searchResults, setRenderRoot: setRoot, shouldDrawInspect: shouldDrawInspect })) : (_jsx(GameObject, { k: k, className: "game-object--root", obj: root, setRenderRoot: setRoot, shouldDrawInspect: shouldDrawInspect, isExpanded: true, isRenderRoot: true })) })] }));
48
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { class: "k-inspector__header", children: [_jsx("button", { class: "ki-btn", onClick: () => paused.onChange(!paused.checked), children: paused.checked ? "Resume Game" : "Pause Game" }), "\u2022", _jsx("input", { placeholder: "Search tags or comps", type: "text", class: "ki-input", onInput: handleSearchInput }), "\u2022", _jsxs("div", { children: [k.get("*", { recursive: true }).length, " objects"] }), "\u2022", _jsxs("div", { children: [Math.round(k.debug.fps()), " fps"] }), "\u2022", _jsxs("div", { class: "k-inspector__interval", children: ["Update:", INTERVAL_OPTIONS.map((option) => (_jsxs("label", { children: [_jsx("input", { type: "radio", name: "interval", value: option.value, checked: updateTimeout === option.value, onChange: () => setUpdateTimeout(option.value) }), option.label] }, option.value)))] }), "\u2022", _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: shouldDrawInspect, onChange: () => setShouldDrawInspect(!shouldDrawInspect) }), "Draw bbox on hover"] }), _jsx("button", { class: "ki-btn k-inspector__hide", onClick: toggleVisibility, children: "Hide" })] }), _jsx("div", { class: "k-inspector__objects", children: searchTerm.length > 0 ? (_jsx(SearchResults, { results: searchResults, setRenderRoot: setRoot, shouldDrawInspect: shouldDrawInspect })) : (_jsx(GameObject, { className: "game-object--root", obj: root, setRenderRoot: setRoot, shouldDrawInspect: shouldDrawInspect, isExpanded: true, isRenderRoot: true })) })] }));
48
49
  };
@@ -0,0 +1,7 @@
1
+ export interface NumberControlProps {
2
+ obj: Record<string, any>;
3
+ property: string;
4
+ className?: string;
5
+ step?: number;
6
+ }
7
+ export declare const NumberControl: ({ className, obj, property, step, }: NumberControlProps) => import("preact").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { NumberInput } from "./number-input";
3
+ export const NumberControl = ({ className = "", obj, property, step, }) => {
4
+ return (_jsx(NumberInput, { className: className, obj: obj, property: property, onChange: (n) => {
5
+ obj[property] = n;
6
+ }, step: step }));
7
+ };
@@ -0,0 +1,8 @@
1
+ export interface NumberInputProps {
2
+ obj: Record<string, any>;
3
+ property: string;
4
+ onChange: (value: number) => void;
5
+ className?: string;
6
+ step?: number;
7
+ }
8
+ export declare const NumberInput: ({ obj, property, className, onChange, step, }: NumberInputProps) => import("preact").JSX.Element | null;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { useEffect, useState } from "preact/hooks";
3
+ import { cx } from "../lib/cx";
4
+ import { toFixed } from "../lib/to-fixed";
5
+ import { HoldButton } from "./hold-button";
6
+ export const NumberInput = ({ obj, property, className = "", onChange, step = 1, }) => {
7
+ const [localValue, setLocalValue] = useState(toFixed(obj[property]).toString());
8
+ const [isFocused, setIsFocused] = useState(false);
9
+ const [error, setError] = useState(false);
10
+ // Sync game state -> UI when input is not focused
11
+ useEffect(() => {
12
+ if (!isFocused) {
13
+ setLocalValue(toFixed(obj[property]).toString());
14
+ }
15
+ }, [obj[property], isFocused]);
16
+ // Has to be after hooks
17
+ if (obj[property] === undefined) {
18
+ return null;
19
+ }
20
+ const handleInput = (e) => {
21
+ const value = e.target.value;
22
+ setLocalValue(value);
23
+ const parsedValue = parseFloat(value);
24
+ if (!isNaN(value) && !Number.isNaN(parsedValue)) {
25
+ // Sync UI -> game state
26
+ onChange(parsedValue);
27
+ setError(false);
28
+ }
29
+ else {
30
+ setError(true);
31
+ }
32
+ };
33
+ const handleClickAndHold = (offset) => {
34
+ // Use local value so we don't wait for the inspector to re-render
35
+ const newValue = parseFloat(localValue) + offset;
36
+ // obj[property] = newValue;
37
+ onChange(newValue);
38
+ setLocalValue(toFixed(newValue).toString());
39
+ };
40
+ return (_jsxs("div", { class: cx(className, "number-input ki-flex"), children: [_jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => handleClickAndHold(-step), onHoldEnd: () => {
41
+ // Final sync to ensure value is in sync
42
+ setLocalValue(toFixed(obj[property]).toString());
43
+ }, children: "-" }), _jsx("input", { class: cx("ki-input", {
44
+ "ki-input--error": error,
45
+ }), value: localValue, onInput: handleInput, onFocus: () => setIsFocused(true), onBlur: () => {
46
+ setIsFocused(false);
47
+ setError(false);
48
+ // Final sync to ensure value is in sync
49
+ setLocalValue(toFixed(obj[property]).toString());
50
+ } }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => handleClickAndHold(step), children: "+" })] }));
51
+ };
@@ -1,10 +1,8 @@
1
1
  import type { GameObj } from "kaplay";
2
- import type { KAPLAYCtxType } from "../init";
3
2
  export interface SearchResultsProps {
4
- k: KAPLAYCtxType;
5
3
  className?: string;
6
4
  results: GameObj[];
7
5
  setRenderRoot: (obj: GameObj) => void;
8
6
  shouldDrawInspect: boolean;
9
7
  }
10
- export declare const SearchResults: ({ k, results, setRenderRoot, shouldDrawInspect, }: SearchResultsProps) => import("preact").JSX.Element;
8
+ export declare const SearchResults: ({ results, setRenderRoot, shouldDrawInspect, }: SearchResultsProps) => import("preact").JSX.Element;
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { GameObject } from "./game-object";
3
- export const SearchResults = ({ k, results, setRenderRoot, shouldDrawInspect, }) => {
3
+ export const SearchResults = ({ results, setRenderRoot, shouldDrawInspect, }) => {
4
4
  if (results.length === 0) {
5
5
  return _jsx("div", { children: "No results found." });
6
6
  }
7
7
  return (_jsxs("div", { children: [results.map((result) => {
8
- return (_jsx(GameObject, { k: k, className: "game-object--root", obj: result, setRenderRoot: setRenderRoot, shouldDrawInspect: shouldDrawInspect }));
8
+ return (_jsx(GameObject, { className: "game-object--root", obj: result, setRenderRoot: setRenderRoot, shouldDrawInspect: shouldDrawInspect }));
9
9
  }), " "] }));
10
10
  };
@@ -0,0 +1,6 @@
1
+ import type { GameObj } from "kaplay";
2
+ export interface SpriteControlProps {
3
+ className?: string;
4
+ obj: GameObj;
5
+ }
6
+ export declare const SpriteControl: ({ obj }: SpriteControlProps) => import("preact").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
+ export const SpriteControl = ({ obj }) => {
3
+ const object = obj;
4
+ const animation = object.getCurAnim();
5
+ return (_jsxs("div", { class: "sprite-control", children: [_jsx("b", { children: object.sprite }), _jsxs("div", { children: ["frame: ", object.frame] }), animation && (_jsxs(_Fragment, { children: [_jsxs("div", { children: ["animation: ", animation.name] }), _jsxs("div", { children: ["animation frame: ", animation?.frameIndex] })] }))] }));
6
+ };
@@ -0,0 +1,6 @@
1
+ import type { GameObj } from "kaplay";
2
+ export interface TextControlProps {
3
+ className?: string;
4
+ obj: GameObj;
5
+ }
6
+ export declare const TextControl: ({ obj }: TextControlProps) => import("preact").JSX.Element | null;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { TextInput } from "./text-input";
3
+ export const TextControl = ({ obj }) => {
4
+ if (typeof obj.text !== "string") {
5
+ return null;
6
+ }
7
+ return (_jsx("div", { class: "text-control", children: _jsx(TextInput, { className: "text-control__input ki-input", placeholder: "Enter text", value: obj.text, onChange: (text) => (obj.text = text) }) }));
8
+ };
@@ -0,0 +1,7 @@
1
+ export interface TextInputProps {
2
+ className?: string;
3
+ value: string;
4
+ onChange: (text: string) => void;
5
+ placeholder?: string;
6
+ }
7
+ export declare const TextInput: ({ className, value, onChange, placeholder, }: TextInputProps) => import("preact").JSX.Element | null;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { useEffect, useState } from "preact/hooks";
3
+ import { cx } from "../lib/cx";
4
+ export const TextInput = ({ className = "", value, onChange, placeholder = "", }) => {
5
+ const [localValue, setLocalValue] = useState(value.toString());
6
+ const [isFocused, setIsFocused] = useState(false);
7
+ // Sync game state -> UI when input is not focused
8
+ useEffect(() => {
9
+ if (!isFocused) {
10
+ setLocalValue(value.toString());
11
+ }
12
+ }, [value, isFocused]);
13
+ if (value === undefined) {
14
+ return null;
15
+ }
16
+ const handleInput = (e) => {
17
+ const value = e.target.value;
18
+ setLocalValue(value);
19
+ onChange(value);
20
+ };
21
+ return (_jsx("textarea", { class: cx(className, "text-input ki-input"), value: localValue, onInput: handleInput, placeholder: placeholder, onFocus: () => setIsFocused(true), onBlur: () => {
22
+ setIsFocused(false);
23
+ // Final sync to ensure value is in sync
24
+ setLocalValue(value);
25
+ } }));
26
+ };
@@ -0,0 +1,7 @@
1
+ export interface VectorControlProps {
2
+ className?: string;
3
+ obj: Record<string, any>;
4
+ property: string;
5
+ step?: number;
6
+ }
7
+ export declare const VectorControl: ({ className, obj, property, step, }: VectorControlProps) => import("preact").JSX.Element | null;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { cx } from "../lib/cx";
3
+ import { NumberInput } from "./number-input";
4
+ import { k } from "../k";
5
+ export const VectorControl = ({ className = "", obj, property, step, }) => {
6
+ if (obj[property] === undefined) {
7
+ return null;
8
+ }
9
+ return (_jsxs("div", { className: cx(className, "vector-control ki-flex"), children: ["x:", " ", _jsx(NumberInput, { className: "vector-control__x-input", obj: obj[property], property: "x", onChange: (x) => (obj[property] = k.vec2(x, obj[property].y)), step: step }), "y:", " ", _jsx(NumberInput, { obj: obj[property], property: "y", onChange: (y) => (obj[property] = k.vec2(obj[property].x, y)), step: step })] }));
10
+ };
package/dist/init.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import kaplay from "kaplay";
2
- export type KAPLAYCtxType = ReturnType<typeof kaplay>;
1
+ import { type KAPLAYCtxType } from "./k";
3
2
  export interface InspectorOptions {
4
3
  initUpdateTimeout?: number;
5
4
  initDrawInspectOnHover?: boolean;
package/dist/init.js CHANGED
@@ -1,10 +1,13 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
2
  import { render } from "preact";
3
3
  import { Inspector } from "./components/inspector";
4
+ import { setK } from "./k";
4
5
  export default function init(k, props = {}) {
5
6
  const { className = "", initUpdateTimeout = 250, isVisibleOnLoad = true, initDrawInspectOnHover = true, } = props;
7
+ // Set kaplay context to be imported directly from components to reduce prop drilling
8
+ setK(k);
6
9
  const appElement = document.createElement("div");
7
10
  appElement.className = `k-inspector ${className}`;
8
11
  document.body.appendChild(appElement);
9
- render(_jsx(Inspector, { k: k, initUpdateTimeout: initUpdateTimeout, isVisibleOnLoad: isVisibleOnLoad, initDrawInspectOnHover: initDrawInspectOnHover }), appElement);
12
+ render(_jsx(Inspector, { initUpdateTimeout: initUpdateTimeout, isVisibleOnLoad: isVisibleOnLoad, initDrawInspectOnHover: initDrawInspectOnHover }), appElement);
10
13
  }
package/dist/k.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import kaplay from "kaplay";
2
+ export type KAPLAYCtxType = ReturnType<typeof kaplay>;
3
+ export declare let k: KAPLAYCtxType;
4
+ export declare const setK: (ctx: KAPLAYCtxType) => void;
package/dist/k.js ADDED
@@ -0,0 +1,4 @@
1
+ export let k;
2
+ export const setK = (ctx) => {
3
+ k = ctx;
4
+ };
@@ -1,3 +1,2 @@
1
1
  import type { GameObj } from "kaplay";
2
- import type { KAPLAYCtxType } from "../init";
3
- export declare const drawBoundingBox: (obj: GameObj, k: KAPLAYCtxType) => void;
2
+ export declare const drawBoundingBox: (obj: GameObj) => void;
@@ -1,3 +1,4 @@
1
+ import { k } from "../k";
1
2
  const anchorMap = {
2
3
  topleft: [-1, -1],
3
4
  top: [0, -1],
@@ -9,7 +10,7 @@ const anchorMap = {
9
10
  bot: [0, 1],
10
11
  botright: [1, 1],
11
12
  };
12
- export const drawBoundingBox = (obj, k) => {
13
+ export const drawBoundingBox = (obj) => {
13
14
  if (obj.renderArea) {
14
15
  const localArea = obj.renderArea();
15
16
  const transform = obj.transform.clone();
@@ -40,7 +41,7 @@ export const drawBoundingBox = (obj, k) => {
40
41
  }
41
42
  obj.children.forEach((child) => {
42
43
  if (!child.hidden) {
43
- drawBoundingBox(child, k);
44
+ drawBoundingBox(child);
44
45
  }
45
46
  });
46
47
  };
@@ -1,16 +1,30 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
2
  import { stringify } from "./stringify";
3
- import { PositionControls } from "../components/position-controls";
4
- import { TextControls } from "../components/text-controls";
5
- import { SpriteControls } from "../components/sprite-controls";
6
- import { Color } from "../components/color-controls";
3
+ import { TextControl } from "../components/text-control";
4
+ import { SpriteControl } from "../components/sprite-control";
5
+ import { ColorControl } from "../components/color-control";
7
6
  import { ChildObject } from "../components/child-object";
8
7
  import { isGameObj } from "./is-game-obj";
8
+ import { AnchorControl } from "../components/anchor-control";
9
+ import { HpControl } from "../components/hp-control";
10
+ import { NumberControl } from "../components/number-control";
11
+ import { VectorControl } from "../components/vector-control";
12
+ import { BlendControl } from "../components/blend-control";
9
13
  const componentMap = {
10
- pos: PositionControls,
11
- text: TextControls,
12
- sprite: SpriteControls,
13
- color: Color,
14
+ text: TextControl,
15
+ sprite: SpriteControl,
16
+ color: ColorControl,
17
+ anchor: AnchorControl,
18
+ health: HpControl,
19
+ blend: BlendControl,
20
+ // Vectors
21
+ pos: ({ obj }) => _jsx(VectorControl, { obj: obj, property: "pos" }),
22
+ scale: ({ obj }) => _jsx(VectorControl, { obj: obj, property: "scale", step: 0.1 }),
23
+ skew: ({ obj }) => _jsx(VectorControl, { obj: obj, property: "skew", step: 5 }),
24
+ // Numbers
25
+ opacity: ({ obj }) => (_jsx(NumberControl, { obj: obj, property: "opacity", step: 0.1 })),
26
+ rotate: ({ obj }) => _jsx(NumberControl, { obj: obj, property: "angle", step: 5 }),
27
+ z: ({ obj }) => _jsx(NumberControl, { obj: obj, property: "z" }),
14
28
  };
15
29
  export const inspectComps = (obj) => {
16
30
  const object = obj;
@@ -0,0 +1 @@
1
+ export declare const toFixed: (value: number, decimalPlaces?: number) => number;
@@ -0,0 +1,3 @@
1
+ export const toFixed = (value, decimalPlaces = 3) => {
2
+ return Number(value.toFixed(decimalPlaces));
3
+ };
package/dist/styles.css CHANGED
@@ -73,6 +73,22 @@
73
73
  color: inherit;
74
74
  }
75
75
 
76
+ textarea {
77
+ field-sizing: content;
78
+ max-height: 6rem;
79
+ }
80
+
81
+ input::-webkit-outer-spin-button,
82
+ input::-webkit-inner-spin-button {
83
+ -webkit-appearance: none;
84
+ margin: 0;
85
+ }
86
+
87
+ input[type="number"] {
88
+ -moz-appearance: textfield;
89
+ appearance: textfield;
90
+ }
91
+
76
92
  button {
77
93
  border: none;
78
94
  cursor: pointer;
@@ -100,6 +116,14 @@
100
116
  border: 1px solid var(--ki-primary);
101
117
  }
102
118
 
119
+ .ki-btn--red {
120
+ color: var(--ki-red);
121
+ }
122
+
123
+ .ki-btn--red:hover {
124
+ border: 1px solid var(--ki-red);
125
+ }
126
+
103
127
  .ki-input {
104
128
  background-color: var(--ki-bg);
105
129
  border: none;
@@ -114,6 +138,11 @@
114
138
  border-color: var(--ki-primary);
115
139
  }
116
140
 
141
+ .ki-input--error,
142
+ .ki-input--error:focus-visible {
143
+ border-color: var(--ki-red);
144
+ }
145
+
117
146
  /* Main layout */
118
147
 
119
148
  .k-inspector__header {
@@ -309,35 +338,62 @@
309
338
  padding-bottom: 1rem;
310
339
  }
311
340
 
312
- /* Position controls */
341
+ /* Flex layout for controls */
313
342
 
314
- .pos-controls {
343
+ .ki-flex {
315
344
  display: flex;
316
- align-items: center;
317
345
  gap: 0.25rem;
318
- width: fit-content;
319
- min-width: 12rem;
346
+ align-items: center;
320
347
  }
321
348
 
322
- .pos-controls .ki-btn,
323
- .pos-controls__value {
324
- flex-shrink: 0;
325
- text-align: center;
349
+ /* Number input */
350
+
351
+ .number-input input {
352
+ width: 4.5rem;
326
353
  }
327
354
 
328
- .pos-controls__value {
329
- width: 5rem;
355
+ /* Vector control */
356
+
357
+ .vector-control__x-input {
358
+ margin-right: 0.75rem; /* with gap it comes to 1rem */
359
+ }
360
+
361
+ /* Blend control */
362
+
363
+ .blend-control {
364
+ display: grid;
365
+ gap: 0.25rem;
330
366
  }
331
367
 
332
- /* Text Controls */
368
+ /* Text control */
333
369
 
334
- .text-controls__input {
370
+ .text-control__input {
335
371
  width: 100%;
336
372
  }
337
373
 
374
+ /* Anchor control */
375
+
376
+ .anchor-control {
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 1rem;
380
+ }
381
+
382
+ .anchor-radios {
383
+ text-align: center;
384
+ }
385
+
386
+ .anchor-row {
387
+ display: flex;
388
+ }
389
+
390
+ .anchor-radios label {
391
+ padding: 0.25rem;
392
+ }
393
+
338
394
  /* Color */
339
395
 
340
- .color-controls {
396
+ .color-control {
341
397
  display: flex;
342
398
  align-items: center;
343
399
  gap: 0.5rem;
@@ -345,28 +401,28 @@
345
401
  min-width: 12rem;
346
402
  }
347
403
 
348
- .color-controls__swatch {
404
+ .color-control__swatch {
349
405
  width: 2rem;
350
406
  height: 2rem;
351
407
  border-radius: 8px;
352
408
  border: 1px solid var(--ki-border-light);
353
409
  }
354
410
 
355
- .color-controls__slider {
411
+ .color-control__slider {
356
412
  display: flex;
357
413
  align-items: center;
358
414
  gap: 0.25rem;
359
415
  }
360
416
 
361
- .color-controls__slider-input--r {
417
+ .color-control__slider-input--r {
362
418
  accent-color: red;
363
419
  }
364
420
 
365
- .color-controls__slider-input--g {
421
+ .color-control__slider-input--g {
366
422
  accent-color: green;
367
423
  }
368
424
 
369
- .color-controls__slider-input--b {
425
+ .color-control__slider-input--b {
370
426
  accent-color: blue;
371
427
  }
372
428
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stanko/kaplay-inspector",
3
3
  "description": "A dev tool for Kaplay which allows you to explore and inspect the game object tree real time.",
4
4
  "private": false,
5
- "version": "0.1.6",
5
+ "version": "0.2.0",
6
6
  "type": "module",
7
7
  "main": "./dist/init.js",
8
8
  "types": "./dist/init.d.ts",
@@ -21,7 +21,7 @@
21
21
  "prepublish": "npm run build-lib"
22
22
  },
23
23
  "peerDependencies": {
24
- "kaplay": "^4000.0.0-alpha.20"
24
+ "kaplay": "^4000.0.0-alpha.26"
25
25
  },
26
26
  "dependencies": {
27
27
  "preact": "^10.28.2"
@@ -30,7 +30,8 @@
30
30
  "@preact/preset-vite": "^2.10.2",
31
31
  "@types/node": "^25.0.6",
32
32
  "typescript": "~5.9.3",
33
- "vite": "^7.3.1"
33
+ "vite": "^7.3.1",
34
+ "kaplay": "^4000.0.0-alpha.26"
34
35
  },
35
36
  "repository": {
36
37
  "type": "git",
Binary file
@@ -1,6 +0,0 @@
1
- import type { GameObj } from "kaplay";
2
- export interface ColorProps {
3
- className?: string;
4
- obj: GameObj;
5
- }
6
- export declare const Color: ({ obj }: ColorProps) => import("preact").JSX.Element;
@@ -1,24 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { useEffect, useState } from "preact/hooks";
3
- import { cx } from "../lib/cx";
4
- const ColorSlider = ({ onChange, value, channel }) => {
5
- const handleChange = (e) => {
6
- const target = e.target;
7
- onChange(channel, parseInt(target.value));
8
- };
9
- return (_jsx("input", { className: cx("color-controls__slider-input", `color-controls__slider-input--${channel}`), type: "range", min: "0", max: "255", value: value, onInput: handleChange }));
10
- };
11
- export const Color = ({ obj }) => {
12
- const object = obj;
13
- const { r, g, b } = object.color;
14
- const [color, setColor] = useState({ r, g, b });
15
- useEffect(() => {
16
- setColor({ r, g, b });
17
- }, [r, g, b]);
18
- const updateColor = (channel, value) => {
19
- const newColor = { ...color, [channel]: value };
20
- setColor(newColor);
21
- object.color[channel] = value;
22
- };
23
- return (_jsxs("div", { class: "color-controls", children: [_jsx("div", { class: "color-controls__swatch", style: { background: `rgb(${color.r} ${color.g} ${color.b})` } }), _jsxs("div", { children: [_jsx(ColorSlider, { value: color.r, onChange: updateColor, channel: "r" }), _jsx(ColorSlider, { value: color.g, onChange: updateColor, channel: "g" }), _jsx(ColorSlider, { value: color.b, onChange: updateColor, channel: "b" })] }), _jsxs("div", { children: ["rgb(", color.r, ", ", color.g, ", ", color.b, ")"] })] }));
24
- };
@@ -1,6 +0,0 @@
1
- import type { GameObj } from "kaplay";
2
- export interface PositionControlsProps {
3
- className?: string;
4
- obj: GameObj;
5
- }
6
- export declare const PositionControls: ({ obj }: PositionControlsProps) => import("preact").JSX.Element | null;
@@ -1,10 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { roundToDecimal } from "../lib/round-to-decimal";
3
- import { HoldButton } from "./hold-button";
4
- import { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, } from "./icons";
5
- export const PositionControls = ({ obj }) => {
6
- if (!obj.pos) {
7
- return null;
8
- }
9
- return (_jsxs("div", { class: "pos-controls", children: [_jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => obj.moveTo(obj.pos.x - 1, obj.pos.y), children: _jsx(ArrowLeftIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => obj.moveTo(obj.pos.x + 1, obj.pos.y), children: _jsx(ArrowRightIcon, {}) }), _jsxs("div", { class: "pos-controls__value", children: ["x: ", roundToDecimal(obj.pos.x, 2)] }), _jsxs("div", { class: "pos-controls__value", children: ["y: ", roundToDecimal(obj.pos.y, 2)] }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => obj.moveTo(obj.pos.x, obj.pos.y - 1), children: _jsx(ArrowUpIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => obj.moveTo(obj.pos.x, obj.pos.y + 1), children: _jsx(ArrowDownIcon, {}) })] }));
10
- };
@@ -1,6 +0,0 @@
1
- import type { GameObj } from "kaplay";
2
- export interface SpriteControlsProps {
3
- className?: string;
4
- obj: GameObj;
5
- }
6
- export declare const SpriteControls: ({ obj }: SpriteControlsProps) => import("preact").JSX.Element;
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
- export const SpriteControls = ({ obj }) => {
3
- const object = obj;
4
- const animation = object.getCurAnim();
5
- return (_jsxs("div", { class: "sprite-controls", children: [_jsx("b", { children: object.sprite }), _jsxs("div", { children: ["frame: ", object.frame] }), animation && (_jsxs(_Fragment, { children: [_jsxs("div", { children: ["animation: ", animation.name] }), _jsxs("div", { children: ["animation frame: ", animation?.frameIndex] })] }))] }));
6
- };
@@ -1,6 +0,0 @@
1
- import type { GameObj } from "kaplay";
2
- export interface TextControlsProps {
3
- className?: string;
4
- obj: GameObj;
5
- }
6
- export declare const TextControls: ({ obj }: TextControlsProps) => import("preact").JSX.Element | null;
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx } from "preact/jsx-runtime";
2
- import { useEffect, useState } from "preact/hooks";
3
- export const TextControls = ({ obj }) => {
4
- const [text, setText] = useState(obj.text);
5
- useEffect(() => {
6
- setText(obj.text);
7
- }, [obj.text]);
8
- if (typeof obj.text !== "string") {
9
- return null;
10
- }
11
- const handleInput = (e) => {
12
- obj.text = e.target.value;
13
- setText(text);
14
- };
15
- return (_jsx("div", { class: "text-controls", children: _jsx("input", { class: "text-controls__input", type: "text", placeholder: "Enter text", value: text, onInput: handleInput }) }));
16
- };
@@ -1 +0,0 @@
1
- export declare const roundToDecimal: (value: number, decimalPlaces: number) => number;
@@ -1,4 +0,0 @@
1
- export const roundToDecimal = (value, decimalPlaces) => {
2
- const factor = Math.pow(10, decimalPlaces);
3
- return Math.round(value * factor) / factor;
4
- };