@stanko/kaplay-inspector 0.1.7 → 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.
- package/README.md +10 -4
- package/dist/components/anchor-control.d.ts +6 -0
- package/dist/components/{anchor-controls.js → anchor-control.js} +4 -4
- package/dist/components/blend-control.d.ts +5 -0
- package/dist/components/blend-control.js +12 -0
- package/dist/components/color-control.d.ts +6 -0
- package/dist/components/color-control.js +24 -0
- package/dist/components/game-object.js +2 -2
- package/dist/components/hold-button.d.ts +2 -1
- package/dist/components/hold-button.js +51 -28
- package/dist/components/hp-control.d.ts +7 -0
- package/dist/components/hp-control.js +8 -0
- package/dist/components/number-control.d.ts +7 -0
- package/dist/components/number-control.js +7 -0
- package/dist/components/number-input.d.ts +8 -0
- package/dist/components/number-input.js +51 -0
- package/dist/components/sprite-control.d.ts +6 -0
- package/dist/components/sprite-control.js +6 -0
- package/dist/components/text-control.d.ts +6 -0
- package/dist/components/text-control.js +8 -0
- package/dist/components/text-input.d.ts +7 -0
- package/dist/components/text-input.js +26 -0
- package/dist/components/vector-control.d.ts +7 -0
- package/dist/components/vector-control.js +10 -0
- package/dist/lib/inspect-comps.js +22 -10
- package/dist/lib/to-fixed.d.ts +1 -0
- package/dist/lib/to-fixed.js +3 -0
- package/dist/styles.css +57 -21
- package/package.json +4 -3
- package/dist/components/anchor-controls.d.ts +0 -6
- package/dist/components/color-controls.d.ts +0 -6
- package/dist/components/color-controls.js +0 -24
- package/dist/components/position-controls.d.ts +0 -6
- package/dist/components/position-controls.js +0 -8
- package/dist/components/sprite-controls.d.ts +0 -6
- package/dist/components/sprite-controls.js +0 -6
- package/dist/components/text-controls.d.ts +0 -6
- package/dist/components/text-controls.js +0 -10
- package/dist/components/vector-controls.d.ts +0 -8
- package/dist/components/vector-controls.js +0 -9
- package/dist/lib/round-to-decimal.d.ts +0 -1
- 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
|
-
|
|
5
|
+
Check the demo: [muffinman.io/kaplay-inspector/](https://muffinman.io/kaplay-inspector/).
|
|
6
|
+
|
|
7
|
+
[](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
|
|
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,5 +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
|
-
* [ ] Persist search in URL or local storage
|
|
136
|
-
* [ ] Collapse/Expand all button
|
|
142
|
+
* [ ] Persist search/options in URL or local storage
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { cx } from "../lib/cx";
|
|
3
|
-
import { VectorControls } from "./vector-controls";
|
|
4
3
|
import { k } from "../k";
|
|
4
|
+
import { VectorControl } from "./vector-control";
|
|
5
5
|
const strings = [
|
|
6
6
|
["topleft", "top", "topright"],
|
|
7
7
|
["left", "center", "right"],
|
|
8
8
|
["botleft", "bot", "botright"],
|
|
9
9
|
];
|
|
10
|
-
export const
|
|
10
|
+
export const AnchorControl = ({ className = "", obj }) => {
|
|
11
11
|
const object = obj;
|
|
12
12
|
const isString = typeof obj.anchor === "string";
|
|
13
|
-
return (_jsx("div", { class: cx(className, "anchor-
|
|
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
14
|
return (_jsx("div", { class: "anchor-row", children: row.map((anchor) => {
|
|
15
15
|
return (_jsx("label", { children: _jsx("input", { type: "radio", name: "anchor", value: anchor, checked: anchor === object.anchor, onChange: () => (object.anchor = anchor) }) }, anchor));
|
|
16
16
|
}) }, i));
|
|
17
|
-
}), _jsx("div", { children: isString && object.anchor })] }), _jsx("button", { class: "ki-btn", onClick: () => (object.anchor = k.vec2(0, 0)), children: "
|
|
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
18
|
};
|
|
@@ -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,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,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";
|
|
@@ -55,5 +55,5 @@ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal
|
|
|
55
55
|
"game-object--no-children": !hasChildren,
|
|
56
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", {
|
|
57
57
|
"game-object__header--expandable": showExpandTree,
|
|
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 && (_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
|
+
}), 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));
|
|
59
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
|
|
3
|
-
export const HoldButton = ({ children, className, onClickAndHold, interval = 50, startRepeatingDelay = 200, }) => {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
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",
|
|
47
|
+
document.addEventListener("mouseup", handleHoldEnd);
|
|
48
|
+
document.addEventListener("touchend", handleHoldEnd);
|
|
9
49
|
return () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
document.removeEventListener("
|
|
50
|
+
stopTimer();
|
|
51
|
+
document.removeEventListener("mouseup", handleHoldEnd);
|
|
52
|
+
document.removeEventListener("touchend", handleHoldEnd);
|
|
13
53
|
};
|
|
14
|
-
}, []);
|
|
15
|
-
|
|
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,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
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -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,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,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
|
+
};
|
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
2
|
import { stringify } from "./stringify";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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";
|
|
9
|
-
import {
|
|
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";
|
|
10
13
|
const componentMap = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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" }),
|
|
16
28
|
};
|
|
17
29
|
export const inspectComps = (obj) => {
|
|
18
30
|
const object = obj;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const toFixed: (value: number, decimalPlaces?: number) => number;
|
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,42 @@
|
|
|
309
338
|
padding-bottom: 1rem;
|
|
310
339
|
}
|
|
311
340
|
|
|
312
|
-
/*
|
|
341
|
+
/* Flex layout for controls */
|
|
313
342
|
|
|
314
|
-
.
|
|
343
|
+
.ki-flex {
|
|
315
344
|
display: flex;
|
|
316
|
-
align-items: center;
|
|
317
345
|
gap: 0.25rem;
|
|
318
|
-
|
|
319
|
-
min-width: 12rem;
|
|
346
|
+
align-items: center;
|
|
320
347
|
}
|
|
321
348
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
349
|
+
/* Number input */
|
|
350
|
+
|
|
351
|
+
.number-input input {
|
|
352
|
+
width: 4.5rem;
|
|
326
353
|
}
|
|
327
354
|
|
|
328
|
-
|
|
329
|
-
|
|
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
|
|
368
|
+
/* Text control */
|
|
333
369
|
|
|
334
|
-
.text-
|
|
370
|
+
.text-control__input {
|
|
335
371
|
width: 100%;
|
|
336
372
|
}
|
|
337
373
|
|
|
338
|
-
/* Anchor
|
|
374
|
+
/* Anchor control */
|
|
339
375
|
|
|
340
|
-
.anchor-
|
|
376
|
+
.anchor-control {
|
|
341
377
|
display: flex;
|
|
342
378
|
align-items: center;
|
|
343
379
|
gap: 1rem;
|
|
@@ -357,7 +393,7 @@
|
|
|
357
393
|
|
|
358
394
|
/* Color */
|
|
359
395
|
|
|
360
|
-
.color-
|
|
396
|
+
.color-control {
|
|
361
397
|
display: flex;
|
|
362
398
|
align-items: center;
|
|
363
399
|
gap: 0.5rem;
|
|
@@ -365,28 +401,28 @@
|
|
|
365
401
|
min-width: 12rem;
|
|
366
402
|
}
|
|
367
403
|
|
|
368
|
-
.color-
|
|
404
|
+
.color-control__swatch {
|
|
369
405
|
width: 2rem;
|
|
370
406
|
height: 2rem;
|
|
371
407
|
border-radius: 8px;
|
|
372
408
|
border: 1px solid var(--ki-border-light);
|
|
373
409
|
}
|
|
374
410
|
|
|
375
|
-
.color-
|
|
411
|
+
.color-control__slider {
|
|
376
412
|
display: flex;
|
|
377
413
|
align-items: center;
|
|
378
414
|
gap: 0.25rem;
|
|
379
415
|
}
|
|
380
416
|
|
|
381
|
-
.color-
|
|
417
|
+
.color-control__slider-input--r {
|
|
382
418
|
accent-color: red;
|
|
383
419
|
}
|
|
384
420
|
|
|
385
|
-
.color-
|
|
421
|
+
.color-control__slider-input--g {
|
|
386
422
|
accent-color: green;
|
|
387
423
|
}
|
|
388
424
|
|
|
389
|
-
.color-
|
|
425
|
+
.color-control__slider-input--b {
|
|
390
426
|
accent-color: blue;
|
|
391
427
|
}
|
|
392
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.
|
|
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.
|
|
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",
|
|
@@ -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,8 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import { VectorControls } from "./vector-controls";
|
|
3
|
-
export const PositionControls = ({ className = "", obj, }) => {
|
|
4
|
-
if (!obj.pos) {
|
|
5
|
-
return null;
|
|
6
|
-
}
|
|
7
|
-
return (_jsx(VectorControls, { className: className, value: obj.pos, onChange: (pos) => (obj.pos = pos) }));
|
|
8
|
-
};
|
|
@@ -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,10 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
export const TextControls = ({ obj }) => {
|
|
3
|
-
if (typeof obj.text !== "string") {
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
const handleInput = (e) => {
|
|
7
|
-
obj.text = e.target.value;
|
|
8
|
-
};
|
|
9
|
-
return (_jsx("div", { class: "text-controls", children: _jsx("textarea", { class: "text-controls__input ki-input", placeholder: "Enter text", value: obj.text, onInput: handleInput }) }));
|
|
10
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Vec2 } from "kaplay";
|
|
2
|
-
export interface VectorControlsProps {
|
|
3
|
-
className?: string;
|
|
4
|
-
value: Vec2;
|
|
5
|
-
onChange: (v: Vec2) => void;
|
|
6
|
-
step?: number;
|
|
7
|
-
}
|
|
8
|
-
export declare const VectorControls: ({ className, value, onChange, step, }: VectorControlsProps) => import("preact").JSX.Element;
|
|
@@ -1,9 +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
|
-
import { k } from "../k";
|
|
6
|
-
import { cx } from "../lib/cx";
|
|
7
|
-
export const VectorControls = ({ className = "", value, onChange, step = 1, }) => {
|
|
8
|
-
return (_jsxs("div", { class: cx(className, "vector-controls"), children: [_jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => onChange(k.vec2(value.x - step, value.y)), children: _jsx(ArrowLeftIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => onChange(k.vec2(value.x + step, value.y)), children: _jsx(ArrowRightIcon, {}) }), _jsxs("div", { class: "vector-controls__value", children: ["x: ", roundToDecimal(value.x, 2)] }), _jsxs("div", { class: "vector-controls__value", children: ["y: ", roundToDecimal(value.y, 2)] }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => onChange(k.vec2(value.x, value.y - step)), children: _jsx(ArrowUpIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => onChange(k.vec2(value.x, value.y + step)), children: _jsx(ArrowDownIcon, {}) })] }));
|
|
9
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const roundToDecimal: (value: number, decimalPlaces: number) => number;
|