@nice2dev/ui-graphics 1.0.2 → 1.0.4
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/dist/cjs/design/design-tools.js +26 -0
- package/dist/cjs/design/design-tools.js.map +1 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pixel/pixel-pro.js +67 -0
- package/dist/cjs/pixel/pixel-pro.js.map +1 -0
- package/dist/cjs/ui/dist/index.js +26325 -18992
- package/dist/cjs/ui/dist/index.js.map +1 -1
- package/dist/esm/animation/AnimationEditor.js +5 -5
- package/dist/esm/animation/Audience.js +2 -2
- package/dist/esm/core/LocalUI.js +4 -4
- package/dist/esm/design/design-tools.js +24 -0
- package/dist/esm/design/design-tools.js.map +1 -0
- package/dist/esm/game/GameAsset2dEditor.js +19 -19
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pixel/PixelEditor.js +3 -3
- package/dist/esm/pixel/PixelEditorMenuBar.js +2 -2
- package/dist/esm/pixel/PixelEditorRightPanel.js +3 -3
- package/dist/esm/pixel/PixelEditorTimeline.js +2 -2
- package/dist/esm/pixel/PixelEditorToolbar.js +2 -2
- package/dist/esm/pixel/SpriteGeneratorPanel.js +8 -8
- package/dist/esm/pixel/pixel-pro.js +65 -0
- package/dist/esm/pixel/pixel-pro.js.map +1 -0
- package/dist/esm/ui/dist/index.js +25903 -18591
- package/dist/esm/ui/dist/index.js.map +1 -1
- package/dist/esm/vector/VectorEditor.js +3 -3
- package/dist/esm/vector/VectorEditorMenuBar.js +3 -3
- package/dist/esm/vector/VectorEditorRightPanel.js +13 -13
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
-
import { NiceTextInput as
|
|
3
|
+
import { NiceTextInput as _t, NiceSlider as Ar, NiceButton as Oe, NiceTextArea as ms, NiceSelect as nn, NiceColorPicker as Nn } from '../ui/dist/index.js';
|
|
4
4
|
import { useNiceTranslation } from '../core/i18n.js';
|
|
5
5
|
import NiceAnimatedPerson from './AnimatedPerson.js';
|
|
6
6
|
import { DEFAULT_CHARACTER, VARIANTS } from './characterTypes.js';
|
|
@@ -20,18 +20,18 @@ function ColorInputs({ value, onChange, }) {
|
|
|
20
20
|
const base = clampPalette(value);
|
|
21
21
|
const count = Math.min(3, Math.max(1, (_a = value === null || value === void 0 ? void 0 : value.length) !== null && _a !== void 0 ? _a : 1));
|
|
22
22
|
const view = base.slice(0, count);
|
|
23
|
-
return (jsxs("div", { className: "ntd-ae-color-inputs", children: [jsx("div", { className: "ntd-ae-colors", children: view.map((col, i) => (jsxs("label", { className: "ntd-ae-color-label", children: ["C", i + 1, jsx(
|
|
23
|
+
return (jsxs("div", { className: "ntd-ae-color-inputs", children: [jsx("div", { className: "ntd-ae-colors", children: view.map((col, i) => (jsxs("label", { className: "ntd-ae-color-label", children: ["C", i + 1, jsx(Nn, { value: col, onChange: (color) => {
|
|
24
24
|
const next = [...view];
|
|
25
25
|
next[i] = color;
|
|
26
26
|
onChange(next);
|
|
27
|
-
} })] }, i))) }), jsxs("div", { className: "ntd-ae-color-btns", children: [jsx(
|
|
27
|
+
} })] }, i))) }), jsxs("div", { className: "ntd-ae-color-btns", children: [jsx(Oe, { variant: "ghost", size: "sm", disabled: count <= 1, onClick: () => onChange(view.slice(0, Math.max(1, count - 1))), children: "-" }), jsx(Oe, { variant: "ghost", size: "sm", disabled: count >= 3, onClick: () => onChange([...view, view[view.length - 1] || "var(--anim-default-black, #000000)"]), children: "+" })] })] }));
|
|
28
28
|
}
|
|
29
29
|
function Row({ title, children }) {
|
|
30
30
|
return (jsxs("div", { className: "ntd-ae-row", children: [jsx("div", { className: "ntd-ae-row-title", children: title }), jsx("div", { className: "ntd-ae-row-content", children: children })] }));
|
|
31
31
|
}
|
|
32
32
|
function FeatureEditor({ title, feature, variants, onChange, }) {
|
|
33
33
|
const options = variants.map((v) => ({ value: v, label: v }));
|
|
34
|
-
return (jsxs(Row, { title: title, children: [jsx(
|
|
34
|
+
return (jsxs(Row, { title: title, children: [jsx(nn, { className: "ntd-ae-select", value: feature.variant, onChange: (val) => onChange({ ...feature, variant: val }), options: options }), jsx(ColorInputs, { value: feature.colors, onChange: (colors) => onChange({ ...feature, colors }) })] }));
|
|
35
35
|
}
|
|
36
36
|
/* ---------- component ---------- */
|
|
37
37
|
function NiceAnimationEditor({ initialConfig, onChange, storageKey = STORAGE_KEY, onNotify, showJsonPanel = true, showPreview = true, className, style, }) {
|
|
@@ -99,7 +99,7 @@ function NiceAnimationEditor({ initialConfig, onChange, storageKey = STORAGE_KEY
|
|
|
99
99
|
const updateFeature = (key, val) => {
|
|
100
100
|
setCfg((prev) => ({ ...prev, [key]: val }));
|
|
101
101
|
};
|
|
102
|
-
return (jsxs("div", { className: `ntd-animation-editor ${className !== null && className !== void 0 ? className : ""}`, style: style, children: [jsxs("div", { className: "ntd-ae-controls", children: [jsx(Row, { title: t("characterEditor.name", "Name"), children: jsx(
|
|
102
|
+
return (jsxs("div", { className: `ntd-animation-editor ${className !== null && className !== void 0 ? className : ""}`, style: style, children: [jsxs("div", { className: "ntd-ae-controls", children: [jsx(Row, { title: t("characterEditor.name", "Name"), children: jsx(_t, { className: "ntd-ae-input", value: cfg.name || "", onChange: (val) => setCfg({ ...cfg, name: val }), placeholder: t("characterEditor.namePlaceholder", "Character name") }) }), jsxs(Row, { title: t("characters.size", "Size"), children: [jsx(Ar, { min: 140, max: 300, value: (_a = cfg.size) !== null && _a !== void 0 ? _a : 180, onChange: (val) => setCfg({ ...cfg, size: val }) }), jsxs("span", { className: "ntd-ae-size-value", children: [(_b = cfg.size) !== null && _b !== void 0 ? _b : 180, "px"] })] }), jsx(FeatureEditor, { title: t("characters.faceShape", "Face"), feature: cfg.face, variants: VARIANTS.face, onChange: (v) => updateFeature("face", v) }), jsx(FeatureEditor, { title: t("characters.hairstyle", "Hair"), feature: cfg.hair, variants: VARIANTS.hair, onChange: (v) => updateFeature("hair", v) }), jsx(FeatureEditor, { title: t("characters.eyes", "Eyes"), feature: cfg.eyes, variants: VARIANTS.eyes, onChange: (v) => updateFeature("eyes", v) }), jsx(FeatureEditor, { title: t("characters.nose", "Nose"), feature: cfg.nose, variants: VARIANTS.nose, onChange: (v) => updateFeature("nose", v) }), jsx(FeatureEditor, { title: t("characters.mouth", "Mouth"), feature: cfg.mouth, variants: VARIANTS.mouth, onChange: (v) => updateFeature("mouth", v) }), jsx(FeatureEditor, { title: t("characters.clothing", "Outfit"), feature: cfg.outfit, variants: VARIANTS.outfit, onChange: (v) => updateFeature("outfit", v) }), jsx(FeatureEditor, { title: t("characters.headwear", "Headwear"), feature: cfg.headwear, variants: VARIANTS.headwear, onChange: (v) => updateFeature("headwear", v) }), jsx(FeatureEditor, { title: t("characters.prop", "Prop"), feature: cfg.prop, variants: VARIANTS.prop, onChange: (v) => updateFeature("prop", v) }), jsxs("div", { className: "ntd-ae-action-row", children: [jsxs(Oe, { className: "ntd-ae-btn", variant: "ghost", onClick: randomizeColors, children: ["\uD83C\uDFB2 ", t("characterEditor.randomizeColors", "Randomize colors")] }), jsxs(Oe, { className: "ntd-ae-btn", variant: "ghost", onClick: downloadJson, children: ["\u2B07 ", t("characters.downloadJson", "Download JSON")] })] }), showJsonPanel && (jsxs("div", { className: "ntd-ae-json-panel", children: [jsx("div", { className: "ntd-ae-json-label", children: t("characterEditor.jsonSection", "JSON") }), jsx(ms, { className: "ntd-ae-textarea", value: json, onChange: (val) => setJson(val) }), jsxs("div", { className: "ntd-ae-json-actions", children: [jsx(Oe, { className: "ntd-ae-btn", variant: "primary", onClick: applyJson, children: t("characterEditor.applyJson", "Apply JSON") }), jsx(Oe, { className: "ntd-ae-btn", variant: "ghost", onClick: () => navigator.clipboard.writeText(JSON.stringify(cfg, null, 2)), children: t("characterEditor.copyToClipboard", "Copy") })] })] }))] }), showPreview && (jsxs("div", { className: "ntd-ae-preview", children: [jsx("div", { className: "ntd-ae-preview-label", children: t("characterEditor.preview", "Preview") }), jsx(NiceAnimatedPerson, { character: cfg, score: 10, startPose: "idle" })] }))] }));
|
|
103
103
|
}
|
|
104
104
|
/** @deprecated Use NiceAnimationEditor */
|
|
105
105
|
const AnimationEditor = NiceAnimationEditor;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { NiceButton as
|
|
3
|
+
import { NiceButton as Oe } from '../ui/dist/index.js';
|
|
4
4
|
import NiceAnimatedPerson from './AnimatedPerson.js';
|
|
5
5
|
import { DEFAULT_CHARACTER } from './characterTypes.js';
|
|
6
6
|
import { runRounds, seq } from './choreoDSL.js';
|
|
@@ -51,7 +51,7 @@ const NiceAudience = ({ count = 36, columns = 12 }) => {
|
|
|
51
51
|
display: "grid",
|
|
52
52
|
gap: 8,
|
|
53
53
|
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
54
|
-
}, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: "flex", gap: 8 }, children: [jsx(
|
|
54
|
+
}, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: "flex", gap: 8 }, children: [jsx(Oe, { variant: "ghost", size: "sm", onClick: wave, children: "Wave" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: cheer, children: "Cheer" })] })] }));
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export { NiceAudience as default };
|
package/dist/esm/core/LocalUI.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
|
-
import { NiceButton as
|
|
3
|
+
import { NiceButton as Oe, NiceSlider as Ar, NiceTextArea as ms } from '../ui/dist/index.js';
|
|
4
4
|
|
|
5
5
|
const variantMap = {
|
|
6
6
|
"dark": "secondary",
|
|
@@ -15,7 +15,7 @@ const sizeMap = {
|
|
|
15
15
|
"md": "md",
|
|
16
16
|
};
|
|
17
17
|
const Button = ({ variant = "dark", size = "md", className = "", children, disabled, onClick, title, style, }) => {
|
|
18
|
-
return (jsx(
|
|
18
|
+
return (jsx(Oe, { variant: variantMap[variant], size: sizeMap[size], className: className, disabled: disabled, onClick: onClick, "aria-label": title, style: style, children: children }));
|
|
19
19
|
};
|
|
20
20
|
const RangeInput = ({ accentColor, style, className = "", value, min = 0, max = 100, step = 1, onChange, onMouseUp, }) => {
|
|
21
21
|
const handleChange = useCallback((val) => {
|
|
@@ -31,7 +31,7 @@ const RangeInput = ({ accentColor, style, className = "", value, min = 0, max =
|
|
|
31
31
|
onMouseUp({});
|
|
32
32
|
}
|
|
33
33
|
}, [onChange, onMouseUp]);
|
|
34
|
-
return (jsx(
|
|
34
|
+
return (jsx(Ar, { value: typeof value === "number" ? value : Number(value) || 0, min: Number(min), max: Number(max), step: Number(step), className: className, style: { ...style, "--nice-accent": accentColor }, onChange: handleChange, showValue: false }));
|
|
35
35
|
};
|
|
36
36
|
const TextArea = ({ className = "", value, onChange, rows, placeholder, disabled, style, }) => {
|
|
37
37
|
const handleChange = useCallback((newValue) => {
|
|
@@ -43,7 +43,7 @@ const TextArea = ({ className = "", value, onChange, rows, placeholder, disabled
|
|
|
43
43
|
onChange(syntheticEvent);
|
|
44
44
|
}
|
|
45
45
|
}, [onChange]);
|
|
46
|
-
return (jsx(
|
|
46
|
+
return (jsx(ms, { className: className, value: typeof value === "string" ? value : String(value !== null && value !== void 0 ? value : ""), onChange: handleChange, rows: rows, placeholder: placeholder, disabled: disabled, style: style }));
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
export { Button, RangeInput, TextArea };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI/UX Design Tools — Wireframes, mockups, prototyping,
|
|
3
|
+
* design tokens, component library, responsive preview,
|
|
4
|
+
* handoff, comments, versioning, Figma/Sketch/XD import, code export.
|
|
5
|
+
*
|
|
6
|
+
* PRO-4.6 — 13 items
|
|
7
|
+
*
|
|
8
|
+
* @module @nice2dev/ui-graphics/design/design-tools
|
|
9
|
+
*/
|
|
10
|
+
const DEVICE_PRESETS = [
|
|
11
|
+
{ name: 'iPhone 15', width: 393, height: 852, devicePixelRatio: 3, type: 'phone' },
|
|
12
|
+
{ name: 'iPhone 15 Pro Max', width: 430, height: 932, devicePixelRatio: 3, type: 'phone' },
|
|
13
|
+
{ name: 'Samsung Galaxy S24', width: 360, height: 780, devicePixelRatio: 3, type: 'phone' },
|
|
14
|
+
{ name: 'Pixel 8', width: 412, height: 915, devicePixelRatio: 2.625, type: 'phone' },
|
|
15
|
+
{ name: 'iPad Air', width: 820, height: 1180, devicePixelRatio: 2, type: 'tablet' },
|
|
16
|
+
{ name: 'iPad Pro 12.9"', width: 1024, height: 1366, devicePixelRatio: 2, type: 'tablet' },
|
|
17
|
+
{ name: 'MacBook Pro 14"', width: 1512, height: 982, devicePixelRatio: 2, type: 'desktop' },
|
|
18
|
+
{ name: 'Desktop 1080p', width: 1920, height: 1080, devicePixelRatio: 1, type: 'desktop' },
|
|
19
|
+
{ name: 'Desktop 4K', width: 3840, height: 2160, devicePixelRatio: 2, type: 'desktop' },
|
|
20
|
+
{ name: 'Apple Watch Ultra', width: 205, height: 251, devicePixelRatio: 2, type: 'watch' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export { DEVICE_PRESETS };
|
|
24
|
+
//# sourceMappingURL=design-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"design-tools.js","sources":["../../../src/design/design-tools.ts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;AAQG;AA2MI,MAAM,cAAc,GAAmB;AAC5C,IAAA,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;AAClF,IAAA,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;AAC1F,IAAA,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;AAC3F,IAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE;AACpF,IAAA,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;AACnF,IAAA,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;AAC1F,IAAA,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;AAC3F,IAAA,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;AAC1F,IAAA,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;AACvF,IAAA,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;;;;;"}
|
|
@@ -2,14 +2,14 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import { useRef, useState, useMemo, useEffect, useCallback } from 'react';
|
|
3
3
|
import styles from './GameAsset2dEditor.module.css.js';
|
|
4
4
|
import { useGameAssetEditor } from './useGameAssetEditor.js';
|
|
5
|
-
import { NiceButton as
|
|
5
|
+
import { NiceButton as Oe, NiceTextInput as _t, NiceSelect as nn, NiceNumberInput as Ri } from '../ui/dist/index.js';
|
|
6
6
|
|
|
7
7
|
/* ────────────────────────────────────────────────────────────────
|
|
8
8
|
MENU BAR
|
|
9
9
|
──────────────────────────────────────────────────────────────── */
|
|
10
10
|
const MenuBar = ({ api }) => {
|
|
11
11
|
const fileInputRef = useRef(null);
|
|
12
|
-
return (jsxs("div", { className: styles.menuBar, children: [jsx("h3", { children: "\uD83C\uDFAE Game Asset Editor" }), jsxs("div", { className: styles.menuGroup, children: [jsx(
|
|
12
|
+
return (jsxs("div", { className: styles.menuBar, children: [jsx("h3", { children: "\uD83C\uDFAE Game Asset Editor" }), jsxs("div", { className: styles.menuGroup, children: [jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.newProject('New Project'), children: "\uD83D\uDCC4 New" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "\uD83D\uDCC2 Open" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.saveProject, children: "\uD83D\uDCBE Save" })] }), jsx("div", { className: styles.spacer }), jsxs("div", { className: styles.menuGroup, children: [jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.undo, disabled: !api.canUndo, children: "\u21A9 Undo" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.redo, disabled: !api.canRedo, children: "\u21AA Redo" })] }), jsx("input", { ref: fileInputRef, type: "file", accept: ".json", style: { display: 'none' }, onChange: e => {
|
|
13
13
|
var _a;
|
|
14
14
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
15
15
|
if (file)
|
|
@@ -27,7 +27,7 @@ const ModeTabs = ({ api }) => {
|
|
|
27
27
|
{ id: 'animation', label: 'Animation', icon: '🎬' },
|
|
28
28
|
{ id: 'effects', label: 'Effects', icon: '✨' },
|
|
29
29
|
];
|
|
30
|
-
return (jsx("div", { className: styles.modeTabs, children: modes.map(mode => (jsxs(
|
|
30
|
+
return (jsx("div", { className: styles.modeTabs, children: modes.map(mode => (jsxs(Oe, { className: `${styles.modeTab} ${api.editorState.mode === mode.id ? styles.active : ''}`, variant: api.editorState.mode === mode.id ? 'primary' : 'ghost', onClick: () => api.setMode(mode.id), children: [mode.icon, " ", mode.label] }, mode.id))) }));
|
|
31
31
|
};
|
|
32
32
|
/* ────────────────────────────────────────────────────────────────
|
|
33
33
|
TILE MAP TOOLBAR
|
|
@@ -42,7 +42,7 @@ const TileMapToolbar = ({ api }) => {
|
|
|
42
42
|
{ id: 'line', icon: '╱', label: 'Line' },
|
|
43
43
|
{ id: 'eyedropper', icon: '💧', label: 'Eyedropper' },
|
|
44
44
|
];
|
|
45
|
-
return (jsxs("div", { className: styles.toolbar, children: [jsx("div", { className: styles.toolGroup, children: tools.map(tool => (jsx(
|
|
45
|
+
return (jsxs("div", { className: styles.toolbar, children: [jsx("div", { className: styles.toolGroup, children: tools.map(tool => (jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.tileMapTool === tool.id ? styles.active : ''}`, variant: api.editorState.tileMapTool === tool.id ? 'primary' : 'ghost', size: "sm", onClick: () => api.setTileMapTool(tool.id), title: tool.label, children: tool.icon }, tool.id))) }), jsx("div", { className: styles.spacer }), jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.showGrid ? styles.active : ''}`, variant: api.editorState.showGrid ? 'primary' : 'ghost', size: "sm", onClick: api.toggleGrid, title: "Toggle Grid", children: "\u229E" }), jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.showCollision ? styles.active : ''}`, variant: api.editorState.showCollision ? 'primary' : 'ghost', size: "sm", onClick: api.toggleCollision, title: "Show Collision", children: "\u25C7" }), jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.snapToGrid ? styles.active : ''}`, variant: api.editorState.snapToGrid ? 'primary' : 'ghost', size: "sm", onClick: api.toggleSnap, title: "Snap to Grid", children: "\u22A1" })] }));
|
|
46
46
|
};
|
|
47
47
|
/* ────────────────────────────────────────────────────────────────
|
|
48
48
|
SPRITE SHEET TOOLBAR
|
|
@@ -50,14 +50,14 @@ const TileMapToolbar = ({ api }) => {
|
|
|
50
50
|
const SpriteSheetToolbar = ({ api }) => {
|
|
51
51
|
const [sliceWidth, setSliceWidth] = useState(16);
|
|
52
52
|
const [sliceHeight, setSliceHeight] = useState(16);
|
|
53
|
-
return (jsxs("div", { className: styles.toolbar, children: [jsxs("div", { className: styles.toolGroup, children: [jsx(
|
|
53
|
+
return (jsxs("div", { className: styles.toolbar, children: [jsxs("div", { className: styles.toolGroup, children: [jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === 'select' ? styles.active : ''}`, variant: api.editorState.spriteSheetTool === 'select' ? 'primary' : 'ghost', size: "sm", onClick: () => api.setSpriteSheetTool('select'), title: "Select", children: "\u2B1A" }), jsx(Oe, { className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === 'slice' ? styles.active : ''}`, variant: api.editorState.spriteSheetTool === 'slice' ? 'primary' : 'ghost', size: "sm", onClick: () => api.setSpriteSheetTool('slice'), title: "Manual Slice", children: "\u2702" })] }), jsx("div", { className: styles.spacer }), jsxs("label", { style: { fontSize: 11, color: '#8892a8' }, children: ["Frame size:", jsx(Ri, { value: sliceWidth, onChange: val => setSliceWidth(val !== null && val !== void 0 ? val : 16), style: { width: 40, margin: '0 4px' }, className: styles.panelInput }), "\u00D7", jsx(Ri, { value: sliceHeight, onChange: val => setSliceHeight(val !== null && val !== void 0 ? val : 16), style: { width: 40, marginLeft: 4 }, className: styles.panelInput })] }), jsx(Oe, { className: styles.btnSecondary, variant: "ghost", onClick: () => api.autoSlice(sliceWidth, sliceHeight), style: { marginLeft: 8 }, children: "Auto Slice" }), jsx(Oe, { className: styles.btnSecondary, variant: "ghost", onClick: api.detectSprites, style: { marginLeft: 4 }, children: "Detect Sprites" })] }));
|
|
54
54
|
};
|
|
55
55
|
/* ────────────────────────────────────────────────────────────────
|
|
56
56
|
TILE PALETTE (LEFT PANEL)
|
|
57
57
|
──────────────────────────────────────────────────────────────── */
|
|
58
58
|
const TilePalette = ({ api }) => {
|
|
59
59
|
const fileInputRef = useRef(null);
|
|
60
|
-
return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Tiles" }), jsx(
|
|
60
|
+
return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Tiles" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "+ Add" })] }), jsx("div", { className: styles.panelContent, children: api.project.tileSets.length === 0 ? (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: "No tiles yet" }), jsx(Oe, { className: styles.btnSecondary, variant: "ghost", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "Import Tiles" })] })) : (jsx("div", { className: styles.tilePalette, children: api.project.tileSets.map(tile => (jsx("div", { className: `${styles.tileItem} ${api.editorState.selectedTileId === tile.id ? styles.selected : ''}`, onClick: () => api.selectTile(tile.id), title: tile.name, children: jsx("img", { src: tile.thumbnail, alt: tile.name }) }, tile.id))) })) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: e => {
|
|
61
61
|
var _a;
|
|
62
62
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
63
63
|
if (file)
|
|
@@ -70,10 +70,10 @@ const TilePalette = ({ api }) => {
|
|
|
70
70
|
──────────────────────────────────────────────────────────────── */
|
|
71
71
|
const LayersPanel = ({ api }) => {
|
|
72
72
|
const activeMap = api.project.tileMaps.find(m => m.id === api.editorState.activeTileMapId);
|
|
73
|
-
return (jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Layers" }), jsx(
|
|
73
|
+
return (jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Layers" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.addLayer, disabled: !activeMap, children: "+ Add" })] }), jsx("div", { className: styles.panelContent, children: activeMap ? (jsx("div", { className: styles.layersList, children: [...activeMap.layers].reverse().map(layer => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.activeLayerId === layer.id ? styles.selected : ''}`, onClick: () => api.selectLayer(layer.id), children: [jsx(Oe, { className: `${styles.layerVisibility} ${!layer.visible ? styles.hidden : ''}`, variant: "ghost", size: "sm", onClick: e => {
|
|
74
74
|
e.stopPropagation();
|
|
75
75
|
api.toggleLayerVisibility(layer.id);
|
|
76
|
-
}, children: layer.visible ? '👁' : '○' }), jsx("span", { className: styles.layerName, children: layer.name }), jsxs("span", { className: styles.layerOpacity, children: [Math.round(layer.opacity * 100), "%"] })] }, layer.id))) })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: "No tile map selected" }) })) }), jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Maps" }), jsx(
|
|
76
|
+
}, children: layer.visible ? '👁' : '○' }), jsx("span", { className: styles.layerName, children: layer.name }), jsxs("span", { className: styles.layerOpacity, children: [Math.round(layer.opacity * 100), "%"] })] }, layer.id))) })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: "No tile map selected" }) })) }), jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Maps" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.createTileMap('New Map', 32, 32), children: "+ New" })] }), jsx("div", { className: styles.panelContent, children: api.project.tileMaps.map(map => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.activeTileMapId === map.id ? styles.selected : ''}`, onClick: () => api.selectTileMap(map.id), children: [jsx("span", { className: styles.layerName, children: map.name }), jsxs("span", { className: styles.layerOpacity, children: [map.width, "\u00D7", map.height] })] }, map.id))) })] }));
|
|
77
77
|
};
|
|
78
78
|
/* ────────────────────────────────────────────────────────────────
|
|
79
79
|
TILE MAP CANVAS
|
|
@@ -161,7 +161,7 @@ const TileMapCanvas = ({ api }) => {
|
|
|
161
161
|
if (isDragging && (api.editorState.tileMapTool === 'brush' || api.editorState.tileMapTool === 'eraser')) {
|
|
162
162
|
handleCanvasClick(e);
|
|
163
163
|
}
|
|
164
|
-
} })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: "Create or select a tile map to start editing" }), jsx(
|
|
164
|
+
} })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: "Create or select a tile map to start editing" }), jsx(Oe, { className: styles.btnPrimary, variant: "primary", onClick: () => api.createTileMap('New Map', 32, 32), children: "Create Tile Map" })] })) }));
|
|
165
165
|
};
|
|
166
166
|
/* ────────────────────────────────────────────────────────────────
|
|
167
167
|
SPRITE SHEET CANVAS
|
|
@@ -229,7 +229,7 @@ const SpriteSheetCanvas = ({ api }) => {
|
|
|
229
229
|
}, [activeSheet, api]);
|
|
230
230
|
return (jsx("div", { className: styles.viewport, children: activeSheet ? (jsx("canvas", { ref: canvasRef, className: styles.viewportCanvas, style: {
|
|
231
231
|
transform: `translate(-50%, -50%) scale(${api.editorState.zoom})`,
|
|
232
|
-
}, onClick: handleFrameClick })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: "Import a sprite sheet to start" }), jsx(
|
|
232
|
+
}, onClick: handleFrameClick })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: "Import a sprite sheet to start" }), jsx(Oe, { className: styles.btnPrimary, variant: "primary", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "Import Sprite Sheet" }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: e => {
|
|
233
233
|
var _a;
|
|
234
234
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
235
235
|
if (file)
|
|
@@ -242,7 +242,7 @@ const SpriteSheetCanvas = ({ api }) => {
|
|
|
242
242
|
──────────────────────────────────────────────────────────────── */
|
|
243
243
|
const SpriteSheetsList = ({ api }) => {
|
|
244
244
|
const fileInputRef = useRef(null);
|
|
245
|
-
return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Sprite Sheets" }), jsx(
|
|
245
|
+
return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Sprite Sheets" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "+ Import" })] }), jsx("div", { className: styles.panelContent, children: api.project.spriteSheets.length === 0 ? (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: "No sprite sheets" }) })) : (jsx("div", { className: styles.layersList, children: api.project.spriteSheets.map(sheet => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.activeSpriteSheetId === sheet.id ? styles.selected : ''}`, onClick: () => api.selectSpriteSheet(sheet.id), children: [jsx("span", { className: styles.layerName, children: sheet.name }), jsxs("span", { className: styles.layerOpacity, children: [sheet.frames.length, " frames"] })] }, sheet.id))) })) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: e => {
|
|
246
246
|
var _a;
|
|
247
247
|
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
248
248
|
if (file)
|
|
@@ -263,7 +263,7 @@ const FramesPanel = ({ api }) => {
|
|
|
263
263
|
setAnimName('');
|
|
264
264
|
}
|
|
265
265
|
}, [animName, api]);
|
|
266
|
-
return (jsxs("div", { className: styles.rightPanel, children: [jsx("div", { className: styles.panelHeader, children: jsxs("span", { children: ["Frames (", (_a = activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.length) !== null && _a !== void 0 ? _a : 0, ")"] }) }), jsx("div", { className: styles.panelContent, style: { maxHeight: 200 }, children: activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.map(frame => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.selectedFrameIds.includes(frame.id) ? styles.selected : ''}`, onClick: () => api.selectFrames([frame.id]), children: [jsx("span", { className: styles.layerName, children: frame.name }), jsxs("span", { className: styles.layerOpacity, children: [frame.width, "\u00D7", frame.height] })] }, frame.id))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Animations" }) }), jsxs("div", { className: styles.panelContent, children: [api.editorState.selectedFrameIds.length > 0 && (jsxs("div", { className: styles.panelSection, children: [jsx(
|
|
266
|
+
return (jsxs("div", { className: styles.rightPanel, children: [jsx("div", { className: styles.panelHeader, children: jsxs("span", { children: ["Frames (", (_a = activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.length) !== null && _a !== void 0 ? _a : 0, ")"] }) }), jsx("div", { className: styles.panelContent, style: { maxHeight: 200 }, children: activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.map(frame => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.selectedFrameIds.includes(frame.id) ? styles.selected : ''}`, onClick: () => api.selectFrames([frame.id]), children: [jsx("span", { className: styles.layerName, children: frame.name }), jsxs("span", { className: styles.layerOpacity, children: [frame.width, "\u00D7", frame.height] })] }, frame.id))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Animations" }) }), jsxs("div", { className: styles.panelContent, children: [api.editorState.selectedFrameIds.length > 0 && (jsxs("div", { className: styles.panelSection, children: [jsx(_t, { className: styles.panelInput, placeholder: "Animation name...", value: animName, onChange: val => setAnimName(val) }), jsxs(Oe, { className: styles.btnPrimary, variant: "primary", onClick: createAnimationFromSelection, style: { marginTop: 8, width: '100%' }, disabled: !animName, children: ["Create from ", api.editorState.selectedFrameIds.length, " frames"] })] })), activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.animations.map(anim => (jsxs("div", { className: styles.layerItem, children: [jsx("span", { className: styles.layerName, children: anim.name }), jsxs("span", { className: styles.layerOpacity, children: [anim.frameIds.length, "f"] }), jsx(Oe, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeAnimation(anim.id), children: "\u00D7" })] }, anim.id)))] })] }));
|
|
267
267
|
};
|
|
268
268
|
/* ────────────────────────────────────────────────────────────────
|
|
269
269
|
ANIMATION STATE MACHINE
|
|
@@ -282,7 +282,7 @@ const AnimationStateMachineView = ({ api }) => {
|
|
|
282
282
|
}
|
|
283
283
|
return result;
|
|
284
284
|
}, [api.project.spriteSheets]);
|
|
285
|
-
return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "State Machines" }), jsx("button", { className: styles.menuBtn, onClick: () => api.createStateMachine('New State Machine'), children: "+ New" })] }), jsx("div", { className: styles.panelContent, style: { maxHeight: 150 }, children: api.project.stateMachines.map(sm => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.activeStateMachineId === sm.id ? styles.selected : ''}`, onClick: () => api.selectStateMachine(sm.id), children: [jsx("span", { className: styles.layerName, children: sm.name }), jsxs("span", { className: styles.layerOpacity, children: [sm.states.length, " states"] })] }, sm.id))) }), activeSM && (jsxs(Fragment, { children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "States" }) }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.panelSection, children: [jsx(
|
|
285
|
+
return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "State Machines" }), jsx("button", { className: styles.menuBtn, onClick: () => api.createStateMachine('New State Machine'), children: "+ New" })] }), jsx("div", { className: styles.panelContent, style: { maxHeight: 150 }, children: api.project.stateMachines.map(sm => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.activeStateMachineId === sm.id ? styles.selected : ''}`, onClick: () => api.selectStateMachine(sm.id), children: [jsx("span", { className: styles.layerName, children: sm.name }), jsxs("span", { className: styles.layerOpacity, children: [sm.states.length, " states"] })] }, sm.id))) }), activeSM && (jsxs(Fragment, { children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "States" }) }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.panelSection, children: [jsx(_t, { className: styles.panelInput, placeholder: "State name...", value: newStateName, onChange: val => setNewStateName(val) }), allAnimations.length > 0 && newStateName && (jsx(nn, { className: styles.panelInput, style: { marginTop: 4 }, onChange: val => {
|
|
286
286
|
if (val) {
|
|
287
287
|
api.addState(newStateName, val);
|
|
288
288
|
setNewStateName('');
|
|
@@ -293,7 +293,7 @@ const AnimationStateMachineView = ({ api }) => {
|
|
|
293
293
|
value: anim.id,
|
|
294
294
|
label: `${anim.sheetName}: ${anim.name}`
|
|
295
295
|
}))
|
|
296
|
-
] }))] }), activeSM.states.map(state => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.selectedStateId === state.id ? styles.selected : ''} ${activeSM.entryStateId === state.id ? styles.entry : ''}`, onClick: () => api.selectState(state.id), children: [jsxs("span", { className: styles.layerName, children: [activeSM.entryStateId === state.id && '▶ ', state.name] }), jsx(
|
|
296
|
+
] }))] }), activeSM.states.map(state => (jsxs("div", { className: `${styles.layerItem} ${api.editorState.selectedStateId === state.id ? styles.selected : ''} ${activeSM.entryStateId === state.id ? styles.entry : ''}`, onClick: () => api.selectState(state.id), children: [jsxs("span", { className: styles.layerName, children: [activeSM.entryStateId === state.id && '▶ ', state.name] }), jsx(Oe, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: e => {
|
|
297
297
|
e.stopPropagation();
|
|
298
298
|
api.removeState(state.id);
|
|
299
299
|
}, children: "\u00D7" })] }, state.id)))] })] }))] }), jsx("div", { className: styles.stateGraph, children: activeSM ? (jsxs("svg", { width: "100%", height: "100%", children: [jsx("defs", { children: jsx("marker", { id: "arrowhead", markerWidth: "10", markerHeight: "7", refX: "9", refY: "3.5", orient: "auto", children: jsx("polygon", { points: "0 0, 10 3.5, 0 7", fill: "#4a5a7e" }) }) }), activeSM.transitions.map(trans => {
|
|
@@ -305,12 +305,12 @@ const AnimationStateMachineView = ({ api }) => {
|
|
|
305
305
|
}), activeSM.states.map(state => {
|
|
306
306
|
var _a, _b;
|
|
307
307
|
return (jsx("foreignObject", { x: state.position.x, y: state.position.y, width: 120, height: 50, children: jsxs("div", { className: `${styles.stateNode} ${api.editorState.selectedStateId === state.id ? styles.selected : ''} ${activeSM.entryStateId === state.id ? styles.entry : ''}`, onClick: () => api.selectState(state.id), children: [jsx("div", { className: styles.stateNodeName, children: state.name }), jsx("div", { className: styles.stateNodeAnim, children: (_b = (_a = allAnimations.find(a => a.id === state.animationId)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'No animation' })] }) }, state.id));
|
|
308
|
-
})] })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: "Create a state machine to design animation flows" }) })) }), jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Parameters" }), jsx(
|
|
308
|
+
})] })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: "Create a state machine to design animation flows" }) })) }), jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: "Parameters" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.addParameter('new_param', 'trigger'), disabled: !activeSM, children: "+ Add" })] }), jsx("div", { className: styles.panelContent, children: activeSM === null || activeSM === void 0 ? void 0 : activeSM.parameters.map(param => (jsxs("div", { className: styles.layerItem, children: [jsx("span", { className: styles.layerOpacity, children: param.type }), jsx("span", { className: styles.layerName, children: param.name }), jsx(Oe, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeParameter(param.id), children: "\u00D7" })] }, param.id))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Transitions" }) }), jsxs("div", { className: styles.panelContent, children: [activeSM === null || activeSM === void 0 ? void 0 : activeSM.transitions.map(trans => {
|
|
309
309
|
var _a, _b;
|
|
310
310
|
const from = activeSM.states.find(s => s.id === trans.fromStateId);
|
|
311
311
|
const to = activeSM.states.find(s => s.id === trans.toStateId);
|
|
312
|
-
return (jsxs("div", { className: styles.layerItem, children: [jsxs("span", { className: styles.layerName, children: [(_a = from === null || from === void 0 ? void 0 : from.name) !== null && _a !== void 0 ? _a : '?', " \u2192 ", (_b = to === null || to === void 0 ? void 0 : to.name) !== null && _b !== void 0 ? _b : '?'] }), jsx(
|
|
313
|
-
}), api.editorState.selectedStateId && (jsxs("div", { className: styles.panelSection, style: { marginTop: 12 }, children: [jsx("label", { className: styles.panelLabel, children: "Add transition to:" }), jsx(
|
|
312
|
+
return (jsxs("div", { className: styles.layerItem, children: [jsxs("span", { className: styles.layerName, children: [(_a = from === null || from === void 0 ? void 0 : from.name) !== null && _a !== void 0 ? _a : '?', " \u2192 ", (_b = to === null || to === void 0 ? void 0 : to.name) !== null && _b !== void 0 ? _b : '?'] }), jsx(Oe, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeTransition(trans.id), children: "\u00D7" })] }, trans.id));
|
|
313
|
+
}), api.editorState.selectedStateId && (jsxs("div", { className: styles.panelSection, style: { marginTop: 12 }, children: [jsx("label", { className: styles.panelLabel, children: "Add transition to:" }), jsx(nn, { className: styles.panelInput, onChange: val => {
|
|
314
314
|
if (val && api.editorState.selectedStateId) {
|
|
315
315
|
api.addTransition(api.editorState.selectedStateId, val);
|
|
316
316
|
}
|
|
@@ -339,7 +339,7 @@ const EffectsView = ({ api }) => {
|
|
|
339
339
|
{ type: 'shadow', label: 'Shadow' },
|
|
340
340
|
{ type: 'palette-swap', label: 'Palette Swap' },
|
|
341
341
|
];
|
|
342
|
-
return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Add Effect" }) }), jsx("div", { className: styles.panelContent, children: effectTypes.map(({ type, label }) => (jsx(
|
|
342
|
+
return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Add Effect" }) }), jsx("div", { className: styles.panelContent, children: effectTypes.map(({ type, label }) => (jsx(Oe, { className: styles.layerItem, variant: "ghost", onClick: () => api.addEffect(type), style: { cursor: 'pointer', border: 'none', textAlign: 'left' }, children: jsx("span", { className: styles.layerName, children: label }) }, type))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: "Presets" }) }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.panelSection, children: [jsx(_t, { className: styles.panelInput, placeholder: "Preset name...", value: presetName, onChange: val => setPresetName(val) }), jsx(Oe, { className: styles.btnSecondary, variant: "ghost", style: { marginTop: 4, width: '100%' }, onClick: () => {
|
|
343
343
|
if (presetName) {
|
|
344
344
|
api.saveEffectPreset(presetName);
|
|
345
345
|
setPresetName('');
|
|
@@ -352,7 +352,7 @@ const EffectsView = ({ api }) => {
|
|
|
352
352
|
const StatusBar = ({ api }) => {
|
|
353
353
|
const activeMap = api.project.tileMaps.find(m => m.id === api.editorState.activeTileMapId);
|
|
354
354
|
const activeSheet = api.project.spriteSheets.find(s => s.id === api.editorState.activeSpriteSheetId);
|
|
355
|
-
return (jsxs("div", { className: styles.statusBar, children: [jsxs("span", { className: styles.statusItem, children: ["Mode: ", api.editorState.mode] }), api.editorState.mode === 'tilemap' && activeMap && (jsxs(Fragment, { children: [jsxs("span", { className: styles.statusItem, children: ["Map: ", activeMap.name, " (", activeMap.width, "\u00D7", activeMap.height, ")"] }), jsxs("span", { className: styles.statusItem, children: ["Tile: ", activeMap.tileWidth, "\u00D7", activeMap.tileHeight, "px"] })] })), api.editorState.mode === 'spritesheet' && activeSheet && (jsxs(Fragment, { children: [jsxs("span", { className: styles.statusItem, children: ["Sheet: ", activeSheet.name] }), jsxs("span", { className: styles.statusItem, children: ["Selected: ", api.editorState.selectedFrameIds.length, " frames"] })] })), jsxs("div", { className: styles.zoomControl, children: [jsx(
|
|
355
|
+
return (jsxs("div", { className: styles.statusBar, children: [jsxs("span", { className: styles.statusItem, children: ["Mode: ", api.editorState.mode] }), api.editorState.mode === 'tilemap' && activeMap && (jsxs(Fragment, { children: [jsxs("span", { className: styles.statusItem, children: ["Map: ", activeMap.name, " (", activeMap.width, "\u00D7", activeMap.height, ")"] }), jsxs("span", { className: styles.statusItem, children: ["Tile: ", activeMap.tileWidth, "\u00D7", activeMap.tileHeight, "px"] })] })), api.editorState.mode === 'spritesheet' && activeSheet && (jsxs(Fragment, { children: [jsxs("span", { className: styles.statusItem, children: ["Sheet: ", activeSheet.name] }), jsxs("span", { className: styles.statusItem, children: ["Selected: ", api.editorState.selectedFrameIds.length, " frames"] })] })), jsxs("div", { className: styles.zoomControl, children: [jsx(Oe, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(api.editorState.zoom - 0.25), children: "\u2212" }), jsxs("span", { children: [Math.round(api.editorState.zoom * 100), "%"] }), jsx(Oe, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(api.editorState.zoom + 0.25), children: "+" }), jsx(Oe, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(1), children: "\u2299" })] })] }));
|
|
356
356
|
};
|
|
357
357
|
/* ────────────────────────────────────────────────────────────────
|
|
358
358
|
MAIN COMPONENT
|
package/dist/esm/index.js
CHANGED
|
@@ -66,4 +66,6 @@ export { COMMON_KERNING_PAIRS, OPENTYPE_FEATURE_PRESETS, autoKern, buildTTF, dow
|
|
|
66
66
|
export { applyObjectChange, addObject as crdtAddObject, batchUpdate as crdtBatchUpdate, removeObject as crdtRemoveObject, updateCursor as crdtUpdateCursor, updateSelection as crdtUpdateSelection, getRemotePresences, useYjsCollaboration } from './core/yjsCollaboration.js';
|
|
67
67
|
export { BRUSH_PRESETS, NiceBrushEngine, useBrushEngine } from './core/NiceBrushEngine.js';
|
|
68
68
|
export { NiceSvgToCode, convertSvgToCode, useSvgToCode } from './vector/NiceSvgToCode.js';
|
|
69
|
+
export { RETRO_PRESETS } from './pixel/pixel-pro.js';
|
|
70
|
+
export { DEVICE_PRESETS } from './design/design-tools.js';
|
|
69
71
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useCallback } from 'react';
|
|
3
3
|
import styles from './PixelEditor.module.css.js';
|
|
4
|
-
import { NiceModal as
|
|
4
|
+
import { NiceModal as _s, NiceNumberInput as Ri, NiceButton as Oe } from '../ui/dist/index.js';
|
|
5
5
|
import { usePixelEditor } from './usePixelEditor.js';
|
|
6
6
|
import PixelEditorMenuBar from './PixelEditorMenuBar.js';
|
|
7
7
|
import PixelEditorToolbar from './PixelEditorToolbar.js';
|
|
@@ -24,10 +24,10 @@ const NicePixelEditor = ({ initialWidth = 64, initialHeight = 64, className, onS
|
|
|
24
24
|
if (f)
|
|
25
25
|
api.openFile(f);
|
|
26
26
|
e.target.value = "";
|
|
27
|
-
} }), jsx(PixelEditorMenuBar, { api: api, hasOnSaveToLibrary: !!onSaveToLibrary, onOpenGenerator: () => setShowGenerator(true) }), jsxs("div", { className: styles.mainArea, children: [jsx(PixelEditorToolbar, { api: api }), jsx(PixelEditorCanvas, { api: api }), jsx(PixelEditorRightPanel, { api: api })] }), jsx(PixelEditorTimeline, { api: api }), jsx(PixelEditorStatusBar, { api: api }), showGenerator && (jsx(NiceSpriteGeneratorPanel, { onSendToCanvas: handleGeneratorSend, onClose: () => setShowGenerator(false) })), jsxs(
|
|
27
|
+
} }), jsx(PixelEditorMenuBar, { api: api, hasOnSaveToLibrary: !!onSaveToLibrary, onOpenGenerator: () => setShowGenerator(true) }), jsxs("div", { className: styles.mainArea, children: [jsx(PixelEditorToolbar, { api: api }), jsx(PixelEditorCanvas, { api: api }), jsx(PixelEditorRightPanel, { api: api })] }), jsx(PixelEditorTimeline, { api: api }), jsx(PixelEditorStatusBar, { api: api }), showGenerator && (jsx(NiceSpriteGeneratorPanel, { onSendToCanvas: handleGeneratorSend, onClose: () => setShowGenerator(false) })), jsxs(_s, { open: api.dlgNew, onClose: () => api.setDlgNew(false), title: "New Document", children: [jsx("label", { children: "Width (px)" }), jsx(Ri, { min: 1, max: 4096, value: api.nw, onChange: (val) => api.setNw(val !== null && val !== void 0 ? val : 64), "aria-label": "Document width" }), jsx("label", { children: "Height (px)" }), jsx(Ri, { min: 1, max: 4096, value: api.nh, onChange: (val) => api.setNh(val !== null && val !== void 0 ? val : 64), "aria-label": "Document height" }), jsx("div", { style: { display: "flex", gap: 4, flexWrap: "wrap", marginTop: 8 }, children: [
|
|
28
28
|
[16, 16], [32, 32], [48, 48], [64, 64], [128, 128],
|
|
29
29
|
[256, 256], [16, 32], [32, 64], [48, 16], [64, 32],
|
|
30
|
-
].map(([w, h]) => (jsxs(
|
|
30
|
+
].map(([w, h]) => (jsxs(Oe, { className: styles.btnSecondary, variant: "ghost", size: "sm", style: { fontSize: 10, padding: "2px 6px" }, onClick: () => { api.setNw(w); api.setNh(h); }, children: [w, "x", h] }, `${w}x${h}`))) }), jsxs("div", { className: styles.dialogActions, children: [jsx(Oe, { className: styles.btnSecondary, variant: "ghost", onClick: () => api.setDlgNew(false), children: "Cancel" }), jsx(Oe, { className: styles.btnPrimary, variant: "primary", onClick: () => api.newDoc(api.nw, api.nh), children: "Create" })] })] })] }));
|
|
31
31
|
};
|
|
32
32
|
/** @deprecated Use NicePixelEditor */
|
|
33
33
|
const PixelEditor = NicePixelEditor;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styles from './PixelEditor.module.css.js';
|
|
4
|
-
import { NiceButton as
|
|
4
|
+
import { NiceButton as Oe } from '../ui/dist/index.js';
|
|
5
5
|
|
|
6
|
-
const PixelEditorMenuBar = React.memo(({ api, hasOnSaveToLibrary, onOpenGenerator }) => (jsxs("div", { className: styles.menuBar, children: [jsx(
|
|
6
|
+
const PixelEditorMenuBar = React.memo(({ api, hasOnSaveToLibrary, onOpenGenerator }) => (jsxs("div", { className: styles.menuBar, children: [jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.setDlgNew(true), children: "New" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = api.fInp.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: "Open" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportAse, children: "Save .ase" }), jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportPNG, children: "PNG" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportJPG, children: "JPG" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportWebP, children: "WebP" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportBMP, children: "BMP" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportSVG, children: "SVG" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.exportSheet, children: "Sheet" }), hasOnSaveToLibrary && (jsxs(Fragment, { children: [jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.saveToLibrary, children: "\uD83D\uDCE6 Save to Library" })] })), jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.undo, children: "Undo" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.redo, children: "Redo" }), jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.flipCv("h"), children: "Flip H" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.flipCv("v"), children: "Flip V" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.rotateCv, children: "Rot 90\u00B0" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.clearCv, children: "Clear" }), jsx("div", { className: styles.menuSep }), jsxs(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.setGrid((g) => !g), children: ["Grid: ", api.grid ? "ON" : "OFF"] }), jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.doCopy, disabled: !api.sel, children: "Copy" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.doCut, disabled: !api.sel, children: "Cut" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.doPaste, disabled: !api.clipboard.current, children: "Paste" }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.doSelectAll, children: "Sel All" }), jsx("div", { className: styles.menuSep }), jsx(Oe, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: onOpenGenerator, children: "\uD83C\uDFA8 Generator" })] })));
|
|
7
7
|
PixelEditorMenuBar.displayName = "PixelEditorMenuBar";
|
|
8
8
|
|
|
9
9
|
export { PixelEditorMenuBar as default };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styles from './PixelEditor.module.css.js';
|
|
4
|
-
import { NiceSlider as
|
|
4
|
+
import { NiceSlider as Ar, NiceButton as Oe, NiceCheckbox as ma, NiceTextInput as _t, NiceSelect as nn } from '../ui/dist/index.js';
|
|
5
5
|
import { BRUSH_SHAPES, BLEND_MODES } from './pixelEditorTypes.js';
|
|
6
6
|
import { HSVPicker } from './HSVPicker.js';
|
|
7
7
|
|
|
8
|
-
const PixelEditorRightPanel = React.memo(({ api }) => (jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelSection, children: [jsx("div", { className: styles.panelTitle, children: jsx("span", { children: "Brush" }) }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Size" }), jsx(
|
|
9
|
-
api.setEditingLayerName(null); }, autoFocus: true, "aria-label": "Layer name" })) : (jsx("span", { className: styles.layerName, onDoubleClick: (e) => { e.stopPropagation(); api.setEditingLayerName(l.id); }, children: l.name })), jsxs("span", { className: styles.layerOpacity, children: [Math.round((l.opacity / 255) * 100), "%"] })] }, l.id))), api.layers[api.aLi] && (jsxs("div", { className: styles.layerControls, children: [jsx(
|
|
8
|
+
const PixelEditorRightPanel = React.memo(({ api }) => (jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelSection, children: [jsx("div", { className: styles.panelTitle, children: jsx("span", { children: "Brush" }) }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Size" }), jsx(Ar, { min: 1, max: 64, value: api.brush.size, onChange: (val) => api.setBrush((b) => ({ ...b, size: val })), "aria-label": "Brush size" }), jsx("span", { children: api.brush.size })] }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Opacity" }), jsx(Ar, { min: 1, max: 100, value: api.brush.opacity, onChange: (val) => api.setBrush((b) => ({ ...b, opacity: val })), "aria-label": "Brush opacity" }), jsxs("span", { children: [api.brush.opacity, "%"] })] }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Flow" }), jsx(Ar, { min: 1, max: 100, value: api.brush.flow, onChange: (val) => api.setBrush((b) => ({ ...b, flow: val })), "aria-label": "Brush flow" }), jsxs("span", { children: [api.brush.flow, "%"] })] }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Hard" }), jsx(Ar, { min: 0, max: 100, value: api.brush.hardness, onChange: (val) => api.setBrush((b) => ({ ...b, hardness: val })), "aria-label": "Brush hardness" }), jsxs("span", { children: [api.brush.hardness, "%"] })] }), jsxs("div", { className: styles.brushSlider, children: [jsx("label", { children: "Space" }), jsx(Ar, { min: 1, max: 100, value: api.brush.spacing, onChange: (val) => api.setBrush((b) => ({ ...b, spacing: val })), "aria-label": "Brush spacing" }), jsxs("span", { children: [api.brush.spacing, "%"] })] }), jsx("div", { className: styles.brushShapes, children: BRUSH_SHAPES.map((s) => (jsx(Oe, { className: `${styles.miniBtn} ${api.brush.shape === s.id ? styles.miniBtnActive : ""}`, variant: api.brush.shape === s.id ? 'primary' : 'ghost', size: "sm", onClick: () => api.setBrush((b) => ({ ...b, shape: s.id })), title: s.label, children: s.icon }, s.id))) }), jsxs("div", { className: styles.pressureToggles, children: [jsx(ma, { checked: api.brush.pressureSize, onChange: (checked) => api.setBrush((b) => ({ ...b, pressureSize: checked })), label: "Pressure\u2192Size", className: styles.checkLabel }), jsx(ma, { checked: api.brush.pressureOpacity, onChange: (checked) => api.setBrush((b) => ({ ...b, pressureOpacity: checked })), label: "Pressure\u2192Opacity", className: styles.checkLabel })] })] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx("span", { children: "Colour" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: () => api.setShowHSV(!api.showHSV), title: "Toggle HSV Picker", children: api.showHSV ? "▾" : "▸" })] }), api.showHSV && jsx(HSVPicker, { color: api.col1, onChange: api.setCol1 })] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx("span", { children: "Layers" }), jsxs("span", { children: [jsx(Oe, { variant: "ghost", size: "sm", onClick: api.addLayer, title: "Add Layer", "aria-label": "Add Layer", children: "+" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.dupLayer, title: "Duplicate Layer", "aria-label": "Duplicate Layer", children: "\u29C9" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.rmLayer, title: "Remove Layer", "aria-label": "Remove Layer", children: "\u2212" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.mergeDown, title: "Merge Down", "aria-label": "Merge Down", children: "\u2913" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.moveLayerUp, title: "Move Up", "aria-label": "Move Up", children: "\u2191" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.moveLayerDown, title: "Move Down", "aria-label": "Move Down", children: "\u2193" })] })] }), [...api.layers].map((l, i) => ({ l, i })).reverse().map(({ l, i }) => (jsxs("div", { className: `${styles.layerItem} ${i === api.aLi ? styles.layerItemActive : ""}`, onClick: () => api.setALi(i), children: [jsx("span", { className: styles.layerVis, onClick: (e) => { e.stopPropagation(); api.toggleVis(i); }, children: l.visible ? "👁" : "─" }), jsx("span", { className: styles.layerLock, onClick: (e) => { e.stopPropagation(); api.toggleLock(i); }, title: l.locked ? "Locked" : "Unlocked", children: l.locked ? "🔒" : "🔓" }), api.editingLayerName === l.id ? (jsx(_t, { className: styles.layerNameInput, value: l.name, onChange: (val) => api.renameLayer(i, val), onBlur: () => api.setEditingLayerName(null), onKeyDown: (e) => { if (e.key === "Enter")
|
|
9
|
+
api.setEditingLayerName(null); }, autoFocus: true, "aria-label": "Layer name" })) : (jsx("span", { className: styles.layerName, onDoubleClick: (e) => { e.stopPropagation(); api.setEditingLayerName(l.id); }, children: l.name })), jsxs("span", { className: styles.layerOpacity, children: [Math.round((l.opacity / 255) * 100), "%"] })] }, l.id))), api.layers[api.aLi] && (jsxs("div", { className: styles.layerControls, children: [jsx(nn, { value: api.layers[api.aLi].blendMode, onChange: (val) => api.setLayerBlendMode(api.aLi, val), className: styles.blendSelect, "aria-label": "Blend mode", options: BLEND_MODES.map((m) => ({ value: m, label: m })) }), jsx(Ar, { min: 0, max: 255, value: api.layers[api.aLi].opacity, onChange: (val) => api.setLayerOpacity(api.aLi, val), className: styles.layerOpSlider, title: `Opacity: ${Math.round((api.layers[api.aLi].opacity / 255) * 100)}%` })] }))] }), jsxs("div", { className: styles.panelSection, children: [jsxs("div", { className: styles.panelTitle, children: [jsx("span", { children: "Palette" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: () => { if (!api.pal.includes(api.col1))
|
|
10
10
|
api.setPal((p) => [...p, api.col1]); }, title: "Add current colour", children: "+" })] }), jsx("div", { className: styles.paletteGrid, children: api.pal.map((c, i) => (jsx("div", { className: `${styles.paletteSwatch} ${c === api.col1 ? styles.paletteSwatchActive : ""}`, style: { backgroundColor: c }, onClick: () => api.setCol1(c), onContextMenu: (e) => { e.preventDefault(); api.setCol2(c); }, title: c }, i))) })] })] })));
|
|
11
11
|
PixelEditorRightPanel.displayName = "PixelEditorRightPanel";
|
|
12
12
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styles from './PixelEditor.module.css.js';
|
|
4
|
-
import { NiceButton as
|
|
4
|
+
import { NiceButton as Oe, NiceNumberInput as Ri, NiceCheckbox as ma } from '../ui/dist/index.js';
|
|
5
5
|
|
|
6
|
-
const PixelEditorTimeline = React.memo(({ api }) => (jsxs("div", { className: styles.bottomPanel, children: [jsxs("div", { className: styles.timelineControls, children: [jsx(
|
|
6
|
+
const PixelEditorTimeline = React.memo(({ api }) => (jsxs("div", { className: styles.bottomPanel, children: [jsxs("div", { className: styles.timelineControls, children: [jsx(Oe, { variant: "ghost", size: "sm", onClick: api.addFrame, children: "+ Frame" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.dupFrame, "aria-label": "Duplicate frame", children: "Dup" }), jsx(Oe, { variant: "ghost", size: "sm", onClick: api.rmFrame, "aria-label": "Remove frame", children: "\u2212 Frame" }), jsx("div", { className: styles.menuSep }), jsx(Oe, { variant: "ghost", size: "sm", onClick: () => api.setPlaying((p) => !p), children: api.playing ? "⏸" : "▶" }), jsx("label", { children: "FPS:" }), jsx(Ri, { min: 1, max: 60, value: api.fps, onChange: (val) => api.setFps(val !== null && val !== void 0 ? val : 8), "aria-label": "Frames per second" }), jsx("div", { className: styles.menuSep }), jsx(ma, { checked: api.onion, onChange: (checked) => api.setOnion(checked), label: "Onion" }), jsx("div", { style: { flex: 1 } }), jsxs("span", { children: [api.fc, " frame", api.fc !== 1 ? "s" : ""] })] }), jsx("div", { className: styles.timelineFrames, children: Array.from({ length: api.fc }, (_, fi) => (jsxs("div", { className: `${styles.frameThumb} ${fi === api.afi ? styles.frameThumbActive : ""}`, onClick: () => api.setAfi(fi), children: [api.thumbs[fi] && (jsx("img", { src: api.thumbs[fi], alt: `Frame ${fi + 1}`, style: { width: 36, height: 36, imageRendering: "pixelated", objectFit: "contain" } })), jsx("span", { className: styles.frameNum, children: fi + 1 })] }, fi))) })] })));
|
|
7
7
|
PixelEditorTimeline.displayName = "PixelEditorTimeline";
|
|
8
8
|
|
|
9
9
|
export { PixelEditorTimeline as default };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styles from './PixelEditor.module.css.js';
|
|
4
|
-
import { NiceButton as
|
|
4
|
+
import { NiceButton as Oe, NiceColorPicker as Nn } from '../ui/dist/index.js';
|
|
5
5
|
import { TOOLS, SYMMETRY_MODES } from './pixelEditorTypes.js';
|
|
6
6
|
|
|
7
|
-
const PixelEditorToolbar = React.memo(({ api }) => (jsxs("div", { className: styles.toolbar, children: [TOOLS.map((t) => (jsx(
|
|
7
|
+
const PixelEditorToolbar = React.memo(({ api }) => (jsxs("div", { className: styles.toolbar, children: [TOOLS.map((t) => (jsx(Oe, { className: `${styles.toolBtn} ${api.tool === t.id ? styles.toolBtnActive : ""}`, variant: api.tool === t.id ? 'primary' : 'ghost', size: "sm", onClick: () => api.setTool(t.id), title: `${t.label} (${t.key})`, children: t.icon }, t.id))), jsx("div", { className: styles.toolSep }), jsx(Oe, { className: `${styles.toolBtn} ${api.shapeFilled ? styles.toolBtnActive : ""}`, variant: api.shapeFilled ? 'primary' : 'ghost', size: "sm", onClick: () => api.setShapeFilled((f) => !f), title: `${api.shapeFilled ? "Filled" : "Outline"} (F)`, children: api.shapeFilled ? "■" : "□" }), jsx("div", { className: styles.toolGroup, children: SYMMETRY_MODES.map((m) => (jsx(Oe, { className: `${styles.miniBtn} ${api.symmetry === m.id ? styles.miniBtnActive : ""}`, variant: api.symmetry === m.id ? 'primary' : 'ghost', size: "sm", onClick: () => api.setSymmetry(m.id), title: `Symmetry: ${m.label}`, children: m.label.slice(0, 2) }, m.id))) }), jsxs("div", { className: styles.colorSwatches, children: [jsx("label", { style: { position: "relative", width: 28, height: 28, cursor: "pointer" }, title: "Primary Colour (click for HSV)", children: jsx(Nn, { value: api.col1, onChange: (c) => api.setCol1(c) }) }), jsx("label", { style: { position: "relative", width: 22, height: 22, cursor: "pointer" }, title: "Secondary Colour", children: jsx(Nn, { value: api.col2, onChange: (c) => api.setCol2(c) }) }), jsx(Oe, { className: styles.toolBtn, variant: "ghost", size: "sm", onClick: () => {
|
|
8
8
|
const tmp = api.col1;
|
|
9
9
|
api.setCol1(api.col2);
|
|
10
10
|
api.setCol2(tmp);
|