@runek/core 0.6.0 → 0.10.1
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.d.ts +245 -28
- package/dist/index.js +710 -83
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// src/context.ts
|
|
2
2
|
import { createContext } from "react";
|
|
3
3
|
|
|
4
|
+
// src/font.ts
|
|
5
|
+
var DEFAULT_FONT = "data:font/woff2;base64,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";
|
|
6
|
+
var DEFAULT_FONTS = {
|
|
7
|
+
display: DEFAULT_FONT,
|
|
8
|
+
body: DEFAULT_FONT
|
|
9
|
+
};
|
|
10
|
+
|
|
4
11
|
// src/palette.ts
|
|
5
12
|
var DEFAULT_PALETTE = {
|
|
6
13
|
wood: "#6b4f3a",
|
|
@@ -20,13 +27,101 @@ var DEFAULT_PALETTE = {
|
|
|
20
27
|
waterShallow: "#3f86a8"
|
|
21
28
|
};
|
|
22
29
|
|
|
30
|
+
// src/time.ts
|
|
31
|
+
var DEFAULT_WORLD_TIME = { hours: 12, live: false };
|
|
32
|
+
function parseClockTime(value) {
|
|
33
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
|
|
34
|
+
if (!match) return null;
|
|
35
|
+
const h = Number(match[1]);
|
|
36
|
+
const m = Number(match[2]);
|
|
37
|
+
if (h < 0 || h > 23 || m < 0 || m > 59) return null;
|
|
38
|
+
return h + m / 60;
|
|
39
|
+
}
|
|
40
|
+
function clockHours(timezone) {
|
|
41
|
+
const now = /* @__PURE__ */ new Date();
|
|
42
|
+
if (timezone) {
|
|
43
|
+
try {
|
|
44
|
+
const parts = new Intl.DateTimeFormat("en-GB", {
|
|
45
|
+
timeZone: timezone,
|
|
46
|
+
hour: "2-digit",
|
|
47
|
+
minute: "2-digit",
|
|
48
|
+
hour12: false
|
|
49
|
+
}).formatToParts(now);
|
|
50
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
51
|
+
const h2 = get("hour") % 24;
|
|
52
|
+
const m2 = get("minute");
|
|
53
|
+
if (Number.isFinite(h2) && Number.isFinite(m2)) return h2 + m2 / 60;
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
return now.getUTCHours() + now.getUTCMinutes() / 60;
|
|
57
|
+
}
|
|
58
|
+
const h = now.getHours();
|
|
59
|
+
const m = now.getMinutes();
|
|
60
|
+
if (Number.isFinite(h) && Number.isFinite(m)) return h + m / 60;
|
|
61
|
+
return now.getUTCHours() + now.getUTCMinutes() / 60;
|
|
62
|
+
}
|
|
63
|
+
function resolveWorldTime(opts) {
|
|
64
|
+
if (opts.time !== void 0) {
|
|
65
|
+
const hours = parseClockTime(opts.time);
|
|
66
|
+
if (hours !== null) return { hours, live: false, timezone: opts.timezone };
|
|
67
|
+
}
|
|
68
|
+
return { hours: clockHours(opts.timezone), live: true, timezone: opts.timezone };
|
|
69
|
+
}
|
|
70
|
+
function currentHours(time) {
|
|
71
|
+
return time.live ? clockHours(time.timezone) : time.hours;
|
|
72
|
+
}
|
|
73
|
+
function sunState(hours, radius = 100) {
|
|
74
|
+
const theta = (hours - 6) / 12 * Math.PI;
|
|
75
|
+
const sinEl = Math.sin(theta);
|
|
76
|
+
const elevation = Math.max(0, sinEl);
|
|
77
|
+
const x = Math.cos(theta);
|
|
78
|
+
const y = sinEl;
|
|
79
|
+
const z = 0.3;
|
|
80
|
+
const len = Math.hypot(x, y, z) || 1;
|
|
81
|
+
return {
|
|
82
|
+
position: [x / len * radius, y / len * radius, z / len * radius],
|
|
83
|
+
elevation,
|
|
84
|
+
day: sinEl > 0
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
23
88
|
// src/context.ts
|
|
24
89
|
var WorldContext = createContext({
|
|
25
90
|
unit: 1,
|
|
26
91
|
gravity: [0, -9.81, 0],
|
|
27
|
-
|
|
92
|
+
ground: 0,
|
|
93
|
+
palette: DEFAULT_PALETTE,
|
|
94
|
+
fonts: DEFAULT_FONTS,
|
|
95
|
+
time: DEFAULT_WORLD_TIME
|
|
28
96
|
});
|
|
29
97
|
|
|
98
|
+
// src/contribute.ts
|
|
99
|
+
function parseGitHubSource(source) {
|
|
100
|
+
if (!source?.url) return null;
|
|
101
|
+
let url;
|
|
102
|
+
try {
|
|
103
|
+
url = new URL(source.url);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
if (url.hostname !== "github.com" && url.hostname !== "www.github.com") return null;
|
|
108
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
109
|
+
if (parts.length < 2) return null;
|
|
110
|
+
return {
|
|
111
|
+
owner: parts[0],
|
|
112
|
+
repo: parts[1].replace(/\.git$/, ""),
|
|
113
|
+
branch: source.branch?.trim() || "main",
|
|
114
|
+
path: source.path?.trim() || void 0
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function forkUrl(gh) {
|
|
118
|
+
return `https://github.com/${gh.owner}/${gh.repo}/fork`;
|
|
119
|
+
}
|
|
120
|
+
function editFileUrl(gh) {
|
|
121
|
+
if (!gh.path) return null;
|
|
122
|
+
return `https://github.com/${gh.owner}/${gh.repo}/edit/${gh.branch}/${gh.path}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
30
125
|
// src/keyboard.ts
|
|
31
126
|
var keyboardMap = [
|
|
32
127
|
{ name: "forward", keys: ["ArrowUp", "KeyW"] },
|
|
@@ -64,50 +159,240 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
64
159
|
function World({
|
|
65
160
|
unit = 1,
|
|
66
161
|
gravity = [0, -9.81, 0],
|
|
162
|
+
ground = 0,
|
|
67
163
|
keyboardMap: keyboardMap2 = keyboardMap,
|
|
68
164
|
lights = true,
|
|
69
165
|
palette,
|
|
166
|
+
fonts,
|
|
70
167
|
fog,
|
|
168
|
+
time,
|
|
169
|
+
timezone,
|
|
170
|
+
avatar,
|
|
71
171
|
onPointerMissed,
|
|
172
|
+
preserveDrawingBuffer = false,
|
|
72
173
|
debug = false,
|
|
73
174
|
children
|
|
74
175
|
}) {
|
|
75
176
|
const context = useMemo(
|
|
76
|
-
() => ({
|
|
77
|
-
|
|
177
|
+
() => ({
|
|
178
|
+
unit,
|
|
179
|
+
gravity,
|
|
180
|
+
ground,
|
|
181
|
+
palette: { ...DEFAULT_PALETTE, ...palette },
|
|
182
|
+
fonts: { ...DEFAULT_FONTS, ...fonts },
|
|
183
|
+
time: resolveWorldTime({ time, timezone }),
|
|
184
|
+
avatar
|
|
185
|
+
}),
|
|
186
|
+
[unit, gravity, ground, palette, fonts, time, timezone, avatar]
|
|
78
187
|
);
|
|
79
|
-
return /* @__PURE__ */ jsx(KeyboardControls, { map: keyboardMap2, children: /* @__PURE__ */ jsx(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
188
|
+
return /* @__PURE__ */ jsx(KeyboardControls, { map: keyboardMap2, children: /* @__PURE__ */ jsx(
|
|
189
|
+
Canvas,
|
|
190
|
+
{
|
|
191
|
+
shadows: true,
|
|
192
|
+
camera: { position: [6, 4, 6], fov: 60 },
|
|
193
|
+
gl: { preserveDrawingBuffer },
|
|
194
|
+
onPointerMissed,
|
|
195
|
+
children: /* @__PURE__ */ jsxs(WorldContext.Provider, { value: context, children: [
|
|
196
|
+
fog && /* @__PURE__ */ jsx("fog", { attach: "fog", args: [fog.color, fog.near * unit, fog.far * unit] }),
|
|
197
|
+
lights && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
198
|
+
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
199
|
+
/* @__PURE__ */ jsx(
|
|
200
|
+
"directionalLight",
|
|
201
|
+
{
|
|
202
|
+
position: [12, 18, 8],
|
|
203
|
+
intensity: 1.6,
|
|
204
|
+
castShadow: true,
|
|
205
|
+
"shadow-mapSize": [2048, 2048],
|
|
206
|
+
"shadow-camera-near": 1,
|
|
207
|
+
"shadow-camera-far": 60,
|
|
208
|
+
"shadow-camera-left": -25,
|
|
209
|
+
"shadow-camera-right": 25,
|
|
210
|
+
"shadow-camera-top": 25,
|
|
211
|
+
"shadow-camera-bottom": -25
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsx(Physics, { gravity, debug, children })
|
|
216
|
+
] })
|
|
217
|
+
}
|
|
218
|
+
) });
|
|
101
219
|
}
|
|
102
220
|
|
|
103
|
-
// src/
|
|
104
|
-
import {
|
|
105
|
-
import {
|
|
106
|
-
|
|
221
|
+
// src/WorldAbout.tsx
|
|
222
|
+
import { useEffect, useState } from "react";
|
|
223
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
224
|
+
function WorldAbout({ meta }) {
|
|
225
|
+
const [open, setOpen] = useState(false);
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (!open) return;
|
|
228
|
+
const onKey = (event) => {
|
|
229
|
+
if (event.key === "Escape") setOpen(false);
|
|
230
|
+
};
|
|
231
|
+
window.addEventListener("keydown", onKey);
|
|
232
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
233
|
+
}, [open]);
|
|
234
|
+
const title = meta?.title?.trim() || "Untitled world";
|
|
235
|
+
const authors = meta?.authors ?? [];
|
|
236
|
+
const source = meta?.source;
|
|
237
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
238
|
+
/* @__PURE__ */ jsx2(
|
|
239
|
+
"button",
|
|
240
|
+
{
|
|
241
|
+
type: "button",
|
|
242
|
+
style: INFO_BTN,
|
|
243
|
+
onClick: () => setOpen(true),
|
|
244
|
+
title: "About this world",
|
|
245
|
+
"aria-label": "About this world",
|
|
246
|
+
children: "\u24D8"
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
open && /* @__PURE__ */ jsxs2("div", { style: OVERLAY, role: "dialog", "aria-modal": "true", "aria-label": "About this world", children: [
|
|
250
|
+
/* @__PURE__ */ jsx2("button", { type: "button", style: SCRIM, "aria-label": "Close", onClick: () => setOpen(false) }),
|
|
251
|
+
/* @__PURE__ */ jsxs2("div", { style: CARD, children: [
|
|
252
|
+
/* @__PURE__ */ jsx2("button", { type: "button", style: CLOSE, onClick: () => setOpen(false), "aria-label": "Close", children: "\xD7" }),
|
|
253
|
+
/* @__PURE__ */ jsx2("h2", { style: TITLE, children: title }),
|
|
254
|
+
meta?.description && /* @__PURE__ */ jsx2("p", { style: DESC, children: meta.description }),
|
|
255
|
+
authors.length > 0 && /* @__PURE__ */ jsxs2("p", { style: ROW, children: [
|
|
256
|
+
/* @__PURE__ */ jsx2("span", { style: LABEL, children: "by " }),
|
|
257
|
+
authors.map((author, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
258
|
+
index > 0 && ", ",
|
|
259
|
+
author.url ? /* @__PURE__ */ jsx2("a", { style: LINK, href: author.url, target: "_blank", rel: "noreferrer", children: author.name }) : author.name
|
|
260
|
+
] }, `${author.name}-${index}`))
|
|
261
|
+
] }),
|
|
262
|
+
meta?.license && /* @__PURE__ */ jsxs2("p", { style: ROW, children: [
|
|
263
|
+
/* @__PURE__ */ jsx2("span", { style: LABEL, children: "license " }),
|
|
264
|
+
meta.license
|
|
265
|
+
] }),
|
|
266
|
+
source?.url && /* @__PURE__ */ jsx2("p", { style: ROW, children: /* @__PURE__ */ jsx2("a", { style: LINK, href: source.url, target: "_blank", rel: "noreferrer", children: "View source repository \u2197" }) }),
|
|
267
|
+
/* @__PURE__ */ jsx2("p", { style: BUILT, children: /* @__PURE__ */ jsx2(
|
|
268
|
+
"a",
|
|
269
|
+
{
|
|
270
|
+
style: BUILT_LINK,
|
|
271
|
+
href: "https://runek.nullorder.org",
|
|
272
|
+
target: "_blank",
|
|
273
|
+
rel: "noreferrer",
|
|
274
|
+
children: "Built with Runek"
|
|
275
|
+
}
|
|
276
|
+
) })
|
|
277
|
+
] })
|
|
278
|
+
] })
|
|
279
|
+
] });
|
|
280
|
+
}
|
|
281
|
+
var MONO = "ui-monospace, SF Mono, Menlo, monospace";
|
|
282
|
+
var PANEL_BG = "rgba(7, 11, 17, 0.95)";
|
|
283
|
+
var BORDER = "#15202a";
|
|
284
|
+
var INFO_BTN = {
|
|
285
|
+
position: "fixed",
|
|
286
|
+
top: "1rem",
|
|
287
|
+
right: "1rem",
|
|
288
|
+
zIndex: 10,
|
|
289
|
+
width: 32,
|
|
290
|
+
height: 32,
|
|
291
|
+
borderRadius: "50%",
|
|
292
|
+
border: `1px solid ${BORDER}`,
|
|
293
|
+
background: "rgba(7, 11, 17, 0.82)",
|
|
294
|
+
color: "#cfe6db",
|
|
295
|
+
backdropFilter: "blur(8px)",
|
|
296
|
+
cursor: "pointer",
|
|
297
|
+
fontSize: "0.95rem",
|
|
298
|
+
lineHeight: 1,
|
|
299
|
+
fontFamily: MONO
|
|
300
|
+
};
|
|
301
|
+
var OVERLAY = {
|
|
302
|
+
position: "fixed",
|
|
303
|
+
inset: 0,
|
|
304
|
+
zIndex: 20,
|
|
305
|
+
display: "flex",
|
|
306
|
+
alignItems: "center",
|
|
307
|
+
justifyContent: "center",
|
|
308
|
+
padding: "1rem"
|
|
309
|
+
};
|
|
310
|
+
var SCRIM = {
|
|
311
|
+
position: "absolute",
|
|
312
|
+
inset: 0,
|
|
313
|
+
border: "none",
|
|
314
|
+
padding: 0,
|
|
315
|
+
margin: 0,
|
|
316
|
+
background: "rgba(3, 5, 10, 0.6)",
|
|
317
|
+
backdropFilter: "blur(2px)",
|
|
318
|
+
cursor: "default"
|
|
319
|
+
};
|
|
320
|
+
var CARD = {
|
|
321
|
+
position: "relative",
|
|
322
|
+
zIndex: 1,
|
|
323
|
+
width: "100%",
|
|
324
|
+
maxWidth: 420,
|
|
325
|
+
padding: "1.5rem 1.6rem",
|
|
326
|
+
borderRadius: 12,
|
|
327
|
+
background: PANEL_BG,
|
|
328
|
+
border: `1px solid ${BORDER}`,
|
|
329
|
+
color: "#cfe6db",
|
|
330
|
+
fontFamily: MONO,
|
|
331
|
+
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.5)"
|
|
332
|
+
};
|
|
333
|
+
var CLOSE = {
|
|
334
|
+
position: "absolute",
|
|
335
|
+
top: "0.7rem",
|
|
336
|
+
right: "0.8rem",
|
|
337
|
+
border: "none",
|
|
338
|
+
background: "transparent",
|
|
339
|
+
color: "#5f7d75",
|
|
340
|
+
cursor: "pointer",
|
|
341
|
+
fontSize: "1.1rem",
|
|
342
|
+
lineHeight: 1,
|
|
343
|
+
fontFamily: MONO
|
|
344
|
+
};
|
|
345
|
+
var TITLE = {
|
|
346
|
+
margin: "0 0 0.5rem",
|
|
347
|
+
fontSize: "1.15rem",
|
|
348
|
+
fontWeight: 600,
|
|
349
|
+
color: "#3df58a"
|
|
350
|
+
};
|
|
351
|
+
var DESC = {
|
|
352
|
+
margin: "0 0 0.9rem",
|
|
353
|
+
fontSize: "0.85rem",
|
|
354
|
+
lineHeight: 1.5
|
|
355
|
+
};
|
|
356
|
+
var ROW = {
|
|
357
|
+
margin: "0 0 0.4rem",
|
|
358
|
+
fontSize: "0.8rem"
|
|
359
|
+
};
|
|
360
|
+
var LABEL = { color: "#5f7d75" };
|
|
361
|
+
var LINK = { color: "#2aa7ff", textDecoration: "none" };
|
|
362
|
+
var BUILT = {
|
|
363
|
+
margin: "1.1rem 0 0",
|
|
364
|
+
paddingTop: "0.8rem",
|
|
365
|
+
borderTop: `1px solid ${BORDER}`,
|
|
366
|
+
fontSize: "0.72rem",
|
|
367
|
+
color: "#5f7d75"
|
|
368
|
+
};
|
|
369
|
+
var BUILT_LINK = { color: "#5f7d75", textDecoration: "none" };
|
|
370
|
+
|
|
371
|
+
// src/WorldContribute.tsx
|
|
372
|
+
import { useEffect as useEffect2 } from "react";
|
|
107
373
|
|
|
108
374
|
// src/world-data.ts
|
|
375
|
+
function normalizeNode(node) {
|
|
376
|
+
const out = { type: node.type };
|
|
377
|
+
if (node.id !== void 0) out.id = node.id;
|
|
378
|
+
if (node.props !== void 0) out.props = node.props;
|
|
379
|
+
if (node.children !== void 0) out.children = node.children.map(normalizeNode);
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
109
382
|
function serializeWorld(data) {
|
|
110
|
-
|
|
383
|
+
const out = { version: data.version };
|
|
384
|
+
if (data.meta !== void 0) out.meta = data.meta;
|
|
385
|
+
if (data.unit !== void 0) out.unit = data.unit;
|
|
386
|
+
if (data.gravity !== void 0) out.gravity = data.gravity;
|
|
387
|
+
if (data.ground !== void 0) out.ground = data.ground;
|
|
388
|
+
if (data.time !== void 0) out.time = data.time;
|
|
389
|
+
if (data.timezone !== void 0) out.timezone = data.timezone;
|
|
390
|
+
if (data.avatar !== void 0) out.avatar = data.avatar;
|
|
391
|
+
if (data.palette !== void 0) out.palette = data.palette;
|
|
392
|
+
if (data.fonts !== void 0) out.fonts = data.fonts;
|
|
393
|
+
if (data.fog !== void 0) out.fog = data.fog;
|
|
394
|
+
out.nodes = data.nodes.map(normalizeNode);
|
|
395
|
+
return `${JSON.stringify(out, null, 2)}
|
|
111
396
|
`;
|
|
112
397
|
}
|
|
113
398
|
function parseWorld(json) {
|
|
@@ -120,23 +405,316 @@ function parseWorld(json) {
|
|
|
120
405
|
if (!Array.isArray(data.nodes)) {
|
|
121
406
|
throw new Error('World data must have a "nodes" array');
|
|
122
407
|
}
|
|
408
|
+
if (data.meta !== void 0) {
|
|
409
|
+
const meta = data.meta;
|
|
410
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) {
|
|
411
|
+
throw new Error('World "meta" must be an object');
|
|
412
|
+
}
|
|
413
|
+
if (meta.authors !== void 0 && !Array.isArray(meta.authors)) {
|
|
414
|
+
throw new Error('World "meta.authors" must be an array');
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (data.time !== void 0 && typeof data.time !== "string") {
|
|
418
|
+
throw new Error('World "time" must be an "HH:MM" string');
|
|
419
|
+
}
|
|
420
|
+
if (data.timezone !== void 0 && typeof data.timezone !== "string") {
|
|
421
|
+
throw new Error('World "timezone" must be a string');
|
|
422
|
+
}
|
|
423
|
+
if (data.avatar !== void 0 && data.avatar !== "first" && data.avatar !== "third") {
|
|
424
|
+
throw new Error('World "avatar" must be "first" or "third"');
|
|
425
|
+
}
|
|
426
|
+
if (data.ground !== void 0 && typeof data.ground !== "number") {
|
|
427
|
+
throw new Error('World "ground" must be a number');
|
|
428
|
+
}
|
|
429
|
+
if (data.fonts !== void 0) {
|
|
430
|
+
const fonts = data.fonts;
|
|
431
|
+
if (typeof fonts !== "object" || fonts === null || Array.isArray(fonts)) {
|
|
432
|
+
throw new Error('World "fonts" must be an object of role to font URL');
|
|
433
|
+
}
|
|
434
|
+
for (const value of Object.values(fonts)) {
|
|
435
|
+
if (typeof value !== "string") {
|
|
436
|
+
throw new Error('World "fonts" values must be font URL strings');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
123
440
|
return data;
|
|
124
441
|
}
|
|
442
|
+
function collectIds(nodes, into) {
|
|
443
|
+
for (const node of nodes) {
|
|
444
|
+
if (node.id) into.add(node.id);
|
|
445
|
+
if (node.children) collectIds(node.children, into);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function makeNodeId(taken) {
|
|
449
|
+
let id;
|
|
450
|
+
do {
|
|
451
|
+
id = `n${Math.random().toString(36).slice(2, 8)}`;
|
|
452
|
+
} while (taken.has(id));
|
|
453
|
+
taken.add(id);
|
|
454
|
+
return id;
|
|
455
|
+
}
|
|
456
|
+
function withIds(nodes, taken) {
|
|
457
|
+
return nodes.map((node) => ({
|
|
458
|
+
...node,
|
|
459
|
+
id: node.id ?? makeNodeId(taken),
|
|
460
|
+
...node.children ? { children: withIds(node.children, taken) } : {}
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
463
|
+
function assignNodeIds(data) {
|
|
464
|
+
const taken = /* @__PURE__ */ new Set();
|
|
465
|
+
collectIds(data.nodes, taken);
|
|
466
|
+
return { ...data, nodes: withIds(data.nodes, taken) };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/WorldContribute.tsx
|
|
470
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
471
|
+
function downloadHref(href, filename) {
|
|
472
|
+
const a = document.createElement("a");
|
|
473
|
+
a.href = href;
|
|
474
|
+
a.download = filename;
|
|
475
|
+
a.click();
|
|
476
|
+
}
|
|
477
|
+
function downloadText(text, filename) {
|
|
478
|
+
const url = URL.createObjectURL(new Blob([text], { type: "application/json" }));
|
|
479
|
+
downloadHref(url, filename);
|
|
480
|
+
URL.revokeObjectURL(url);
|
|
481
|
+
}
|
|
482
|
+
function captureSnapshot() {
|
|
483
|
+
const canvas = document.querySelector("canvas");
|
|
484
|
+
if (!canvas) return null;
|
|
485
|
+
try {
|
|
486
|
+
return canvas.toDataURL("image/png");
|
|
487
|
+
} catch {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function WorldContribute({ data, onClose }) {
|
|
492
|
+
const source = data.meta?.source;
|
|
493
|
+
const gh = parseGitHubSource(source);
|
|
494
|
+
const editUrl = gh ? editFileUrl(gh) : null;
|
|
495
|
+
useEffect2(() => {
|
|
496
|
+
const onKey = (event) => {
|
|
497
|
+
if (event.key === "Escape") onClose();
|
|
498
|
+
};
|
|
499
|
+
window.addEventListener("keydown", onKey);
|
|
500
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
501
|
+
}, [onClose]);
|
|
502
|
+
const downloadJson = () => downloadText(serializeWorld(data), "world.json");
|
|
503
|
+
const downloadSnapshot = () => {
|
|
504
|
+
const png = captureSnapshot();
|
|
505
|
+
if (png) downloadHref(png, "world-snapshot.png");
|
|
506
|
+
};
|
|
507
|
+
const open = (url) => window.open(url, "_blank", "noopener,noreferrer");
|
|
508
|
+
return /* @__PURE__ */ jsxs3("div", { style: OVERLAY2, role: "dialog", "aria-modal": "true", "aria-label": "Contribute this world", children: [
|
|
509
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: SCRIM2, "aria-label": "Close", onClick: onClose }),
|
|
510
|
+
/* @__PURE__ */ jsxs3("div", { style: CARD2, children: [
|
|
511
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: CLOSE2, onClick: onClose, "aria-label": "Close", children: "\xD7" }),
|
|
512
|
+
/* @__PURE__ */ jsx3("h2", { style: TITLE2, children: "Contribute this world" }),
|
|
513
|
+
/* @__PURE__ */ jsx3("p", { style: INTRO, children: "Runek hands your edit to GitHub directly, no account or token needed. Your change becomes a normal pull request the world's owner reviews." }),
|
|
514
|
+
gh ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
515
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: SECONDARY, onClick: () => open(forkUrl(gh)), children: "Fork this world \u2197" }),
|
|
516
|
+
/* @__PURE__ */ jsx3("p", { style: HINT, children: "Get your own deployable copy to build on." }),
|
|
517
|
+
/* @__PURE__ */ jsx3("div", { style: DIVIDER }),
|
|
518
|
+
/* @__PURE__ */ jsxs3("ol", { style: STEPS, children: [
|
|
519
|
+
/* @__PURE__ */ jsxs3(Step, { n: 1, children: [
|
|
520
|
+
"Download your edits and a snapshot of this view.",
|
|
521
|
+
/* @__PURE__ */ jsxs3("div", { style: ACTIONS, children: [
|
|
522
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: PRIMARY, onClick: downloadJson, children: "Download world.json" }),
|
|
523
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: PRIMARY, onClick: downloadSnapshot, children: "Download snapshot.png" })
|
|
524
|
+
] })
|
|
525
|
+
] }),
|
|
526
|
+
/* @__PURE__ */ jsxs3(Step, { n: 2, children: [
|
|
527
|
+
"Open the world file on GitHub (it forks the repo for you automatically).",
|
|
528
|
+
/* @__PURE__ */ jsx3("div", { style: ACTIONS, children: editUrl ? /* @__PURE__ */ jsx3("button", { type: "button", style: PRIMARY, onClick: () => open(editUrl), children: "Open GitHub editor \u2192" }) : /* @__PURE__ */ jsx3(
|
|
529
|
+
"button",
|
|
530
|
+
{
|
|
531
|
+
type: "button",
|
|
532
|
+
style: PRIMARY,
|
|
533
|
+
onClick: () => source && open(source.url),
|
|
534
|
+
children: "Open repository \u2197"
|
|
535
|
+
}
|
|
536
|
+
) }),
|
|
537
|
+
!editUrl && /* @__PURE__ */ jsxs3("span", { style: NOTE, children: [
|
|
538
|
+
"The world's file path isn't set in ",
|
|
539
|
+
/* @__PURE__ */ jsx3("code", { children: "meta.source.path" }),
|
|
540
|
+
", so open the repo and edit the world file directly."
|
|
541
|
+
] })
|
|
542
|
+
] }),
|
|
543
|
+
/* @__PURE__ */ jsx3(Step, { n: 3, children: "In GitHub's editor: select all, paste the downloaded JSON, and commit to a new branch." }),
|
|
544
|
+
/* @__PURE__ */ jsx3(Step, { n: 4, children: "GitHub offers \u201CPropose changes\u201D \u2192 open the pull request. Attach the snapshot so the owner can see the change a JSON diff can't show." })
|
|
545
|
+
] })
|
|
546
|
+
] }) : /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
547
|
+
/* @__PURE__ */ jsx3("p", { style: INTRO, children: "This world's repository isn't on GitHub. Download your changes and open the repo to contribute however it accepts them." }),
|
|
548
|
+
/* @__PURE__ */ jsxs3("div", { style: ACTIONS, children: [
|
|
549
|
+
/* @__PURE__ */ jsx3("button", { type: "button", style: PRIMARY, onClick: downloadJson, children: "Download world.json" }),
|
|
550
|
+
source?.url && /* @__PURE__ */ jsx3("button", { type: "button", style: PRIMARY, onClick: () => open(source.url), children: "Open repository \u2197" })
|
|
551
|
+
] })
|
|
552
|
+
] })
|
|
553
|
+
] })
|
|
554
|
+
] });
|
|
555
|
+
}
|
|
556
|
+
function Step({ n, children }) {
|
|
557
|
+
return /* @__PURE__ */ jsxs3("li", { style: STEP, children: [
|
|
558
|
+
/* @__PURE__ */ jsx3("span", { style: STEP_NUM, children: n }),
|
|
559
|
+
/* @__PURE__ */ jsx3("div", { children })
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
var MONO2 = "ui-monospace, SF Mono, Menlo, monospace";
|
|
563
|
+
var BORDER2 = "#15202a";
|
|
564
|
+
var GREEN = "#3df58a";
|
|
565
|
+
var OVERLAY2 = {
|
|
566
|
+
position: "fixed",
|
|
567
|
+
inset: 0,
|
|
568
|
+
zIndex: 20,
|
|
569
|
+
display: "flex",
|
|
570
|
+
alignItems: "center",
|
|
571
|
+
justifyContent: "center",
|
|
572
|
+
padding: "1rem"
|
|
573
|
+
};
|
|
574
|
+
var SCRIM2 = {
|
|
575
|
+
position: "absolute",
|
|
576
|
+
inset: 0,
|
|
577
|
+
border: "none",
|
|
578
|
+
padding: 0,
|
|
579
|
+
margin: 0,
|
|
580
|
+
background: "rgba(3, 5, 10, 0.6)",
|
|
581
|
+
backdropFilter: "blur(2px)",
|
|
582
|
+
cursor: "default"
|
|
583
|
+
};
|
|
584
|
+
var CARD2 = {
|
|
585
|
+
position: "relative",
|
|
586
|
+
zIndex: 1,
|
|
587
|
+
width: "100%",
|
|
588
|
+
maxWidth: 480,
|
|
589
|
+
maxHeight: "90vh",
|
|
590
|
+
overflowY: "auto",
|
|
591
|
+
padding: "1.5rem 1.6rem",
|
|
592
|
+
borderRadius: 12,
|
|
593
|
+
background: "rgba(7, 11, 17, 0.97)",
|
|
594
|
+
border: `1px solid ${BORDER2}`,
|
|
595
|
+
color: "#cfe6db",
|
|
596
|
+
fontFamily: MONO2,
|
|
597
|
+
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.5)"
|
|
598
|
+
};
|
|
599
|
+
var CLOSE2 = {
|
|
600
|
+
position: "absolute",
|
|
601
|
+
top: "0.7rem",
|
|
602
|
+
right: "0.8rem",
|
|
603
|
+
border: "none",
|
|
604
|
+
background: "transparent",
|
|
605
|
+
color: "#5f7d75",
|
|
606
|
+
cursor: "pointer",
|
|
607
|
+
fontSize: "1.1rem",
|
|
608
|
+
lineHeight: 1,
|
|
609
|
+
fontFamily: MONO2
|
|
610
|
+
};
|
|
611
|
+
var TITLE2 = {
|
|
612
|
+
margin: "0 0 0.5rem",
|
|
613
|
+
fontSize: "1.15rem",
|
|
614
|
+
fontWeight: 600,
|
|
615
|
+
color: GREEN
|
|
616
|
+
};
|
|
617
|
+
var INTRO = {
|
|
618
|
+
margin: "0 0 1rem",
|
|
619
|
+
fontSize: "0.82rem",
|
|
620
|
+
lineHeight: 1.5,
|
|
621
|
+
color: "#cfe6db"
|
|
622
|
+
};
|
|
623
|
+
var HINT = {
|
|
624
|
+
margin: "0.35rem 0 0",
|
|
625
|
+
fontSize: "0.72rem",
|
|
626
|
+
color: "#5f7d75"
|
|
627
|
+
};
|
|
628
|
+
var NOTE = {
|
|
629
|
+
display: "block",
|
|
630
|
+
marginTop: "0.4rem",
|
|
631
|
+
fontSize: "0.72rem",
|
|
632
|
+
color: "#5f7d75"
|
|
633
|
+
};
|
|
634
|
+
var DIVIDER = {
|
|
635
|
+
height: 1,
|
|
636
|
+
margin: "1.1rem 0",
|
|
637
|
+
background: BORDER2
|
|
638
|
+
};
|
|
639
|
+
var STEPS = {
|
|
640
|
+
margin: 0,
|
|
641
|
+
padding: 0,
|
|
642
|
+
listStyle: "none",
|
|
643
|
+
display: "flex",
|
|
644
|
+
flexDirection: "column",
|
|
645
|
+
gap: "0.9rem"
|
|
646
|
+
};
|
|
647
|
+
var STEP = {
|
|
648
|
+
display: "flex",
|
|
649
|
+
gap: "0.6rem",
|
|
650
|
+
fontSize: "0.82rem",
|
|
651
|
+
lineHeight: 1.5
|
|
652
|
+
};
|
|
653
|
+
var STEP_NUM = {
|
|
654
|
+
flex: "0 0 auto",
|
|
655
|
+
width: 20,
|
|
656
|
+
height: 20,
|
|
657
|
+
borderRadius: "50%",
|
|
658
|
+
border: `1px solid ${GREEN}`,
|
|
659
|
+
color: GREEN,
|
|
660
|
+
fontSize: "0.72rem",
|
|
661
|
+
display: "inline-flex",
|
|
662
|
+
alignItems: "center",
|
|
663
|
+
justifyContent: "center",
|
|
664
|
+
marginTop: "0.05rem"
|
|
665
|
+
};
|
|
666
|
+
var ACTIONS = {
|
|
667
|
+
display: "flex",
|
|
668
|
+
flexWrap: "wrap",
|
|
669
|
+
gap: "0.4rem",
|
|
670
|
+
marginTop: "0.5rem"
|
|
671
|
+
};
|
|
672
|
+
var PRIMARY = {
|
|
673
|
+
padding: "0.35rem 0.65rem",
|
|
674
|
+
borderRadius: 6,
|
|
675
|
+
border: `1px solid rgba(61, 245, 138, 0.5)`,
|
|
676
|
+
background: "rgba(61, 245, 138, 0.14)",
|
|
677
|
+
color: GREEN,
|
|
678
|
+
cursor: "pointer",
|
|
679
|
+
fontSize: "0.76rem",
|
|
680
|
+
fontFamily: MONO2
|
|
681
|
+
};
|
|
682
|
+
var SECONDARY = {
|
|
683
|
+
padding: "0.4rem 0.7rem",
|
|
684
|
+
borderRadius: 6,
|
|
685
|
+
border: `1px solid ${BORDER2}`,
|
|
686
|
+
background: "rgba(255, 255, 255, 0.04)",
|
|
687
|
+
color: "#cfe6db",
|
|
688
|
+
cursor: "pointer",
|
|
689
|
+
fontSize: "0.8rem",
|
|
690
|
+
fontFamily: MONO2
|
|
691
|
+
};
|
|
125
692
|
|
|
126
693
|
// src/WorldEditor.tsx
|
|
127
|
-
import {
|
|
694
|
+
import { OrbitControls, TransformControls } from "@react-three/drei";
|
|
695
|
+
import { Leva, useControls } from "leva";
|
|
696
|
+
import { useEffect as useEffect3, useRef, useState as useState2 } from "react";
|
|
697
|
+
import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
128
698
|
var NON_SELECTABLE = /* @__PURE__ */ new Set(["Sky", "LightRig"]);
|
|
129
699
|
var SKIPPED = /* @__PURE__ */ new Set(["Player"]);
|
|
130
700
|
var HISTORY_LIMIT = 100;
|
|
131
701
|
var asVec3 = (value) => Array.isArray(value) && value.length === 3 ? value : void 0;
|
|
702
|
+
var needsIds = (nodes) => nodes.some((node) => !node.id || (node.children ? needsIds(node.children) : false));
|
|
703
|
+
var stripIds = (node) => ({
|
|
704
|
+
...node,
|
|
705
|
+
id: void 0,
|
|
706
|
+
...node.children ? { children: node.children.map(stripIds) } : {}
|
|
707
|
+
});
|
|
132
708
|
var isTyping = (target) => {
|
|
133
709
|
const el = target;
|
|
134
710
|
return !!el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable);
|
|
135
711
|
};
|
|
136
712
|
function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
137
|
-
const [selected, setSelected] =
|
|
138
|
-
const [mode, setMode] =
|
|
713
|
+
const [selected, setSelected] = useState2(null);
|
|
714
|
+
const [mode, setMode] = useState2("translate");
|
|
715
|
+
const [contributeOpen, setContributeOpen] = useState2(false);
|
|
139
716
|
const history = useRef([]);
|
|
717
|
+
const canContribute = !!data.meta?.source?.url;
|
|
140
718
|
const apply = (next) => {
|
|
141
719
|
history.current.push(data);
|
|
142
720
|
if (history.current.length > HISTORY_LIMIT) history.current.shift();
|
|
@@ -155,16 +733,18 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
155
733
|
apply({ ...data, nodes });
|
|
156
734
|
};
|
|
157
735
|
const addNode = (type) => {
|
|
158
|
-
apply(
|
|
736
|
+
apply(
|
|
737
|
+
assignNodeIds({ ...data, nodes: [...data.nodes, { type, props: { position: [0, 0, 0] } }] })
|
|
738
|
+
);
|
|
159
739
|
setSelected(null);
|
|
160
740
|
};
|
|
161
741
|
const duplicateSelected = () => {
|
|
162
742
|
if (!selected) return;
|
|
163
743
|
const source = data.nodes[selected.index];
|
|
164
|
-
const copy = JSON.parse(JSON.stringify(source));
|
|
744
|
+
const copy = stripIds(JSON.parse(JSON.stringify(source)));
|
|
165
745
|
const at = asVec3(copy.props?.position) ?? [0, 0, 0];
|
|
166
746
|
copy.props = { ...copy.props, position: [at[0] + 0.5, at[1], at[2] + 0.5] };
|
|
167
|
-
apply({ ...data, nodes: [...data.nodes, copy] });
|
|
747
|
+
apply(assignNodeIds({ ...data, nodes: [...data.nodes, copy] }));
|
|
168
748
|
setSelected(null);
|
|
169
749
|
};
|
|
170
750
|
const deleteSelected = () => {
|
|
@@ -181,7 +761,10 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
181
761
|
rotation: [r(rotation.x), r(rotation.y), r(rotation.z)]
|
|
182
762
|
});
|
|
183
763
|
};
|
|
184
|
-
|
|
764
|
+
useEffect3(() => {
|
|
765
|
+
if (needsIds(data.nodes)) onChange(assignNodeIds(data));
|
|
766
|
+
}, [data, onChange]);
|
|
767
|
+
useEffect3(() => {
|
|
185
768
|
const onKey = (event) => {
|
|
186
769
|
if (isTyping(event.target)) return;
|
|
187
770
|
if ((event.metaKey || event.ctrlKey) && event.key === "z") {
|
|
@@ -198,19 +781,25 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
198
781
|
window.addEventListener("keydown", onKey);
|
|
199
782
|
return () => window.removeEventListener("keydown", onKey);
|
|
200
783
|
}, [data, selected, onChange]);
|
|
201
|
-
return /* @__PURE__ */
|
|
202
|
-
/* @__PURE__ */
|
|
784
|
+
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
785
|
+
/* @__PURE__ */ jsxs4(
|
|
203
786
|
World,
|
|
204
787
|
{
|
|
205
788
|
...worldProps,
|
|
206
789
|
unit: data.unit,
|
|
207
790
|
gravity: data.gravity,
|
|
791
|
+
ground: data.ground,
|
|
208
792
|
palette: data.palette,
|
|
793
|
+
fonts: data.fonts,
|
|
209
794
|
fog: data.fog,
|
|
795
|
+
time: data.time,
|
|
796
|
+
timezone: data.timezone,
|
|
797
|
+
avatar: data.avatar,
|
|
798
|
+
preserveDrawingBuffer: canContribute,
|
|
210
799
|
onPointerMissed: () => setSelected(null),
|
|
211
800
|
children: [
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
/* @__PURE__ */
|
|
801
|
+
/* @__PURE__ */ jsx4(OrbitControls, { makeDefault: true }),
|
|
802
|
+
/* @__PURE__ */ jsx4(
|
|
214
803
|
EditableNodes,
|
|
215
804
|
{
|
|
216
805
|
nodes: data.nodes,
|
|
@@ -218,12 +807,14 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
218
807
|
onSelect: (index, object) => setSelected({ index, object })
|
|
219
808
|
}
|
|
220
809
|
),
|
|
221
|
-
selected && /* @__PURE__ */
|
|
810
|
+
selected && /* @__PURE__ */ jsx4(TransformControls, { object: selected.object, mode, onMouseUp: commitTransform })
|
|
222
811
|
]
|
|
223
812
|
}
|
|
224
813
|
),
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
-
/* @__PURE__ */
|
|
814
|
+
/* @__PURE__ */ jsx4(Leva, { hidden: selected === null }),
|
|
815
|
+
/* @__PURE__ */ jsx4(WorldAbout, { meta: data.meta }),
|
|
816
|
+
contributeOpen && /* @__PURE__ */ jsx4(WorldContribute, { data, onClose: () => setContributeOpen(false) }),
|
|
817
|
+
/* @__PURE__ */ jsx4(
|
|
227
818
|
EditorToolbar,
|
|
228
819
|
{
|
|
229
820
|
mode,
|
|
@@ -232,13 +823,15 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
232
823
|
registry,
|
|
233
824
|
selected: selected?.index ?? null,
|
|
234
825
|
canUndo: history.current.length > 0,
|
|
826
|
+
canContribute,
|
|
235
827
|
onAdd: addNode,
|
|
236
828
|
onDuplicate: duplicateSelected,
|
|
237
829
|
onDelete: deleteSelected,
|
|
238
|
-
onUndo: undo
|
|
830
|
+
onUndo: undo,
|
|
831
|
+
onContribute: () => setContributeOpen(true)
|
|
239
832
|
}
|
|
240
833
|
),
|
|
241
|
-
selected !== null && /* @__PURE__ */
|
|
834
|
+
selected !== null && /* @__PURE__ */ jsx4(
|
|
242
835
|
NodeControls,
|
|
243
836
|
{
|
|
244
837
|
index: selected.index,
|
|
@@ -249,17 +842,17 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
249
842
|
] });
|
|
250
843
|
}
|
|
251
844
|
function EditableNodes({ nodes, registry, onSelect }) {
|
|
252
|
-
return /* @__PURE__ */
|
|
845
|
+
return /* @__PURE__ */ jsx4(Fragment4, { children: nodes.map((node, index) => {
|
|
253
846
|
if (SKIPPED.has(node.type)) return null;
|
|
254
847
|
const Component = registry[node.type];
|
|
255
848
|
if (!Component) return null;
|
|
256
849
|
if (NON_SELECTABLE.has(node.type)) {
|
|
257
|
-
return /* @__PURE__ */
|
|
850
|
+
return /* @__PURE__ */ jsx4(Component, { ...node.props }, node.id ?? index);
|
|
258
851
|
}
|
|
259
852
|
const { position, rotation, ...rest } = node.props ?? {};
|
|
260
853
|
return (
|
|
261
854
|
// biome-ignore lint/a11y/noStaticElementInteractions: <group> is a three.js object, not a DOM element
|
|
262
|
-
/* @__PURE__ */
|
|
855
|
+
/* @__PURE__ */ jsx4(
|
|
263
856
|
"group",
|
|
264
857
|
{
|
|
265
858
|
position: asVec3(position) ?? [0, 0, 0],
|
|
@@ -268,9 +861,9 @@ function EditableNodes({ nodes, registry, onSelect }) {
|
|
|
268
861
|
event.stopPropagation();
|
|
269
862
|
onSelect(index, event.eventObject);
|
|
270
863
|
},
|
|
271
|
-
children: /* @__PURE__ */
|
|
864
|
+
children: /* @__PURE__ */ jsx4(Component, { ...rest })
|
|
272
865
|
},
|
|
273
|
-
index
|
|
866
|
+
node.id ?? index
|
|
274
867
|
)
|
|
275
868
|
);
|
|
276
869
|
}) });
|
|
@@ -342,12 +935,12 @@ var SELECT = {
|
|
|
342
935
|
appearance: "none",
|
|
343
936
|
paddingRight: "0.9rem"
|
|
344
937
|
};
|
|
345
|
-
var
|
|
938
|
+
var DIVIDER2 = {
|
|
346
939
|
width: 1,
|
|
347
940
|
alignSelf: "stretch",
|
|
348
941
|
background: "#15202a"
|
|
349
942
|
};
|
|
350
|
-
var
|
|
943
|
+
var HINT2 = {
|
|
351
944
|
color: "#5f7d75",
|
|
352
945
|
fontSize: "0.72rem",
|
|
353
946
|
paddingLeft: "0.2rem"
|
|
@@ -359,10 +952,12 @@ function EditorToolbar({
|
|
|
359
952
|
registry,
|
|
360
953
|
selected,
|
|
361
954
|
canUndo,
|
|
955
|
+
canContribute,
|
|
362
956
|
onAdd,
|
|
363
957
|
onDuplicate,
|
|
364
958
|
onDelete,
|
|
365
|
-
onUndo
|
|
959
|
+
onUndo,
|
|
960
|
+
onContribute
|
|
366
961
|
}) {
|
|
367
962
|
const hasSelection = selected !== null;
|
|
368
963
|
const exportWorld = () => {
|
|
@@ -374,8 +969,8 @@ function EditorToolbar({
|
|
|
374
969
|
a.click();
|
|
375
970
|
URL.revokeObjectURL(url);
|
|
376
971
|
};
|
|
377
|
-
return /* @__PURE__ */
|
|
378
|
-
/* @__PURE__ */
|
|
972
|
+
return /* @__PURE__ */ jsxs4("div", { style: TOOLBAR, children: [
|
|
973
|
+
/* @__PURE__ */ jsx4(
|
|
379
974
|
"button",
|
|
380
975
|
{
|
|
381
976
|
type: "button",
|
|
@@ -385,7 +980,7 @@ function EditorToolbar({
|
|
|
385
980
|
children: "Move"
|
|
386
981
|
}
|
|
387
982
|
),
|
|
388
|
-
/* @__PURE__ */
|
|
983
|
+
/* @__PURE__ */ jsx4(
|
|
389
984
|
"button",
|
|
390
985
|
{
|
|
391
986
|
type: "button",
|
|
@@ -395,8 +990,8 @@ function EditorToolbar({
|
|
|
395
990
|
children: "Rotate"
|
|
396
991
|
}
|
|
397
992
|
),
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
993
|
+
/* @__PURE__ */ jsx4("span", { style: DIVIDER2 }),
|
|
994
|
+
/* @__PURE__ */ jsxs4(
|
|
400
995
|
"select",
|
|
401
996
|
{
|
|
402
997
|
style: SELECT,
|
|
@@ -407,12 +1002,12 @@ function EditorToolbar({
|
|
|
407
1002
|
},
|
|
408
1003
|
title: "Insert a component at the origin",
|
|
409
1004
|
children: [
|
|
410
|
-
/* @__PURE__ */
|
|
411
|
-
Object.keys(registry).sort().map((name) => /* @__PURE__ */
|
|
1005
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: "+ Add\u2026" }),
|
|
1006
|
+
Object.keys(registry).sort().map((name) => /* @__PURE__ */ jsx4("option", { value: name, children: name }, name))
|
|
412
1007
|
]
|
|
413
1008
|
}
|
|
414
1009
|
),
|
|
415
|
-
/* @__PURE__ */
|
|
1010
|
+
/* @__PURE__ */ jsx4(
|
|
416
1011
|
"button",
|
|
417
1012
|
{
|
|
418
1013
|
type: "button",
|
|
@@ -423,7 +1018,7 @@ function EditorToolbar({
|
|
|
423
1018
|
children: "Duplicate"
|
|
424
1019
|
}
|
|
425
1020
|
),
|
|
426
|
-
/* @__PURE__ */
|
|
1021
|
+
/* @__PURE__ */ jsx4(
|
|
427
1022
|
"button",
|
|
428
1023
|
{
|
|
429
1024
|
type: "button",
|
|
@@ -434,7 +1029,7 @@ function EditorToolbar({
|
|
|
434
1029
|
children: "Delete"
|
|
435
1030
|
}
|
|
436
1031
|
),
|
|
437
|
-
/* @__PURE__ */
|
|
1032
|
+
/* @__PURE__ */ jsx4(
|
|
438
1033
|
"button",
|
|
439
1034
|
{
|
|
440
1035
|
type: "button",
|
|
@@ -445,55 +1040,87 @@ function EditorToolbar({
|
|
|
445
1040
|
children: "Undo"
|
|
446
1041
|
}
|
|
447
1042
|
),
|
|
448
|
-
/* @__PURE__ */
|
|
449
|
-
/* @__PURE__ */
|
|
450
|
-
/* @__PURE__ */
|
|
1043
|
+
/* @__PURE__ */ jsx4("span", { style: DIVIDER2 }),
|
|
1044
|
+
/* @__PURE__ */ jsx4("button", { type: "button", style: button(false), onClick: exportWorld, children: "Download JSON" }),
|
|
1045
|
+
canContribute && /* @__PURE__ */ jsx4(
|
|
1046
|
+
"button",
|
|
1047
|
+
{
|
|
1048
|
+
type: "button",
|
|
1049
|
+
style: button(false),
|
|
1050
|
+
onClick: onContribute,
|
|
1051
|
+
title: "Fork or suggest changes upstream",
|
|
1052
|
+
children: "Contribute \u2197"
|
|
1053
|
+
}
|
|
1054
|
+
),
|
|
1055
|
+
/* @__PURE__ */ jsx4("span", { style: HINT2, children: selected === null ? "click a component \xB7 Esc deselects" : `selected #${selected}` })
|
|
451
1056
|
] });
|
|
452
1057
|
}
|
|
453
1058
|
|
|
454
1059
|
// src/WorldNodes.tsx
|
|
455
|
-
import { Fragment as
|
|
1060
|
+
import { Fragment as Fragment5, jsx as jsx5 } from "react/jsx-runtime";
|
|
456
1061
|
function WorldNodes({ nodes, registry }) {
|
|
457
|
-
return /* @__PURE__ */
|
|
1062
|
+
return /* @__PURE__ */ jsx5(Fragment5, { children: nodes.map((node, index) => {
|
|
458
1063
|
const Component = registry[node.type];
|
|
459
1064
|
if (!Component) {
|
|
460
1065
|
console.warn(`[runek] Unknown component "${node.type}" \u2014 skipped.`);
|
|
461
1066
|
return null;
|
|
462
1067
|
}
|
|
463
|
-
const children = node.children?.length ? /* @__PURE__ */
|
|
464
|
-
return /* @__PURE__ */
|
|
1068
|
+
const children = node.children?.length ? /* @__PURE__ */ jsx5(WorldNodes, { nodes: node.children, registry }) : null;
|
|
1069
|
+
return /* @__PURE__ */ jsx5(Component, { ...node.props, children }, node.id ?? index);
|
|
465
1070
|
}) });
|
|
466
1071
|
}
|
|
467
1072
|
|
|
468
1073
|
// src/WorldRenderer.tsx
|
|
469
|
-
import { jsx as
|
|
1074
|
+
import { Fragment as Fragment6, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
470
1075
|
function WorldRenderer({ data, registry, ...worldProps }) {
|
|
471
|
-
return /* @__PURE__ */
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
1076
|
+
return /* @__PURE__ */ jsxs5(Fragment6, { children: [
|
|
1077
|
+
/* @__PURE__ */ jsx6(
|
|
1078
|
+
World,
|
|
1079
|
+
{
|
|
1080
|
+
unit: data.unit,
|
|
1081
|
+
gravity: data.gravity,
|
|
1082
|
+
ground: data.ground,
|
|
1083
|
+
palette: data.palette,
|
|
1084
|
+
fonts: data.fonts,
|
|
1085
|
+
fog: data.fog,
|
|
1086
|
+
time: data.time,
|
|
1087
|
+
timezone: data.timezone,
|
|
1088
|
+
avatar: data.avatar,
|
|
1089
|
+
...worldProps,
|
|
1090
|
+
children: /* @__PURE__ */ jsx6(WorldNodes, { nodes: data.nodes, registry })
|
|
1091
|
+
}
|
|
1092
|
+
),
|
|
1093
|
+
/* @__PURE__ */ jsx6(WorldAbout, { meta: data.meta })
|
|
1094
|
+
] });
|
|
482
1095
|
}
|
|
483
1096
|
export {
|
|
1097
|
+
DEFAULT_FONT,
|
|
1098
|
+
DEFAULT_FONTS,
|
|
484
1099
|
DEFAULT_PALETTE,
|
|
1100
|
+
DEFAULT_WORLD_TIME,
|
|
485
1101
|
World,
|
|
1102
|
+
WorldAbout,
|
|
486
1103
|
WorldContext,
|
|
1104
|
+
WorldContribute,
|
|
487
1105
|
WorldEditor,
|
|
488
1106
|
WorldNodes,
|
|
489
1107
|
WorldRenderer,
|
|
1108
|
+
assignNodeIds,
|
|
1109
|
+
clockHours,
|
|
1110
|
+
currentHours,
|
|
1111
|
+
editFileUrl,
|
|
1112
|
+
forkUrl,
|
|
490
1113
|
int,
|
|
491
1114
|
keyboardMap,
|
|
1115
|
+
parseClockTime,
|
|
1116
|
+
parseGitHubSource,
|
|
492
1117
|
parseWorld,
|
|
493
1118
|
pick,
|
|
494
1119
|
range,
|
|
1120
|
+
resolveWorldTime,
|
|
495
1121
|
rng,
|
|
496
1122
|
serializeWorld,
|
|
497
1123
|
sub,
|
|
1124
|
+
sunState,
|
|
498
1125
|
useWorld
|
|
499
1126
|
};
|