@stanko/kaplay-inspector 0.1.0 → 0.1.2
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 +6 -1
- package/dist/components/boolean-comp.d.ts +11 -0
- package/dist/components/boolean-comp.js +21 -0
- package/dist/components/breadcrumbs.js +10 -0
- package/dist/components/child-object.d.ts +6 -0
- package/dist/components/child-object.js +4 -0
- package/dist/components/color-controls.d.ts +6 -0
- package/dist/components/color-controls.js +24 -0
- package/dist/components/game-object.d.ts +1 -1
- package/dist/components/game-object.js +14 -14
- package/dist/components/hold-button.d.ts +9 -0
- package/dist/components/hold-button.js +33 -0
- package/dist/components/icons.d.ts +10 -6
- package/dist/components/icons.js +12 -12
- package/dist/components/inspector.js +7 -5
- package/dist/components/position-controls.js +2 -1
- package/dist/components/sprite-controls.d.ts +6 -0
- package/dist/components/sprite-controls.js +6 -0
- package/dist/components/text-controls.js +10 -3
- package/dist/lib/draw-bbox.js +32 -27
- package/dist/lib/inspect-comps.js +21 -2
- package/dist/lib/is-game-obj.d.ts +2 -0
- package/dist/lib/is-game-obj.js +5 -0
- package/dist/lib/stringify.js +7 -3
- package/dist/styles.css +48 -8
- package/package.json +2 -2
- package/dist/components/sprite.d.ts +0 -6
- package/dist/components/sprite.js +0 -6
package/README.md
CHANGED
|
@@ -52,6 +52,12 @@ if (
|
|
|
52
52
|
}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
If typescript is complaining about importing CSS files, you probably need to add this to `declaration.d.ts` file in you project's root.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
declare module '*.scss';
|
|
59
|
+
```
|
|
60
|
+
|
|
55
61
|
### Options
|
|
56
62
|
|
|
57
63
|
You can pass options object to the init method as a second parameter:
|
|
@@ -127,4 +133,3 @@ Same as with colors, be sure to have a higher specificity selector if inspector'
|
|
|
127
133
|
* [ ] Filter/search
|
|
128
134
|
* [ ] Persist search in URL or local storage
|
|
129
135
|
* [ ] Collapse/Expand all button
|
|
130
|
-
* [ ] Clean up `inspect-comps.tsx`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GameObj } from "kaplay";
|
|
2
|
+
type CheckboxCompProps = {
|
|
3
|
+
obj: GameObj;
|
|
4
|
+
propName: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const useObjectBoolean: (obj: GameObj, propName: string) => {
|
|
7
|
+
checked: any;
|
|
8
|
+
onChange: (checked: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const BooleanComp: ({ obj, propName }: CheckboxCompProps) => import("preact").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "preact/hooks";
|
|
3
|
+
export const useObjectBoolean = (obj, propName) => {
|
|
4
|
+
const [checked, setChecked] = useState(obj[propName]);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
setChecked(obj[propName]);
|
|
7
|
+
}, [obj[propName]]);
|
|
8
|
+
const onChange = (checked) => {
|
|
9
|
+
setChecked(checked);
|
|
10
|
+
obj[propName] = checked;
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
checked,
|
|
14
|
+
onChange,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export const BooleanComp = ({ obj, propName }) => {
|
|
18
|
+
const id = `${propName}-${obj.id}`;
|
|
19
|
+
const { checked, onChange } = useObjectBoolean(obj, propName);
|
|
20
|
+
return (_jsxs("div", { class: "game-object__comps-row", children: [_jsx("label", { for: id, children: _jsx("b", { children: propName }) }), _jsx("div", { children: _jsx("input", { id: id, type: "checkbox", checked: checked, onChange: (e) => onChange(e.target.checked) }) })] }));
|
|
21
|
+
};
|
|
@@ -13,5 +13,15 @@ export const Breadcrumbs = ({ obj, setRenderRoot }) => {
|
|
|
13
13
|
});
|
|
14
14
|
parent = parent.parent;
|
|
15
15
|
}
|
|
16
|
+
// TODO for destroyed objects always add root as a breadcrumb
|
|
17
|
+
// if (breadcrumbs.length === 0) {
|
|
18
|
+
// const root = k.getTreeRoot()
|
|
19
|
+
// breadcrumbs.push({
|
|
20
|
+
// id: root.id,
|
|
21
|
+
// tags: root.tags,
|
|
22
|
+
// compsLabel: root.compsLabel,
|
|
23
|
+
// object: root,
|
|
24
|
+
// });
|
|
25
|
+
// }
|
|
16
26
|
return (_jsxs("div", { class: "breadcrumbs", children: ["Back to", breadcrumbs.map((breadcrumb) => (_jsxs("button", { class: "ki-btn", onClick: () => setRenderRoot(breadcrumb.object), children: ["ID ", breadcrumb.id, ": ", breadcrumb.tags || breadcrumb.compsLabel] })))] }));
|
|
17
27
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
export const ChildObject = ({ className = "", obj }) => {
|
|
3
|
+
return (_jsxs("div", { class: className, children: ["game object", " ", _jsx("button", { class: "ki-btn", onClick: () => console.log(obj), children: "log" })] }));
|
|
4
|
+
};
|
|
@@ -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-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
|
+
};
|
|
@@ -9,4 +9,4 @@ export interface GameObjectProps {
|
|
|
9
9
|
shouldDrawInspect: boolean;
|
|
10
10
|
k: KAPLAYCtxType;
|
|
11
11
|
}
|
|
12
|
-
export declare const GameObject: ({ obj, className, isExpanded: isExpandedExternal, isRenderRoot, setRenderRoot, shouldDrawInspect, k, }: GameObjectProps) => import("preact").JSX.Element;
|
|
12
|
+
export declare const GameObject: ({ obj, className, isExpanded: isExpandedExternal, isRenderRoot, setRenderRoot, shouldDrawInspect, k, }: GameObjectProps) => import("preact").JSX.Element | null;
|
|
@@ -5,6 +5,7 @@ import { cx } from "../lib/cx";
|
|
|
5
5
|
import { drawBoundingBox } from "../lib/draw-bbox";
|
|
6
6
|
import { getObjectInfo } from "../lib/get-object-info";
|
|
7
7
|
import { Breadcrumbs } from "./breadcrumbs";
|
|
8
|
+
import { BooleanComp } from "./boolean-comp";
|
|
8
9
|
export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal = false, isRenderRoot, setRenderRoot, shouldDrawInspect, k, }) => {
|
|
9
10
|
const [isExpanded, setIsExpanded] = useState(isExpandedExternal);
|
|
10
11
|
const updateControllers = useRef([]);
|
|
@@ -13,24 +14,20 @@ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal
|
|
|
13
14
|
const isObjectDestroyed = !obj.exists() && !isRootObject;
|
|
14
15
|
const showExpandTree = obj.children.length > 0;
|
|
15
16
|
const hasChildren = obj.children.length > 0;
|
|
17
|
+
const hasSize = typeof obj.width === "number" && typeof obj.height === "number";
|
|
18
|
+
const isDrawController = compsData.length === 1 && compsData[0].tag === "draw";
|
|
16
19
|
const isInspecting = isRenderRoot && obj.id !== 0;
|
|
17
20
|
const cancelUpdateControllers = useCallback(() => {
|
|
18
21
|
updateControllers.current.forEach((controller) => controller.cancel());
|
|
19
22
|
updateControllers.current = [];
|
|
20
23
|
}, []);
|
|
21
|
-
const drawInspect = useCallback((obj
|
|
24
|
+
const drawInspect = useCallback((obj) => {
|
|
22
25
|
if (!obj.hidden) {
|
|
23
|
-
const updateController =
|
|
24
|
-
// Kaplay calls drawInspect on all of the children, no need to call it again
|
|
25
|
-
if (!isChild) {
|
|
26
|
-
obj.drawInspect();
|
|
27
|
-
}
|
|
26
|
+
const updateController = k.onDraw(() => {
|
|
28
27
|
drawBoundingBox(obj, k);
|
|
28
|
+
obj.drawInspect();
|
|
29
29
|
});
|
|
30
30
|
updateControllers.current.push(updateController);
|
|
31
|
-
obj.children.forEach((child) => {
|
|
32
|
-
drawInspect(child, true);
|
|
33
|
-
});
|
|
34
31
|
}
|
|
35
32
|
}, []);
|
|
36
33
|
useEffect(() => {
|
|
@@ -47,12 +44,15 @@ export const GameObject = ({ obj, className = "", isExpanded: isExpandedExternal
|
|
|
47
44
|
drawInspect(obj);
|
|
48
45
|
}
|
|
49
46
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
// In Kaplay, onDraw and onUpdate are also game objects
|
|
48
|
+
// For now, I disabled displaying draw objects,
|
|
49
|
+
// mostly because inspector is adding them on hover, making the list jump around
|
|
50
|
+
if (isDrawController) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
53
|
return (_jsxs("div", { class: cx("game-object", className, {
|
|
54
54
|
"game-object--no-children": !hasChildren,
|
|
55
|
-
}), children: [isInspecting && _jsx(Breadcrumbs, { setRenderRoot: setRenderRoot, obj: obj }), _jsxs("div", { class: "game-object__content", onMouseEnter: handleMouseEnter, onMouseLeave:
|
|
55
|
+
}), 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
56
|
"game-object__header--expandable": showExpandTree,
|
|
57
|
-
}), onClick: handleToggleClick, children: [_jsx(
|
|
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
58
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JSX } from "preact/jsx-runtime";
|
|
2
|
+
export type HoldButtonProps = {
|
|
3
|
+
children?: JSX.Element | string | number;
|
|
4
|
+
className?: string;
|
|
5
|
+
onClickAndHold: () => void;
|
|
6
|
+
interval?: number;
|
|
7
|
+
startRepeatingDelay?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const HoldButton: ({ children, className, onClickAndHold, interval, startRepeatingDelay, }: HoldButtonProps) => JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
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);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
9
|
+
return () => {
|
|
10
|
+
clearTimeout(timeoutRef.current);
|
|
11
|
+
clearInterval(intervalRef.current);
|
|
12
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
13
|
+
};
|
|
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 }));
|
|
33
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
1
|
+
type IconProps = {
|
|
2
|
+
className?: string;
|
|
3
|
+
};
|
|
4
|
+
export declare const PlusIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
5
|
+
export declare const MinusIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
6
|
+
export declare const ArrowUpIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
7
|
+
export declare const ArrowDownIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
8
|
+
export declare const ArrowLeftIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
9
|
+
export declare const ArrowRightIcon: ({ className }: IconProps) => import("preact").JSX.Element;
|
|
10
|
+
export {};
|
package/dist/components/icons.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
-
export const PlusIcon = () => {
|
|
3
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", children: _jsxs("g", { fill: "none", "stroke-width": "1.25", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: [_jsx("rect", { width: "14", height: "14", x: "1", y: "1", rx: "4" }), _jsx("path", { d: "M 8 4 V 12 M 4 8 H12" })] }) }));
|
|
2
|
+
export const PlusIcon = ({ className = "" }) => {
|
|
3
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsxs("g", { fill: "none", "stroke-width": "1.25", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: [_jsx("rect", { width: "14", height: "14", x: "1", y: "1", rx: "4" }), _jsx("path", { d: "M 8 4 V 12 M 4 8 H12" })] }) }));
|
|
4
4
|
};
|
|
5
|
-
export const MinusIcon = () => {
|
|
6
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsxs("g", { fill: "none", "stroke-width": "1.25", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: [_jsx("rect", { width: "14", height: "14", x: "1", y: "1", rx: "4" }), _jsx("path", { d: "M 4 8 H 12" })] }) }));
|
|
5
|
+
export const MinusIcon = ({ className = "" }) => {
|
|
6
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsxs("g", { fill: "none", "stroke-width": "1.25", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: [_jsx("rect", { width: "14", height: "14", x: "1", y: "1", rx: "4" }), _jsx("path", { d: "M 4 8 H 12" })] }) }));
|
|
7
7
|
};
|
|
8
|
-
export const ArrowUpIcon = () => {
|
|
9
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 3 10 L 8 6 L 13 10" }) }) }));
|
|
8
|
+
export const ArrowUpIcon = ({ className = "" }) => {
|
|
9
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 3 10 L 8 6 L 13 10" }) }) }));
|
|
10
10
|
};
|
|
11
|
-
export const ArrowDownIcon = () => {
|
|
12
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 3 6 L 8 10 L 13 6" }) }) }));
|
|
11
|
+
export const ArrowDownIcon = ({ className = "" }) => {
|
|
12
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 3 6 L 8 10 L 13 6" }) }) }));
|
|
13
13
|
};
|
|
14
|
-
export const ArrowLeftIcon = () => {
|
|
15
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 10 3 L 6 8 L 10 13" }) }) }));
|
|
14
|
+
export const ArrowLeftIcon = ({ className = "" }) => {
|
|
15
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 10 3 L 6 8 L 10 13" }) }) }));
|
|
16
16
|
};
|
|
17
|
-
export const ArrowRightIcon = () => {
|
|
18
|
-
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 6 3 L 10 8 L 6 13" }) }) }));
|
|
17
|
+
export const ArrowRightIcon = ({ className = "" }) => {
|
|
18
|
+
return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", class: className, children: _jsx("g", { fill: "none", stroke: "currentColor", "stroke-linejoin": "round", "stroke-linecap": "round", children: _jsx("path", { d: "M 6 3 L 10 8 L 6 13" }) }) }));
|
|
19
19
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "preact/hooks";
|
|
3
3
|
import { GameObject } from "./game-object";
|
|
4
|
+
import { useObjectBoolean } from "./boolean-comp";
|
|
4
5
|
const INTERVAL_OPTIONS = [
|
|
5
6
|
{ value: 100, label: "100ms" },
|
|
6
7
|
{ value: 250, label: "250ms" },
|
|
@@ -13,6 +14,7 @@ export const Inspector = ({ initUpdateTimeout = 250, isVisibleOnLoad = true, ini
|
|
|
13
14
|
const [root, setRoot] = useState(k.getTreeRoot());
|
|
14
15
|
const [renderIndex, setRenderIndex] = useState(0);
|
|
15
16
|
const [isVisible, setIsVisible] = useState(isVisibleOnLoad);
|
|
17
|
+
const paused = useObjectBoolean(k.getTreeRoot(), "paused");
|
|
16
18
|
// Force re-render every updateTimeout milliseconds
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
const interval = setInterval(() => {
|
|
@@ -20,15 +22,15 @@ export const Inspector = ({ initUpdateTimeout = 250, isVisibleOnLoad = true, ini
|
|
|
20
22
|
}, updateTimeout);
|
|
21
23
|
return () => clearInterval(interval);
|
|
22
24
|
}, [renderIndex, updateTimeout]);
|
|
23
|
-
const handlePauseClick = () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
25
|
+
// const handlePauseClick = () => {
|
|
26
|
+
// const root = k.getTreeRoot();
|
|
27
|
+
// root.paused = !root.paused;
|
|
28
|
+
// };
|
|
27
29
|
const toggleVisibility = () => {
|
|
28
30
|
setIsVisible(!isVisible);
|
|
29
31
|
};
|
|
30
32
|
if (!isVisible) {
|
|
31
33
|
return (_jsx("button", { class: "ki-btn k-inspector__show", onClick: toggleVisibility, children: "Show Inspector" }));
|
|
32
34
|
}
|
|
33
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { class: "k-inspector__header", children: [_jsx("button", { class: "ki-btn", onClick:
|
|
35
|
+
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", _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: _jsx(GameObject, { k: k, className: "game-object--root", obj: root, setRenderRoot: setRoot, shouldDrawInspect: shouldDrawInspect, isExpanded: true, isRenderRoot: true }) })] }));
|
|
34
36
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { roundToDecimal } from "../lib/round-to-decimal";
|
|
3
|
+
import { HoldButton } from "./hold-button";
|
|
3
4
|
import { ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, } from "./icons";
|
|
4
5
|
export const PositionControls = ({ obj }) => {
|
|
5
6
|
if (!obj.pos) {
|
|
6
7
|
return null;
|
|
7
8
|
}
|
|
8
|
-
return (_jsxs("div", { class: "pos-controls", children: [_jsx(
|
|
9
|
+
return (_jsxs("div", { class: "pos-controls", children: [_jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => (obj.pos.x -= 1), children: _jsx(ArrowLeftIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => (obj.pos.x += 1), 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.pos.y -= 1), children: _jsx(ArrowUpIcon, {}) }), _jsx(HoldButton, { className: "ki-btn", onClickAndHold: () => (obj.pos.y += 1), children: _jsx(ArrowDownIcon, {}) })] }));
|
|
9
10
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
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,9 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "preact/hooks";
|
|
2
3
|
export const TextControls = ({ obj }) => {
|
|
4
|
+
const [text, setText] = useState(obj.text);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
setText(obj.text);
|
|
7
|
+
}, [obj.text]);
|
|
3
8
|
if (typeof obj.text !== "string") {
|
|
4
9
|
return null;
|
|
5
10
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 }) }));
|
|
9
16
|
};
|
package/dist/lib/draw-bbox.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
+
const anchorMap = {
|
|
2
|
+
topleft: [-1, -1],
|
|
3
|
+
top: [0, -1],
|
|
4
|
+
topright: [1, -1],
|
|
5
|
+
left: [-1, 0],
|
|
6
|
+
center: [0, 0],
|
|
7
|
+
right: [1, 0],
|
|
8
|
+
botleft: [-1, 1],
|
|
9
|
+
bot: [0, 1],
|
|
10
|
+
botright: [1, 1],
|
|
11
|
+
};
|
|
1
12
|
export const drawBoundingBox = (obj, k) => {
|
|
2
13
|
if (obj.renderArea) {
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
if (anchor
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
else if (anchor.includes("right")) {
|
|
11
|
-
offset.x = rect.width;
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
offset.x = rect.width / 2;
|
|
15
|
-
}
|
|
16
|
-
if (anchor.includes("top")) {
|
|
17
|
-
offset.y = 0;
|
|
18
|
-
}
|
|
19
|
-
else if (anchor.includes("bot")) {
|
|
20
|
-
offset.y = rect.height;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
offset.y = rect.height / 2;
|
|
14
|
+
const localArea = obj.renderArea();
|
|
15
|
+
const transform = obj.transform.clone();
|
|
16
|
+
let anchor = obj.anchor || "topleft";
|
|
17
|
+
if (localArea && obj.anchor !== "topleft") {
|
|
18
|
+
if (typeof anchor === "string") {
|
|
19
|
+
const coords = anchorMap[anchor];
|
|
20
|
+
anchor = new k.Vec2(coords[0], coords[1]);
|
|
24
21
|
}
|
|
22
|
+
const offset = anchor
|
|
23
|
+
.add(1, 1)
|
|
24
|
+
.scale(-0.5 * localArea.width, -0.5 * localArea.height);
|
|
25
|
+
transform.translateSelfV(offset);
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
offset.y = (anchor.y * rect.height + 1) / 2 + rect.height / 2;
|
|
29
|
-
}
|
|
30
|
-
rect.pos = rect.pos.sub(offset);
|
|
27
|
+
const worldArea = localArea.transform(transform);
|
|
28
|
+
const worldBBox = worldArea.bbox();
|
|
31
29
|
k.drawRect({
|
|
32
|
-
|
|
30
|
+
pos: worldBBox.pos,
|
|
31
|
+
width: worldBBox.width,
|
|
32
|
+
height: worldBBox.height,
|
|
33
33
|
fill: false,
|
|
34
34
|
outline: {
|
|
35
35
|
width: 1,
|
|
@@ -38,4 +38,9 @@ export const drawBoundingBox = (obj, k) => {
|
|
|
38
38
|
},
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
obj.children.forEach((child) => {
|
|
42
|
+
if (!child.hidden) {
|
|
43
|
+
drawBoundingBox(child, k);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
41
46
|
};
|
|
@@ -2,11 +2,15 @@ import { jsx as _jsx } from "preact/jsx-runtime";
|
|
|
2
2
|
import { stringify } from "./stringify";
|
|
3
3
|
import { PositionControls } from "../components/position-controls";
|
|
4
4
|
import { TextControls } from "../components/text-controls";
|
|
5
|
-
import {
|
|
5
|
+
import { SpriteControls } from "../components/sprite-controls";
|
|
6
|
+
import { Color } from "../components/color-controls";
|
|
7
|
+
import { ChildObject } from "../components/child-object";
|
|
8
|
+
import { isGameObj } from "./is-game-obj";
|
|
6
9
|
const componentMap = {
|
|
7
10
|
pos: PositionControls,
|
|
8
11
|
text: TextControls,
|
|
9
|
-
sprite:
|
|
12
|
+
sprite: SpriteControls,
|
|
13
|
+
color: Color,
|
|
10
14
|
};
|
|
11
15
|
export const inspectComps = (obj) => {
|
|
12
16
|
const object = obj;
|
|
@@ -30,6 +34,15 @@ export const inspectComps = (obj) => {
|
|
|
30
34
|
value: value ? value.replace(`${tag}: `, "") : "",
|
|
31
35
|
});
|
|
32
36
|
}
|
|
37
|
+
else {
|
|
38
|
+
data.push({
|
|
39
|
+
tag,
|
|
40
|
+
// Commented out on purpose
|
|
41
|
+
// For now, only the name of the component is shown,
|
|
42
|
+
// until I try it out and figure if it would be useful to display the full component state
|
|
43
|
+
// value: stringify(comp),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
33
46
|
}
|
|
34
47
|
for (const [i, comp] of object._anonymousCompStates.entries()) {
|
|
35
48
|
if (comp.inspect) {
|
|
@@ -46,6 +59,12 @@ export const inspectComps = (obj) => {
|
|
|
46
59
|
value: "function",
|
|
47
60
|
});
|
|
48
61
|
}
|
|
62
|
+
else if (isGameObj(value)) {
|
|
63
|
+
data.push({
|
|
64
|
+
tag: key,
|
|
65
|
+
value: _jsx(ChildObject, { obj: value }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
49
68
|
else if (typeof value === "object") {
|
|
50
69
|
data.push({
|
|
51
70
|
tag: key,
|
package/dist/lib/stringify.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
import { isGameObj } from "./is-game-obj";
|
|
1
2
|
export const stringify = (obj, maxDepth = 1, currentDepth = 0) => {
|
|
2
|
-
if (typeof obj !== "object"
|
|
3
|
-
return
|
|
3
|
+
if (typeof obj !== "object") {
|
|
4
|
+
return JSON.stringify(obj);
|
|
4
5
|
}
|
|
5
6
|
const lines = Object.entries(obj).map(([key, value]) => {
|
|
6
7
|
if (typeof value === "function") {
|
|
7
8
|
return `${key}: function`;
|
|
8
9
|
}
|
|
9
10
|
else if (typeof value === "object") {
|
|
11
|
+
if (isGameObj(value)) {
|
|
12
|
+
return `${key}: [GameObj]`;
|
|
13
|
+
}
|
|
10
14
|
if (currentDepth < maxDepth) {
|
|
11
15
|
return `${key}: ${stringify(value, maxDepth, currentDepth + 1)}`;
|
|
12
16
|
}
|
|
13
17
|
else {
|
|
14
|
-
return `${key}: [Object
|
|
18
|
+
return `${key}: [Object]`;
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
else {
|
package/dist/styles.css
CHANGED
|
@@ -197,6 +197,11 @@
|
|
|
197
197
|
width: calc(100% + 0.25rem);
|
|
198
198
|
border-radius: 4px;
|
|
199
199
|
outline-offset: 0;
|
|
200
|
+
position: sticky;
|
|
201
|
+
/* TODO add sticky header and sticky side buttons */
|
|
202
|
+
/*z-index: 1;
|
|
203
|
+
top: 2.625rem;
|
|
204
|
+
background-color: var(--ki-bg);*/
|
|
200
205
|
|
|
201
206
|
svg rect {
|
|
202
207
|
fill: var(--ki-bg);
|
|
@@ -326,14 +331,49 @@
|
|
|
326
331
|
outline: none;
|
|
327
332
|
border-color: var(--ki-primary);
|
|
328
333
|
}
|
|
329
|
-
}
|
|
330
334
|
|
|
331
|
-
/*
|
|
335
|
+
/* Color */
|
|
336
|
+
|
|
337
|
+
.color-controls {
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
gap: 0.5rem;
|
|
341
|
+
width: fit-content;
|
|
342
|
+
min-width: 12rem;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.color-controls__swatch {
|
|
346
|
+
width: 2rem;
|
|
347
|
+
height: 2rem;
|
|
348
|
+
border-radius: 8px;
|
|
349
|
+
border: 1px solid var(--ki-border-light);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.color-controls__slider {
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
gap: 0.25rem;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.color-controls__slider-input--r {
|
|
359
|
+
accent-color: red;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.color-controls__slider-input--g {
|
|
363
|
+
accent-color: green;
|
|
364
|
+
}
|
|
332
365
|
|
|
333
|
-
.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
366
|
+
.color-controls__slider-input--b {
|
|
367
|
+
accent-color: blue;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* Breadcrumbs */
|
|
371
|
+
|
|
372
|
+
.breadcrumbs {
|
|
373
|
+
font-size: 0.75rem;
|
|
374
|
+
display: flex;
|
|
375
|
+
margin-bottom: 0.5rem;
|
|
376
|
+
gap: 0.5rem;
|
|
377
|
+
align-items: center;
|
|
378
|
+
}
|
|
339
379
|
}
|
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.
|
|
5
|
+
"version": "0.1.2",
|
|
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.24"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"preact": "^10.28.0"
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
|
-
export const Sprite = ({ obj }) => {
|
|
3
|
-
const object = obj;
|
|
4
|
-
const animation = object.getCurAnim();
|
|
5
|
-
return (_jsxs("div", { class: "sprite", 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
|
-
};
|