@retro-antlitz-kartei/react-editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,743 @@
1
+ import { useRef, useEffect, useState, useMemo, useCallback } from 'react';
2
+ import { renderAvatar, normalizeConfig, DEFAULT_CONFIG, configFromSeed, PART_NAMES, BG, encodeConfig, SKIN, CLOTH, randomConfig, decodeConfig } from '@retro-antlitz-kartei/generator';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { AvatarArena, POSES, POSE_NAMES } from '@retro-antlitz-kartei/animate';
5
+
6
+ // src/AvatarEditor.tsx
7
+ function AvatarPreview({
8
+ config,
9
+ width = 320,
10
+ height = 400,
11
+ background = true,
12
+ floor = true,
13
+ className,
14
+ style,
15
+ onClick
16
+ }) {
17
+ const ref = useRef(null);
18
+ useEffect(() => {
19
+ const canvas = ref.current;
20
+ if (!canvas) return;
21
+ renderAvatar(canvas, config, { background, floor });
22
+ }, [config, background, floor, width, height]);
23
+ return /* @__PURE__ */ jsx(
24
+ "canvas",
25
+ {
26
+ ref,
27
+ width,
28
+ height,
29
+ className,
30
+ style: { imageRendering: "pixelated", display: "block", ...style },
31
+ onClick
32
+ }
33
+ );
34
+ }
35
+ function CombatModal({ config, theme: t, onClose }) {
36
+ const canvasRef = useRef(null);
37
+ const arenaRef = useRef(null);
38
+ const [pose, setPose] = useState("idle");
39
+ useEffect(() => {
40
+ const canvas = canvasRef.current;
41
+ if (!canvas) return;
42
+ const arena = new AvatarArena(canvas, { pose: "idle" });
43
+ arena.setConfig(config);
44
+ arenaRef.current = arena;
45
+ return () => arena.destroy();
46
+ }, []);
47
+ useEffect(() => {
48
+ arenaRef.current?.setConfig(config);
49
+ }, [config]);
50
+ useEffect(() => {
51
+ arenaRef.current?.setPose(pose);
52
+ }, [pose]);
53
+ return /* @__PURE__ */ jsx(
54
+ "div",
55
+ {
56
+ onClick: onClose,
57
+ style: {
58
+ position: "fixed",
59
+ inset: 0,
60
+ zIndex: 60,
61
+ background: "rgba(8,4,16,0.82)",
62
+ display: "flex",
63
+ alignItems: "center",
64
+ justifyContent: "center",
65
+ padding: "20px",
66
+ backdropFilter: "blur(3px)"
67
+ },
68
+ children: /* @__PURE__ */ jsxs(
69
+ "div",
70
+ {
71
+ onClick: (e) => e.stopPropagation(),
72
+ style: {
73
+ background: t.cabinet,
74
+ border: "4px solid " + t.cabinetBorder,
75
+ borderRadius: t.paper ? "6px" : "14px",
76
+ padding: "18px",
77
+ display: "flex",
78
+ flexDirection: "column",
79
+ gap: "14px",
80
+ width: "100%",
81
+ maxWidth: "420px",
82
+ boxShadow: "0 20px 60px rgba(0,0,0,.6)"
83
+ },
84
+ children: [
85
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "10px" }, children: [
86
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: t.mono, fontSize: "10px", color: t.title, letterSpacing: "1px" }, children: "\u2694 COMBAT MODE" }),
87
+ /* @__PURE__ */ jsx(
88
+ "button",
89
+ {
90
+ onClick: onClose,
91
+ style: {
92
+ fontFamily: t.mono,
93
+ fontSize: "11px",
94
+ cursor: "pointer",
95
+ width: "30px",
96
+ height: "30px",
97
+ borderRadius: "7px",
98
+ border: "2px solid " + t.panelBorder,
99
+ background: "#ffffff10",
100
+ color: "#fff",
101
+ flex: "0 0 auto"
102
+ },
103
+ children: "\u2715"
104
+ }
105
+ )
106
+ ] }),
107
+ /* @__PURE__ */ jsx(
108
+ "canvas",
109
+ {
110
+ ref: canvasRef,
111
+ width: 360,
112
+ height: 380,
113
+ style: {
114
+ width: "100%",
115
+ maxWidth: "360px",
116
+ aspectRatio: "360 / 380",
117
+ imageRendering: "pixelated",
118
+ display: "block",
119
+ margin: "0 auto",
120
+ borderRadius: "8px",
121
+ border: "3px solid " + t.cabinetBorder,
122
+ background: "#0c0618"
123
+ }
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: "7px" }, children: POSES.map((p) => /* @__PURE__ */ jsx(
127
+ "button",
128
+ {
129
+ onClick: () => setPose(p),
130
+ style: {
131
+ fontFamily: t.mono,
132
+ fontSize: "7px",
133
+ letterSpacing: ".5px",
134
+ cursor: "pointer",
135
+ padding: "10px 4px",
136
+ borderRadius: "6px",
137
+ border: "2px solid " + (pose === p ? "#00f0ff" : "#3a2f55"),
138
+ background: pose === p ? "#00f0ff" : "#1c0d33",
139
+ color: pose === p ? "#10131c" : "#cdbcff"
140
+ },
141
+ children: POSE_NAMES[p].toUpperCase()
142
+ },
143
+ p
144
+ )) })
145
+ ]
146
+ }
147
+ )
148
+ }
149
+ );
150
+ }
151
+
152
+ // src/theme.ts
153
+ var LAYOUTS = [
154
+ { key: "arcade", name: "ARCADE" },
155
+ { key: "kompakt", name: "COMPACT" },
156
+ { key: "steckbrief", name: "WANTED" }
157
+ ];
158
+ var MONO = "'Press Start 2P',monospace";
159
+ var BODY = "'VT323',monospace";
160
+ function theme(layout) {
161
+ if (layout === "kompakt") {
162
+ return {
163
+ stageBg: "#10131c",
164
+ cabinet: "#1a1f2e",
165
+ cabinetBorder: "#2a3145",
166
+ marqueeBg: "#232a3d",
167
+ title: "#7df9ff",
168
+ accent: "#ff5d8f",
169
+ screenBg: "#0b0e16",
170
+ panel: "#222a3d",
171
+ panelBorder: "#323b54",
172
+ rowBg: "#1a2032",
173
+ text: "#cdd6f4",
174
+ sub: "#8a93b2",
175
+ mainDir: "row",
176
+ mono: MONO,
177
+ body: BODY,
178
+ radius: "14px",
179
+ titleText: "AVATAR EDITOR",
180
+ glow: false,
181
+ paper: false
182
+ };
183
+ }
184
+ if (layout === "steckbrief") {
185
+ return {
186
+ stageBg: "#3a2a18",
187
+ cabinet: "#efe2c4",
188
+ cabinetBorder: "#7a5a32",
189
+ marqueeBg: "#c0392b",
190
+ title: "#fff4dc",
191
+ accent: "#c0392b",
192
+ screenBg: "#d8c9a6",
193
+ panel: "#e6d8ba",
194
+ panelBorder: "#b89a64",
195
+ rowBg: "#f3ead2",
196
+ text: "#4a3520",
197
+ sub: "#8a6e44",
198
+ mainDir: "row",
199
+ mono: MONO,
200
+ body: BODY,
201
+ radius: "4px",
202
+ titleText: "WANTED",
203
+ glow: false,
204
+ paper: true
205
+ };
206
+ }
207
+ return {
208
+ // arcade
209
+ stageBg: "#1a0b2e",
210
+ cabinet: "#3a1d5c",
211
+ cabinetBorder: "#ff2e88",
212
+ marqueeBg: "#120726",
213
+ title: "#ffe600",
214
+ accent: "#00f0ff",
215
+ screenBg: "#06010f",
216
+ panel: "#2a1248",
217
+ panelBorder: "#7b2ff7",
218
+ rowBg: "#1c0d33",
219
+ text: "#ffe600",
220
+ sub: "#b48cff",
221
+ mainDir: "column",
222
+ mono: MONO,
223
+ body: BODY,
224
+ radius: "10px",
225
+ titleText: "AVATAR-O-MAT",
226
+ glow: true,
227
+ paper: false
228
+ };
229
+ }
230
+ var FONT_HREFS = [
231
+ "https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap"
232
+ ];
233
+ var PART_CONTROLS = [
234
+ { key: "gender", label: "BUILD" },
235
+ { key: "hut", label: "HAT" },
236
+ { key: "haare", label: "HAIR" },
237
+ { key: "ohren", label: "EARS" },
238
+ { key: "nase", label: "NOSE" },
239
+ { key: "acc", label: "ACCESSORY" },
240
+ { key: "torso", label: "TOP" },
241
+ { key: "hose", label: "TROUSERS" }
242
+ ];
243
+ var VIEW_OPTIONS = [
244
+ { key: "left", name: "LEFT" },
245
+ { key: "front", name: "FRONT" },
246
+ { key: "right", name: "RIGHT" }
247
+ ];
248
+ function useControllable(controlled, initial, onChange) {
249
+ const [internal, setInternal] = useState(initial);
250
+ const value = controlled !== void 0 ? controlled : internal;
251
+ const set = useCallback(
252
+ (v) => {
253
+ if (controlled === void 0) setInternal(v);
254
+ onChange?.(v);
255
+ },
256
+ [controlled, onChange]
257
+ );
258
+ return [value, set];
259
+ }
260
+ function AvatarEditor(props) {
261
+ const {
262
+ value,
263
+ defaultValue,
264
+ seed,
265
+ onChange,
266
+ layout: layoutProp,
267
+ defaultLayout = "arcade",
268
+ onLayoutChange,
269
+ showLayoutPicker = true,
270
+ showCombat = true,
271
+ showCode = true,
272
+ showSeed = true,
273
+ loadFonts = true,
274
+ className,
275
+ style
276
+ } = props;
277
+ const initialConfig = useMemo(
278
+ () => normalizeConfig(value ?? defaultValue ?? (seed ? configFromSeed(seed) : DEFAULT_CONFIG)),
279
+ // initial only
280
+ // eslint-disable-next-line react-hooks/exhaustive-deps
281
+ []
282
+ );
283
+ const [config, setConfig] = useControllable(value, initialConfig, onChange);
284
+ const [layout, setLayout] = useControllable(layoutProp, defaultLayout, onLayoutChange);
285
+ const [modalOpen, setModalOpen] = useState(false);
286
+ const [codeInput, setCodeInput] = useState("");
287
+ const [codeMsg, setCodeMsg] = useState("");
288
+ const [seedInput, setSeedInput] = useState(seed ?? "");
289
+ const t = theme(layout);
290
+ useEffect(() => {
291
+ if (!loadFonts || typeof document === "undefined") return;
292
+ for (const href of FONT_HREFS) {
293
+ if (document.querySelector(`link[href="${href}"]`)) continue;
294
+ const link = document.createElement("link");
295
+ link.rel = "stylesheet";
296
+ link.href = href;
297
+ document.head.appendChild(link);
298
+ }
299
+ if (!document.getElementById("rak-keyframes")) {
300
+ const s = document.createElement("style");
301
+ s.id = "rak-keyframes";
302
+ s.textContent = "@keyframes rak-coinpulse{0%,100%{transform:translateY(0)}50%{transform:translateY(-2px)}}";
303
+ document.head.appendChild(s);
304
+ }
305
+ }, [loadFonts]);
306
+ const patch = useCallback((p) => setConfig({ ...config, ...p }), [config, setConfig]);
307
+ const cycle = useCallback(
308
+ (key, dir) => {
309
+ const len = PART_NAMES[key].length;
310
+ patch({ [key]: (config[key] + dir + len) % len });
311
+ },
312
+ [config, patch]
313
+ );
314
+ const onRandom = () => setConfig({ ...randomConfig(), view: config.view });
315
+ const onCopyCode = () => {
316
+ const code = encodeConfig(config);
317
+ try {
318
+ navigator.clipboard.writeText(code);
319
+ setCodeMsg("Copied!");
320
+ } catch {
321
+ setCodeInput(code);
322
+ setCodeMsg("Selected");
323
+ }
324
+ };
325
+ const onLoadCode = () => {
326
+ if (!codeInput.trim()) return;
327
+ const next = decodeConfig(codeInput, config);
328
+ setConfig(next);
329
+ setCodeMsg("Loaded!");
330
+ };
331
+ const onGenerateSeed = () => {
332
+ if (!seedInput.trim()) return;
333
+ setConfig({ ...configFromSeed(seedInput), view: config.view });
334
+ };
335
+ const arrowStyle = {
336
+ fontFamily: t.mono,
337
+ fontSize: "10px",
338
+ cursor: "pointer",
339
+ flex: "0 0 auto",
340
+ width: "32px",
341
+ height: "32px",
342
+ borderRadius: t.paper ? "3px" : "7px",
343
+ border: "2px solid " + t.accent,
344
+ background: t.glow ? t.accent : t.accent + "22",
345
+ color: t.glow ? "#10131c" : t.accent,
346
+ lineHeight: 1
347
+ };
348
+ const rowStyle = {
349
+ display: "flex",
350
+ alignItems: "center",
351
+ gap: "8px",
352
+ background: t.rowBg,
353
+ borderRadius: t.paper ? "3px" : "8px",
354
+ padding: "6px 8px",
355
+ border: "1px solid " + t.panelBorder
356
+ };
357
+ const partLabelStyle = { fontFamily: t.mono, fontSize: "7px", color: t.sub, letterSpacing: "1px" };
358
+ const partValueStyle = { fontFamily: t.body, fontSize: "19px", color: t.text, lineHeight: 1.1, marginTop: "2px" };
359
+ const dividerStyle = { height: "2px", background: t.panelBorder, opacity: 0.6, margin: "2px 0" };
360
+ const swatchRowStyle = { display: "flex", alignItems: "center", gap: "10px", flexWrap: "wrap" };
361
+ const swatchLabelStyle = { fontFamily: t.mono, fontSize: "7px", color: t.sub, letterSpacing: "1px", width: "78px", flex: "0 0 auto" };
362
+ const codeFieldStyle = {
363
+ flex: "1 1 auto",
364
+ minWidth: 0,
365
+ fontFamily: "monospace",
366
+ fontSize: "11px",
367
+ padding: "7px 8px",
368
+ borderRadius: t.paper ? "3px" : "6px",
369
+ border: "2px solid " + t.panelBorder,
370
+ background: t.paper ? "#fff" : "#0c111c",
371
+ color: t.text,
372
+ outline: "none",
373
+ whiteSpace: "nowrap",
374
+ overflow: "hidden",
375
+ textOverflow: "ellipsis",
376
+ boxSizing: "border-box"
377
+ };
378
+ const smallBtnStyle = {
379
+ fontFamily: t.mono,
380
+ fontSize: "7px",
381
+ letterSpacing: ".5px",
382
+ cursor: "pointer",
383
+ padding: "8px 9px",
384
+ borderRadius: t.paper ? "3px" : "6px",
385
+ border: "2px solid " + t.accent,
386
+ background: t.glow ? t.accent : t.accent + "22",
387
+ color: t.glow ? "#10131c" : t.accent,
388
+ flex: "0 0 auto"
389
+ };
390
+ const codeLabelStyle = { fontFamily: t.mono, fontSize: "7px", color: t.sub, letterSpacing: "1px" };
391
+ const swatchBtn = (col, active) => ({
392
+ background: col,
393
+ width: "22px",
394
+ height: "22px",
395
+ cursor: "pointer",
396
+ padding: 0,
397
+ borderRadius: t.paper ? "2px" : "5px",
398
+ border: "2px solid " + (active ? t.accent : t.paper ? "#00000033" : "#ffffff44"),
399
+ boxShadow: active ? "0 0 0 2px " + t.accent + "66" : "none"
400
+ });
401
+ const nameDisplay = PART_NAMES.torso[config.torso].toUpperCase();
402
+ const partRow = (key, label) => /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
403
+ /* @__PURE__ */ jsx("button", { onClick: () => cycle(key, -1), style: arrowStyle, children: "\u25C0" }),
404
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0, textAlign: "center" }, children: [
405
+ /* @__PURE__ */ jsx("div", { style: partLabelStyle, children: label }),
406
+ /* @__PURE__ */ jsx("div", { style: partValueStyle, children: PART_NAMES[key][config[key]] })
407
+ ] }),
408
+ /* @__PURE__ */ jsx("button", { onClick: () => cycle(key, 1), style: arrowStyle, children: "\u25B6" })
409
+ ] }, key);
410
+ return /* @__PURE__ */ jsxs(
411
+ "div",
412
+ {
413
+ className,
414
+ style: {
415
+ minHeight: "100%",
416
+ width: "100%",
417
+ background: t.stageBg,
418
+ padding: "24px",
419
+ display: "flex",
420
+ flexDirection: "column",
421
+ alignItems: "center",
422
+ fontFamily: t.body,
423
+ backgroundImage: "radial-gradient(circle at 50% 0%, " + t.accent + "22, transparent 60%)",
424
+ boxSizing: "border-box",
425
+ ...style
426
+ },
427
+ children: [
428
+ showLayoutPicker && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px", alignItems: "center", flexWrap: "wrap", marginBottom: "14px" }, children: [
429
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: t.mono, fontSize: "9px", color: "#fff", opacity: 0.55, letterSpacing: "1px" }, children: "LAYOUT" }),
430
+ LAYOUTS.map((l) => /* @__PURE__ */ jsx(
431
+ "button",
432
+ {
433
+ onClick: () => setLayout(l.key),
434
+ style: {
435
+ fontFamily: t.mono,
436
+ fontSize: "8px",
437
+ letterSpacing: "1px",
438
+ cursor: "pointer",
439
+ padding: "7px 9px",
440
+ borderRadius: "6px",
441
+ border: "2px solid " + (layout === l.key ? t.accent : "#ffffff22"),
442
+ background: layout === l.key ? t.accent : "#ffffff10",
443
+ color: layout === l.key ? "#10131c" : "#fff"
444
+ },
445
+ children: l.name
446
+ },
447
+ l.key
448
+ ))
449
+ ] }),
450
+ /* @__PURE__ */ jsxs(
451
+ "div",
452
+ {
453
+ style: {
454
+ width: "100%",
455
+ maxWidth: t.mainDir === "row" ? "860px" : "540px",
456
+ background: t.cabinet,
457
+ border: "4px solid " + t.cabinetBorder,
458
+ borderRadius: t.radius,
459
+ boxShadow: "0 18px 50px rgba(0,0,0,.5)" + (t.glow ? ", 0 0 30px " + t.accent + "33" : ""),
460
+ overflow: "hidden"
461
+ },
462
+ children: [
463
+ /* @__PURE__ */ jsxs(
464
+ "div",
465
+ {
466
+ style: {
467
+ background: t.marqueeBg,
468
+ padding: "14px",
469
+ display: "flex",
470
+ gap: "12px",
471
+ alignItems: "center",
472
+ justifyContent: "center",
473
+ borderBottom: "3px solid " + t.cabinetBorder
474
+ },
475
+ children: [
476
+ /* @__PURE__ */ jsx("span", { style: { width: "10px", height: "10px", borderRadius: "50%", background: t.accent, boxShadow: "0 0 8px " + t.accent } }),
477
+ /* @__PURE__ */ jsx(
478
+ "span",
479
+ {
480
+ style: {
481
+ fontFamily: t.mono,
482
+ fontSize: t.titleText === "WANTED" ? "20px" : "15px",
483
+ color: t.title,
484
+ letterSpacing: "2px",
485
+ textShadow: t.glow ? "0 0 10px " + t.accent : "2px 2px 0 #00000044"
486
+ },
487
+ children: t.titleText
488
+ }
489
+ ),
490
+ /* @__PURE__ */ jsx("span", { style: { width: "10px", height: "10px", borderRadius: "50%", background: t.accent, boxShadow: "0 0 8px " + t.accent } })
491
+ ]
492
+ }
493
+ ),
494
+ /* @__PURE__ */ jsxs(
495
+ "div",
496
+ {
497
+ style: {
498
+ display: "flex",
499
+ flexDirection: t.mainDir,
500
+ gap: "16px",
501
+ padding: "18px",
502
+ alignItems: t.mainDir === "row" ? "stretch" : "center"
503
+ },
504
+ children: [
505
+ /* @__PURE__ */ jsxs(
506
+ "div",
507
+ {
508
+ style: {
509
+ flex: t.mainDir === "row" ? "0 0 300px" : "0 0 auto",
510
+ display: "flex",
511
+ flexDirection: "column",
512
+ gap: "8px",
513
+ alignItems: "center"
514
+ },
515
+ children: [
516
+ /* @__PURE__ */ jsxs(
517
+ "div",
518
+ {
519
+ style: {
520
+ position: "relative",
521
+ padding: "10px",
522
+ background: t.screenBg,
523
+ borderRadius: t.paper ? "4px" : "10px",
524
+ border: "4px solid " + t.cabinetBorder,
525
+ boxShadow: "inset 0 0 20px rgba(0,0,0,.6)",
526
+ cursor: showCombat ? "pointer" : "default"
527
+ },
528
+ onClick: showCombat ? () => setModalOpen(true) : void 0,
529
+ children: [
530
+ /* @__PURE__ */ jsx(AvatarPreview, { config, width: 320, height: 400, style: { width: "264px", height: "330px", borderRadius: "3px" } }),
531
+ /* @__PURE__ */ jsx(
532
+ "div",
533
+ {
534
+ style: {
535
+ position: "absolute",
536
+ inset: "10px",
537
+ pointerEvents: "none",
538
+ borderRadius: "3px",
539
+ backgroundImage: "repeating-linear-gradient(0deg, rgba(0,0,0,.16) 0 2px, transparent 2px 4px)",
540
+ mixBlendMode: "multiply"
541
+ }
542
+ }
543
+ ),
544
+ showCombat && /* @__PURE__ */ jsx(
545
+ "div",
546
+ {
547
+ style: {
548
+ position: "absolute",
549
+ right: "14px",
550
+ bottom: "14px",
551
+ fontFamily: t.mono,
552
+ fontSize: "7px",
553
+ letterSpacing: "1px",
554
+ color: t.title,
555
+ background: t.marqueeBg,
556
+ border: "2px solid " + t.cabinetBorder,
557
+ borderRadius: "6px",
558
+ padding: "5px 7px",
559
+ pointerEvents: "none"
560
+ },
561
+ children: "\u2694 FIGHT"
562
+ }
563
+ )
564
+ ]
565
+ }
566
+ ),
567
+ /* @__PURE__ */ jsx(
568
+ "div",
569
+ {
570
+ style: {
571
+ fontFamily: t.mono,
572
+ fontSize: "9px",
573
+ color: t.title,
574
+ letterSpacing: "1px",
575
+ padding: "6px 10px",
576
+ background: t.marqueeBg,
577
+ borderRadius: "5px",
578
+ textAlign: "center",
579
+ border: "2px solid " + t.cabinetBorder
580
+ },
581
+ children: "THE " + nameDisplay + " WEARER"
582
+ }
583
+ ),
584
+ /* @__PURE__ */ jsxs("div", { style: { width: "264px", maxWidth: "100%", display: "flex", flexDirection: "column", gap: "8px", marginTop: "2px" }, children: [
585
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px", width: "100%" }, children: VIEW_OPTIONS.map((o) => /* @__PURE__ */ jsx(
586
+ "button",
587
+ {
588
+ onClick: () => patch({ view: o.key }),
589
+ style: {
590
+ fontFamily: t.mono,
591
+ fontSize: "7px",
592
+ letterSpacing: ".5px",
593
+ cursor: "pointer",
594
+ flex: 1,
595
+ padding: "8px 4px",
596
+ borderRadius: t.paper ? "3px" : "6px",
597
+ border: "2px solid " + (config.view === o.key ? t.accent : t.panelBorder),
598
+ background: config.view === o.key ? t.accent : t.paper ? "#ffffff55" : "#ffffff10",
599
+ color: config.view === o.key ? "#10131c" : t.text
600
+ },
601
+ children: o.name
602
+ },
603
+ o.key
604
+ )) }),
605
+ partRow("mund", "MOUTH"),
606
+ /* @__PURE__ */ jsxs("div", { style: swatchRowStyle, children: [
607
+ /* @__PURE__ */ jsx("span", { style: swatchLabelStyle, children: "BACKGROUND" }),
608
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" }, children: BG.map((c, i) => /* @__PURE__ */ jsx("button", { onClick: () => patch({ bg: i }), style: swatchBtn(c, config.bg === i) }, c)) })
609
+ ] }),
610
+ showCode && /* @__PURE__ */ jsxs(Fragment, { children: [
611
+ /* @__PURE__ */ jsx("div", { style: dividerStyle }),
612
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [
613
+ /* @__PURE__ */ jsx("span", { style: codeLabelStyle, children: "CODE" }),
614
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
615
+ /* @__PURE__ */ jsx("div", { style: codeFieldStyle, children: encodeConfig(config) }),
616
+ /* @__PURE__ */ jsx("button", { onClick: onCopyCode, style: smallBtnStyle, children: "COPY" })
617
+ ] }),
618
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
619
+ /* @__PURE__ */ jsx(
620
+ "input",
621
+ {
622
+ value: codeInput,
623
+ onChange: (e) => {
624
+ setCodeInput(e.target.value);
625
+ setCodeMsg("");
626
+ },
627
+ placeholder: "Paste code\u2026",
628
+ style: codeFieldStyle
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsx("button", { onClick: onLoadCode, style: smallBtnStyle, children: "LOAD" })
632
+ ] }),
633
+ codeMsg && /* @__PURE__ */ jsx("span", { style: { fontFamily: t.mono, fontSize: "7px", color: t.accent }, children: codeMsg })
634
+ ] })
635
+ ] }),
636
+ showSeed && /* @__PURE__ */ jsxs(Fragment, { children: [
637
+ /* @__PURE__ */ jsx("div", { style: dividerStyle }),
638
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [
639
+ /* @__PURE__ */ jsx("span", { style: codeLabelStyle, children: "SEED" }),
640
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
641
+ /* @__PURE__ */ jsx(
642
+ "input",
643
+ {
644
+ value: seedInput,
645
+ onChange: (e) => setSeedInput(e.target.value),
646
+ onKeyDown: (e) => {
647
+ if (e.key === "Enter") onGenerateSeed();
648
+ },
649
+ placeholder: "e.g. your name\u2026",
650
+ style: codeFieldStyle
651
+ }
652
+ ),
653
+ /* @__PURE__ */ jsx("button", { onClick: onGenerateSeed, style: smallBtnStyle, children: "GENERATE" })
654
+ ] })
655
+ ] })
656
+ ] })
657
+ ] })
658
+ ]
659
+ }
660
+ ),
661
+ /* @__PURE__ */ jsxs(
662
+ "div",
663
+ {
664
+ style: {
665
+ flex: 1,
666
+ minWidth: 0,
667
+ width: t.mainDir === "column" ? "100%" : "auto",
668
+ display: "flex",
669
+ flexDirection: "column",
670
+ gap: "9px",
671
+ background: t.panel,
672
+ border: "3px solid " + t.panelBorder,
673
+ borderRadius: t.paper ? "4px" : "10px",
674
+ padding: "14px"
675
+ },
676
+ children: [
677
+ PART_CONTROLS.map((c) => partRow(c.key, c.label)),
678
+ /* @__PURE__ */ jsx("div", { style: dividerStyle }),
679
+ /* @__PURE__ */ jsxs("div", { style: swatchRowStyle, children: [
680
+ /* @__PURE__ */ jsx("span", { style: swatchLabelStyle, children: "SKIN" }),
681
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" }, children: SKIN.map((c, i) => /* @__PURE__ */ jsx("button", { onClick: () => patch({ skin: i }), style: swatchBtn(c, config.skin === i) }, c)) })
682
+ ] }),
683
+ /* @__PURE__ */ jsxs("div", { style: swatchRowStyle, children: [
684
+ /* @__PURE__ */ jsx("span", { style: swatchLabelStyle, children: "CLOTHING" }),
685
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "6px", flexWrap: "wrap" }, children: CLOTH.map((c, i) => /* @__PURE__ */ jsx("button", { onClick: () => patch({ cloth: i }), style: swatchBtn(c, config.cloth === i) }, c)) })
686
+ ] }),
687
+ /* @__PURE__ */ jsx("div", { style: dividerStyle }),
688
+ /* @__PURE__ */ jsx(
689
+ "button",
690
+ {
691
+ onClick: onRandom,
692
+ style: {
693
+ fontFamily: t.mono,
694
+ fontSize: "10px",
695
+ letterSpacing: "1px",
696
+ cursor: "pointer",
697
+ padding: "12px",
698
+ borderRadius: t.paper ? "3px" : "9px",
699
+ marginTop: "2px",
700
+ border: "3px solid " + t.title,
701
+ background: t.accent,
702
+ color: "#10131c"
703
+ },
704
+ children: "\u{1F3B2} RANDOM"
705
+ }
706
+ )
707
+ ]
708
+ }
709
+ )
710
+ ]
711
+ }
712
+ ),
713
+ /* @__PURE__ */ jsxs(
714
+ "div",
715
+ {
716
+ style: {
717
+ background: t.marqueeBg,
718
+ padding: "10px",
719
+ display: "flex",
720
+ gap: "10px",
721
+ alignItems: "center",
722
+ justifyContent: "center",
723
+ color: t.title,
724
+ borderTop: "3px solid " + t.cabinetBorder
725
+ },
726
+ children: [
727
+ /* @__PURE__ */ jsx("span", { style: { color: t.accent, fontSize: "14px", animation: "rak-coinpulse 1.2s ease-in-out infinite" }, children: "\u25CF" }),
728
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: t.mono, fontSize: "8px", letterSpacing: "1px" }, children: "INSERT COIN \xB7 SATIRE EDITION" })
729
+ ]
730
+ }
731
+ )
732
+ ]
733
+ }
734
+ ),
735
+ showCombat && modalOpen && /* @__PURE__ */ jsx(CombatModal, { config, theme: t, onClose: () => setModalOpen(false) })
736
+ ]
737
+ }
738
+ );
739
+ }
740
+
741
+ export { AvatarEditor, AvatarPreview, CombatModal, FONT_HREFS, LAYOUTS, theme };
742
+ //# sourceMappingURL=index.js.map
743
+ //# sourceMappingURL=index.js.map