@runek/core 0.5.0 → 0.10.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.d.ts +245 -28
- package/dist/index.js +706 -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,8 +781,8 @@ 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,
|
|
@@ -207,10 +790,14 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
207
790
|
gravity: data.gravity,
|
|
208
791
|
palette: data.palette,
|
|
209
792
|
fog: data.fog,
|
|
793
|
+
time: data.time,
|
|
794
|
+
timezone: data.timezone,
|
|
795
|
+
avatar: data.avatar,
|
|
796
|
+
preserveDrawingBuffer: canContribute,
|
|
210
797
|
onPointerMissed: () => setSelected(null),
|
|
211
798
|
children: [
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
/* @__PURE__ */
|
|
799
|
+
/* @__PURE__ */ jsx4(OrbitControls, { makeDefault: true }),
|
|
800
|
+
/* @__PURE__ */ jsx4(
|
|
214
801
|
EditableNodes,
|
|
215
802
|
{
|
|
216
803
|
nodes: data.nodes,
|
|
@@ -218,12 +805,14 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
218
805
|
onSelect: (index, object) => setSelected({ index, object })
|
|
219
806
|
}
|
|
220
807
|
),
|
|
221
|
-
selected && /* @__PURE__ */
|
|
808
|
+
selected && /* @__PURE__ */ jsx4(TransformControls, { object: selected.object, mode, onMouseUp: commitTransform })
|
|
222
809
|
]
|
|
223
810
|
}
|
|
224
811
|
),
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
-
/* @__PURE__ */
|
|
812
|
+
/* @__PURE__ */ jsx4(Leva, { hidden: selected === null }),
|
|
813
|
+
/* @__PURE__ */ jsx4(WorldAbout, { meta: data.meta }),
|
|
814
|
+
contributeOpen && /* @__PURE__ */ jsx4(WorldContribute, { data, onClose: () => setContributeOpen(false) }),
|
|
815
|
+
/* @__PURE__ */ jsx4(
|
|
227
816
|
EditorToolbar,
|
|
228
817
|
{
|
|
229
818
|
mode,
|
|
@@ -232,13 +821,15 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
232
821
|
registry,
|
|
233
822
|
selected: selected?.index ?? null,
|
|
234
823
|
canUndo: history.current.length > 0,
|
|
824
|
+
canContribute,
|
|
235
825
|
onAdd: addNode,
|
|
236
826
|
onDuplicate: duplicateSelected,
|
|
237
827
|
onDelete: deleteSelected,
|
|
238
|
-
onUndo: undo
|
|
828
|
+
onUndo: undo,
|
|
829
|
+
onContribute: () => setContributeOpen(true)
|
|
239
830
|
}
|
|
240
831
|
),
|
|
241
|
-
selected !== null && /* @__PURE__ */
|
|
832
|
+
selected !== null && /* @__PURE__ */ jsx4(
|
|
242
833
|
NodeControls,
|
|
243
834
|
{
|
|
244
835
|
index: selected.index,
|
|
@@ -249,17 +840,17 @@ function WorldEditor({ data, registry, onChange, ...worldProps }) {
|
|
|
249
840
|
] });
|
|
250
841
|
}
|
|
251
842
|
function EditableNodes({ nodes, registry, onSelect }) {
|
|
252
|
-
return /* @__PURE__ */
|
|
843
|
+
return /* @__PURE__ */ jsx4(Fragment4, { children: nodes.map((node, index) => {
|
|
253
844
|
if (SKIPPED.has(node.type)) return null;
|
|
254
845
|
const Component = registry[node.type];
|
|
255
846
|
if (!Component) return null;
|
|
256
847
|
if (NON_SELECTABLE.has(node.type)) {
|
|
257
|
-
return /* @__PURE__ */
|
|
848
|
+
return /* @__PURE__ */ jsx4(Component, { ...node.props }, node.id ?? index);
|
|
258
849
|
}
|
|
259
850
|
const { position, rotation, ...rest } = node.props ?? {};
|
|
260
851
|
return (
|
|
261
852
|
// biome-ignore lint/a11y/noStaticElementInteractions: <group> is a three.js object, not a DOM element
|
|
262
|
-
/* @__PURE__ */
|
|
853
|
+
/* @__PURE__ */ jsx4(
|
|
263
854
|
"group",
|
|
264
855
|
{
|
|
265
856
|
position: asVec3(position) ?? [0, 0, 0],
|
|
@@ -268,9 +859,9 @@ function EditableNodes({ nodes, registry, onSelect }) {
|
|
|
268
859
|
event.stopPropagation();
|
|
269
860
|
onSelect(index, event.eventObject);
|
|
270
861
|
},
|
|
271
|
-
children: /* @__PURE__ */
|
|
862
|
+
children: /* @__PURE__ */ jsx4(Component, { ...rest })
|
|
272
863
|
},
|
|
273
|
-
index
|
|
864
|
+
node.id ?? index
|
|
274
865
|
)
|
|
275
866
|
);
|
|
276
867
|
}) });
|
|
@@ -342,12 +933,12 @@ var SELECT = {
|
|
|
342
933
|
appearance: "none",
|
|
343
934
|
paddingRight: "0.9rem"
|
|
344
935
|
};
|
|
345
|
-
var
|
|
936
|
+
var DIVIDER2 = {
|
|
346
937
|
width: 1,
|
|
347
938
|
alignSelf: "stretch",
|
|
348
939
|
background: "#15202a"
|
|
349
940
|
};
|
|
350
|
-
var
|
|
941
|
+
var HINT2 = {
|
|
351
942
|
color: "#5f7d75",
|
|
352
943
|
fontSize: "0.72rem",
|
|
353
944
|
paddingLeft: "0.2rem"
|
|
@@ -359,10 +950,12 @@ function EditorToolbar({
|
|
|
359
950
|
registry,
|
|
360
951
|
selected,
|
|
361
952
|
canUndo,
|
|
953
|
+
canContribute,
|
|
362
954
|
onAdd,
|
|
363
955
|
onDuplicate,
|
|
364
956
|
onDelete,
|
|
365
|
-
onUndo
|
|
957
|
+
onUndo,
|
|
958
|
+
onContribute
|
|
366
959
|
}) {
|
|
367
960
|
const hasSelection = selected !== null;
|
|
368
961
|
const exportWorld = () => {
|
|
@@ -374,8 +967,8 @@ function EditorToolbar({
|
|
|
374
967
|
a.click();
|
|
375
968
|
URL.revokeObjectURL(url);
|
|
376
969
|
};
|
|
377
|
-
return /* @__PURE__ */
|
|
378
|
-
/* @__PURE__ */
|
|
970
|
+
return /* @__PURE__ */ jsxs4("div", { style: TOOLBAR, children: [
|
|
971
|
+
/* @__PURE__ */ jsx4(
|
|
379
972
|
"button",
|
|
380
973
|
{
|
|
381
974
|
type: "button",
|
|
@@ -385,7 +978,7 @@ function EditorToolbar({
|
|
|
385
978
|
children: "Move"
|
|
386
979
|
}
|
|
387
980
|
),
|
|
388
|
-
/* @__PURE__ */
|
|
981
|
+
/* @__PURE__ */ jsx4(
|
|
389
982
|
"button",
|
|
390
983
|
{
|
|
391
984
|
type: "button",
|
|
@@ -395,8 +988,8 @@ function EditorToolbar({
|
|
|
395
988
|
children: "Rotate"
|
|
396
989
|
}
|
|
397
990
|
),
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
991
|
+
/* @__PURE__ */ jsx4("span", { style: DIVIDER2 }),
|
|
992
|
+
/* @__PURE__ */ jsxs4(
|
|
400
993
|
"select",
|
|
401
994
|
{
|
|
402
995
|
style: SELECT,
|
|
@@ -407,12 +1000,12 @@ function EditorToolbar({
|
|
|
407
1000
|
},
|
|
408
1001
|
title: "Insert a component at the origin",
|
|
409
1002
|
children: [
|
|
410
|
-
/* @__PURE__ */
|
|
411
|
-
Object.keys(registry).sort().map((name) => /* @__PURE__ */
|
|
1003
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: "+ Add\u2026" }),
|
|
1004
|
+
Object.keys(registry).sort().map((name) => /* @__PURE__ */ jsx4("option", { value: name, children: name }, name))
|
|
412
1005
|
]
|
|
413
1006
|
}
|
|
414
1007
|
),
|
|
415
|
-
/* @__PURE__ */
|
|
1008
|
+
/* @__PURE__ */ jsx4(
|
|
416
1009
|
"button",
|
|
417
1010
|
{
|
|
418
1011
|
type: "button",
|
|
@@ -423,7 +1016,7 @@ function EditorToolbar({
|
|
|
423
1016
|
children: "Duplicate"
|
|
424
1017
|
}
|
|
425
1018
|
),
|
|
426
|
-
/* @__PURE__ */
|
|
1019
|
+
/* @__PURE__ */ jsx4(
|
|
427
1020
|
"button",
|
|
428
1021
|
{
|
|
429
1022
|
type: "button",
|
|
@@ -434,7 +1027,7 @@ function EditorToolbar({
|
|
|
434
1027
|
children: "Delete"
|
|
435
1028
|
}
|
|
436
1029
|
),
|
|
437
|
-
/* @__PURE__ */
|
|
1030
|
+
/* @__PURE__ */ jsx4(
|
|
438
1031
|
"button",
|
|
439
1032
|
{
|
|
440
1033
|
type: "button",
|
|
@@ -445,55 +1038,85 @@ function EditorToolbar({
|
|
|
445
1038
|
children: "Undo"
|
|
446
1039
|
}
|
|
447
1040
|
),
|
|
448
|
-
/* @__PURE__ */
|
|
449
|
-
/* @__PURE__ */
|
|
450
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsx4("span", { style: DIVIDER2 }),
|
|
1042
|
+
/* @__PURE__ */ jsx4("button", { type: "button", style: button(false), onClick: exportWorld, children: "Download JSON" }),
|
|
1043
|
+
canContribute && /* @__PURE__ */ jsx4(
|
|
1044
|
+
"button",
|
|
1045
|
+
{
|
|
1046
|
+
type: "button",
|
|
1047
|
+
style: button(false),
|
|
1048
|
+
onClick: onContribute,
|
|
1049
|
+
title: "Fork or suggest changes upstream",
|
|
1050
|
+
children: "Contribute \u2197"
|
|
1051
|
+
}
|
|
1052
|
+
),
|
|
1053
|
+
/* @__PURE__ */ jsx4("span", { style: HINT2, children: selected === null ? "click a component \xB7 Esc deselects" : `selected #${selected}` })
|
|
451
1054
|
] });
|
|
452
1055
|
}
|
|
453
1056
|
|
|
454
1057
|
// src/WorldNodes.tsx
|
|
455
|
-
import { Fragment as
|
|
1058
|
+
import { Fragment as Fragment5, jsx as jsx5 } from "react/jsx-runtime";
|
|
456
1059
|
function WorldNodes({ nodes, registry }) {
|
|
457
|
-
return /* @__PURE__ */
|
|
1060
|
+
return /* @__PURE__ */ jsx5(Fragment5, { children: nodes.map((node, index) => {
|
|
458
1061
|
const Component = registry[node.type];
|
|
459
1062
|
if (!Component) {
|
|
460
1063
|
console.warn(`[runek] Unknown component "${node.type}" \u2014 skipped.`);
|
|
461
1064
|
return null;
|
|
462
1065
|
}
|
|
463
|
-
const children = node.children?.length ? /* @__PURE__ */
|
|
464
|
-
return /* @__PURE__ */
|
|
1066
|
+
const children = node.children?.length ? /* @__PURE__ */ jsx5(WorldNodes, { nodes: node.children, registry }) : null;
|
|
1067
|
+
return /* @__PURE__ */ jsx5(Component, { ...node.props, children }, node.id ?? index);
|
|
465
1068
|
}) });
|
|
466
1069
|
}
|
|
467
1070
|
|
|
468
1071
|
// src/WorldRenderer.tsx
|
|
469
|
-
import { jsx as
|
|
1072
|
+
import { Fragment as Fragment6, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
470
1073
|
function WorldRenderer({ data, registry, ...worldProps }) {
|
|
471
|
-
return /* @__PURE__ */
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
1074
|
+
return /* @__PURE__ */ jsxs5(Fragment6, { children: [
|
|
1075
|
+
/* @__PURE__ */ jsx6(
|
|
1076
|
+
World,
|
|
1077
|
+
{
|
|
1078
|
+
unit: data.unit,
|
|
1079
|
+
gravity: data.gravity,
|
|
1080
|
+
palette: data.palette,
|
|
1081
|
+
fog: data.fog,
|
|
1082
|
+
time: data.time,
|
|
1083
|
+
timezone: data.timezone,
|
|
1084
|
+
avatar: data.avatar,
|
|
1085
|
+
...worldProps,
|
|
1086
|
+
children: /* @__PURE__ */ jsx6(WorldNodes, { nodes: data.nodes, registry })
|
|
1087
|
+
}
|
|
1088
|
+
),
|
|
1089
|
+
/* @__PURE__ */ jsx6(WorldAbout, { meta: data.meta })
|
|
1090
|
+
] });
|
|
482
1091
|
}
|
|
483
1092
|
export {
|
|
1093
|
+
DEFAULT_FONT,
|
|
1094
|
+
DEFAULT_FONTS,
|
|
484
1095
|
DEFAULT_PALETTE,
|
|
1096
|
+
DEFAULT_WORLD_TIME,
|
|
485
1097
|
World,
|
|
1098
|
+
WorldAbout,
|
|
486
1099
|
WorldContext,
|
|
1100
|
+
WorldContribute,
|
|
487
1101
|
WorldEditor,
|
|
488
1102
|
WorldNodes,
|
|
489
1103
|
WorldRenderer,
|
|
1104
|
+
assignNodeIds,
|
|
1105
|
+
clockHours,
|
|
1106
|
+
currentHours,
|
|
1107
|
+
editFileUrl,
|
|
1108
|
+
forkUrl,
|
|
490
1109
|
int,
|
|
491
1110
|
keyboardMap,
|
|
1111
|
+
parseClockTime,
|
|
1112
|
+
parseGitHubSource,
|
|
492
1113
|
parseWorld,
|
|
493
1114
|
pick,
|
|
494
1115
|
range,
|
|
1116
|
+
resolveWorldTime,
|
|
495
1117
|
rng,
|
|
496
1118
|
serializeWorld,
|
|
497
1119
|
sub,
|
|
1120
|
+
sunState,
|
|
498
1121
|
useWorld
|
|
499
1122
|
};
|