@almadar/ui 6.58.0 → 6.61.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/BehaviorValueFrame-3ORZHPWQ.css +1252 -0
- package/dist/BehaviorValueFrame-QTD7BWOI.js +18 -0
- package/dist/{Canvas3DHost-BTvRPfsr.d.ts → Canvas3DHost-CNjANoaU.d.ts} +1 -1
- package/dist/{GameAudioProvider-kXbonWTv.d.ts → GameAudioProvider-Dv_L-PHt.d.ts} +1 -1
- package/dist/TraitValueFrame-2WMYJOA5.css +1252 -0
- package/dist/TraitValueFrame-WOXNLLMM.js +17 -0
- package/dist/Typography-BY4A2NBY.js +5 -0
- package/dist/{UISlotRenderer-N5DFYE6K.js → UISlotRenderer-EEOXSJCI.js} +10 -8
- package/dist/avl/index.cjs +31960 -27831
- package/dist/avl/index.d.ts +36 -13
- package/dist/avl/index.js +331 -6147
- package/dist/{avl-3d-context-BpBKZIbc.d.ts → avl-3d-context-Dxun3-ri.d.ts} +2 -0
- package/dist/{chunk-I2ZNE5TD.js → chunk-6PV4LASE.js} +9495 -639
- package/dist/{chunk-723MJUU3.js → chunk-6ZCU3BQN.js} +1 -1
- package/dist/{chunk-GZGDBS4S.js → chunk-7NIU5GDC.js} +2 -2
- package/dist/{chunk-YYYHH5BA.js → chunk-CB64IOYD.js} +0 -4
- package/dist/{chunk-MZIDOJMD.js → chunk-EIIQGOZW.js} +2 -1
- package/dist/chunk-F7NZ3KZQ.js +434 -0
- package/dist/{chunk-LZRW6GXG.js → chunk-FYX64CDL.js} +0 -2
- package/dist/{chunk-4ZGXIW3B.js → chunk-KZRAMPM6.js} +1 -1
- package/dist/{chunk-VHTJRS2N.js → chunk-L3MTZ3RH.js} +7 -0
- package/dist/chunk-LUREMDI3.js +49 -0
- package/dist/{chunk-HA7CJWQX.js → chunk-MGN6B6OQ.js} +1477 -1177
- package/dist/{chunk-Q47ZN2DW.js → chunk-N23SVGTH.js} +83 -1
- package/dist/{chunk-KJGVLXEN.js → chunk-N4IJS5DD.js} +50 -116
- package/dist/{chunk-I5QOYW2Z.js → chunk-UTSDPJUP.js} +205 -2
- package/dist/{chunk-V3FGTQUE.js → chunk-ZR45FGW3.js} +7 -1
- package/dist/{cn-DIql5232.d.ts → cn-CDIhHCUC.d.ts} +40 -16
- package/dist/components/index.cjs +14455 -4099
- package/dist/components/index.d.ts +165 -177
- package/dist/components/index.js +14 -15
- package/dist/context/index.cjs +16 -1
- package/dist/context/index.js +4 -2
- package/dist/hooks/index.cjs +79 -6
- package/dist/hooks/index.d.ts +43 -2
- package/dist/hooks/index.js +6 -7
- package/dist/lib/drawable/three/index.cjs +350 -26
- package/dist/lib/drawable/three/index.d.ts +5 -5
- package/dist/lib/drawable/three/index.js +28 -7
- package/dist/lib/index.cjs +90 -4
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +74 -6
- package/dist/locales/index.cjs +0 -6
- package/dist/locales/index.js +2 -2
- package/dist/{paintDispatch-BN8Vbzgs.d.ts → paintDispatch-Cp1_S93j.d.ts} +30 -2
- package/dist/providers/index.cjs +13853 -1075
- package/dist/providers/index.d.ts +38 -6
- package/dist/providers/index.js +40 -30
- package/dist/runtime/index.cjs +28369 -18287
- package/dist/runtime/index.d.ts +65 -3
- package/dist/runtime/index.js +13 -379
- package/dist/ssr/index.cjs +508 -133
- package/dist/ssr/index.d.cts +56 -4
- package/dist/ssr/index.d.ts +56 -4
- package/dist/ssr/index.js +446 -71
- package/dist/{verificationRegistry-CK76GUfH.d.ts → verificationRegistry-B5aGl7WF.d.ts} +3 -2
- package/locales/ar.json +0 -2
- package/locales/en.json +0 -2
- package/locales/sl.json +0 -2
- package/package.json +7 -7
- package/tailwind-preset.cjs +78 -8
- package/themes/_contract.md +6 -0
- package/themes/_fonts.css +4 -4
- package/themes/_foundation.css +11 -0
- package/themes/_presets.css +2 -0
- package/themes/almadar-website.css +10 -0
- package/themes/almadar.css +10 -0
- package/themes/atelier.css +10 -0
- package/themes/bauhaus.css +10 -0
- package/themes/bloomberg-dense.css +10 -0
- package/themes/clay.css +10 -0
- package/themes/corporate.css +10 -0
- package/themes/gazette.css +10 -0
- package/themes/glass.css +10 -0
- package/themes/kflow.css +5 -0
- package/themes/kiosk.css +10 -0
- package/themes/linear-clean.css +10 -0
- package/themes/material.css +10 -0
- package/themes/minimalist.css +10 -0
- package/themes/nata.css +304 -0
- package/themes/neon.css +10 -0
- package/themes/notion-editorial.css +10 -0
- package/themes/orb.css +10 -0
- package/themes/prism.css +10 -0
- package/themes/rawi.css +304 -0
- package/themes/retro.css +10 -0
- package/themes/risograph.css +10 -0
- package/themes/scandi.css +10 -0
- package/themes/solarpunk.css +10 -0
- package/themes/terminal.css +138 -128
- package/dist/Typography-LZTP72Y4.js +0 -5
- package/dist/chunk-D2E44ODB.js +0 -350
- package/dist/chunk-HB3D3HUJ.js +0 -2265
- package/dist/chunk-N7JIG57B.js +0 -138
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useTapReveal } from './chunk-XJTHMW4G.js';
|
|
2
2
|
import { useEventBus } from './chunk-CHNC4UYE.js';
|
|
3
|
-
import { updateAssetStatus } from './chunk-
|
|
3
|
+
import { isMotionEnabled, updateAssetStatus } from './chunk-N23SVGTH.js';
|
|
4
4
|
import { cn, domPassthrough } from './chunk-64WACXM7.js';
|
|
5
5
|
import { __publicField } from './chunk-QPXIAUJD.js';
|
|
6
6
|
import { createLogger } from '@almadar/logger';
|
|
7
|
-
import React, { useCallback, createContext, useState, useLayoutEffect
|
|
7
|
+
import React, { useRef, useCallback, createContext, useEffect, useContext, useState, useLayoutEffect } from 'react';
|
|
8
8
|
import { useThemeScope, useTheme } from '@almadar/ui/context';
|
|
9
|
-
import { jsx } from 'react/jsx-runtime';
|
|
9
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
10
10
|
import { renderUiEntriesOf } from '@almadar/core/patterns';
|
|
11
11
|
import { DIAGRAM_TONES, isInlineTrait, DIAGRAM_TONE_COLOR_KEYS, DIAGRAM_TEXT_SIZES } from '@almadar/core';
|
|
12
12
|
import { parseListenSource } from '@almadar/runtime';
|
|
@@ -223,17 +223,20 @@ var EFFECT_OPERATOR_CATEGORY = {
|
|
|
223
223
|
"workspace/write-schema": "system",
|
|
224
224
|
"workspace/write-plan": "system",
|
|
225
225
|
"workspace/archive-orbital": "system",
|
|
226
|
-
"
|
|
226
|
+
"behavior/catalog": "system",
|
|
227
|
+
"behavior/describe": "system",
|
|
228
|
+
"behavior/source": "system",
|
|
229
|
+
"program/read": "system",
|
|
230
|
+
"program/print": "system",
|
|
231
|
+
"program/eval": "system",
|
|
232
|
+
"program/compose": "system"
|
|
227
233
|
};
|
|
228
234
|
var EFFECT_FAMILY_CATEGORY = {
|
|
229
235
|
agent: "system",
|
|
230
236
|
os: "system",
|
|
231
237
|
browser: "system",
|
|
232
238
|
llm: "system",
|
|
233
|
-
behavior: "system",
|
|
234
|
-
validate: "control",
|
|
235
239
|
session: "system",
|
|
236
|
-
compose: "system",
|
|
237
240
|
trace: "control",
|
|
238
241
|
memory: "data",
|
|
239
242
|
application: "system"
|
|
@@ -295,17 +298,20 @@ var EFFECT_OPERATOR_ZONE = {
|
|
|
295
298
|
"workspace/write-schema": "system",
|
|
296
299
|
"workspace/write-plan": "system",
|
|
297
300
|
"workspace/archive-orbital": "system",
|
|
298
|
-
"
|
|
301
|
+
"behavior/catalog": "system",
|
|
302
|
+
"behavior/describe": "system",
|
|
303
|
+
"behavior/source": "system",
|
|
304
|
+
"program/read": "system",
|
|
305
|
+
"program/print": "system",
|
|
306
|
+
"program/eval": "system",
|
|
307
|
+
"program/compose": "system"
|
|
299
308
|
};
|
|
300
309
|
var EFFECT_FAMILY_ZONE = {
|
|
301
310
|
agent: "system",
|
|
302
311
|
os: "system",
|
|
303
312
|
browser: "system",
|
|
304
313
|
llm: "remote",
|
|
305
|
-
behavior: "system",
|
|
306
|
-
validate: "flow",
|
|
307
314
|
session: "system",
|
|
308
|
-
compose: "system",
|
|
309
315
|
trace: "flow",
|
|
310
316
|
memory: "store",
|
|
311
317
|
application: "system"
|
|
@@ -328,7 +334,12 @@ function effectCategoryOf(head) {
|
|
|
328
334
|
function asEffectOperator(head) {
|
|
329
335
|
return isEffectOperator(head) ? head : null;
|
|
330
336
|
}
|
|
337
|
+
var PENDING_STD_CATEGORY_COLORS = {
|
|
338
|
+
"std-behavior-value": avlBlend("var(--color-accent)", 40, "var(--color-success)"),
|
|
339
|
+
"std-program": avlBlend("var(--color-accent)", 60, "var(--color-info)")
|
|
340
|
+
};
|
|
331
341
|
var OPERATOR_CATEGORY_COLORS = {
|
|
342
|
+
...PENDING_STD_CATEGORY_COLORS,
|
|
332
343
|
"arithmetic": "var(--color-primary)",
|
|
333
344
|
"comparison": "var(--color-warning)",
|
|
334
345
|
"logic": "var(--color-accent)",
|
|
@@ -383,6 +394,44 @@ function effectZoneOf(head) {
|
|
|
383
394
|
return null;
|
|
384
395
|
}
|
|
385
396
|
|
|
397
|
+
// lib/bandShapes.ts
|
|
398
|
+
var shape = (d) => `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'><path d='${d}'/></svg>")`;
|
|
399
|
+
var BAND_SHAPES = {
|
|
400
|
+
wave: shape("M0 60 C240 100 480 100 720 60 C960 20 1200 20 1440 60 V100 H0 Z"),
|
|
401
|
+
arc: shape("M0 100 Q720 0 1440 100 Z"),
|
|
402
|
+
tilt: shape("M0 100 L1440 0 V100 Z"),
|
|
403
|
+
step: shape("M0 100 V75 H360 V50 H720 V25 H1080 V0 H1440 V100 Z"),
|
|
404
|
+
scallop: shape("M0 100 V40 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 a60 40 0 0 1 120 0 V100 Z")
|
|
405
|
+
};
|
|
406
|
+
var BAND_EDGE_HEIGHT = "clamp(24px, 6vw, 96px)";
|
|
407
|
+
var BAND_ACCENT_FALLBACK = "linear-gradient(180deg, color-mix(in oklab, var(--color-primary) 12%, var(--color-background)), var(--color-background))";
|
|
408
|
+
var BAND_EDGE_FILL = {
|
|
409
|
+
background: "var(--surface-page-image, none), var(--color-background)",
|
|
410
|
+
muted: "var(--color-muted)",
|
|
411
|
+
surface: "var(--color-card)",
|
|
412
|
+
gradient: `var(--surface-accent-image, ${BAND_ACCENT_FALLBACK}), var(--color-muted)`,
|
|
413
|
+
inverse: "var(--color-foreground)"
|
|
414
|
+
};
|
|
415
|
+
function bandEdgeMask(edge) {
|
|
416
|
+
return edge === "curve" ? "var(--surface-edge-mask, none)" : BAND_SHAPES[edge];
|
|
417
|
+
}
|
|
418
|
+
function bandEdgeHeight(edge) {
|
|
419
|
+
return edge === "curve" ? `var(--surface-edge-height, ${BAND_EDGE_HEIGHT})` : BAND_EDGE_HEIGHT;
|
|
420
|
+
}
|
|
421
|
+
function bandEdgeLayerStyle(edge, side, flip, color) {
|
|
422
|
+
const mask = bandEdgeMask(edge);
|
|
423
|
+
const turn = side === "top" ? flip ? "scaleY(-1)" : "rotate(180deg)" : flip ? "scaleX(-1)" : void 0;
|
|
424
|
+
const style = { height: bandEdgeHeight(edge), background: BAND_EDGE_FILL[color], maskImage: mask, WebkitMaskImage: mask, transform: turn };
|
|
425
|
+
if (color === "background") {
|
|
426
|
+
style.backgroundSize = "var(--surface-page-image-size, auto), auto";
|
|
427
|
+
style.backgroundAttachment = "fixed";
|
|
428
|
+
}
|
|
429
|
+
return style;
|
|
430
|
+
}
|
|
431
|
+
function bandPadding(basePad, edge) {
|
|
432
|
+
return edge === "none" ? void 0 : `calc(${basePad} + ${bandEdgeHeight(edge)})`;
|
|
433
|
+
}
|
|
434
|
+
|
|
386
435
|
// lib/pressable.ts
|
|
387
436
|
function clickElement(el) {
|
|
388
437
|
el.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
|
|
@@ -421,7 +470,215 @@ function rowOpenControlProps(enabled) {
|
|
|
421
470
|
var STRETCHED_CONTROL = "after:absolute after:inset-0 after:content-[''] after:rounded-[inherit] focus-visible:outline-none focus-visible:after:ring-2 focus-visible:after:ring-primary focus-visible:after:ring-inset";
|
|
422
471
|
var STRETCHED_ABOVE = "relative z-[1]";
|
|
423
472
|
|
|
424
|
-
//
|
|
473
|
+
// lib/motion-tokens.ts
|
|
474
|
+
function cssTimeMs(value) {
|
|
475
|
+
const v = value.trim();
|
|
476
|
+
if (v.endsWith("ms")) return Number.parseFloat(v);
|
|
477
|
+
if (v.endsWith("s")) return Number.parseFloat(v) * 1e3;
|
|
478
|
+
return Number.NaN;
|
|
479
|
+
}
|
|
480
|
+
function tokenAt(el, name) {
|
|
481
|
+
for (let at = el; at !== null; at = at.parentElement) {
|
|
482
|
+
const value = getComputedStyle(at).getPropertyValue(name).trim();
|
|
483
|
+
if (value !== "") return value;
|
|
484
|
+
}
|
|
485
|
+
return "";
|
|
486
|
+
}
|
|
487
|
+
function motionTiming(durationToken, easingToken, scope) {
|
|
488
|
+
const duration = cssTimeMs(tokenAt(scope ?? document.documentElement, durationToken));
|
|
489
|
+
const easing = tokenAt(scope ?? document.documentElement, easingToken);
|
|
490
|
+
const reduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
491
|
+
if (!isMotionEnabled() || reduced || !Number.isFinite(duration) || easing === "") return void 0;
|
|
492
|
+
return { duration, easing };
|
|
493
|
+
}
|
|
494
|
+
function layoutScale(el) {
|
|
495
|
+
const layoutWidth = el instanceof HTMLElement ? el.offsetWidth : 0;
|
|
496
|
+
const screenWidth = el.getBoundingClientRect().width;
|
|
497
|
+
return layoutWidth > 0 && screenWidth > 0 ? screenWidth / layoutWidth : 1;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// lib/animate-changes.ts
|
|
501
|
+
function ownMotion(el) {
|
|
502
|
+
return el.closest("[data-item-move]") !== null || /\balmadar-enter-/.test(el.getAttribute("class") ?? "");
|
|
503
|
+
}
|
|
504
|
+
function boxesOf(el) {
|
|
505
|
+
return getComputedStyle(el).display === "contents" ? boxedChildren(el) : [el];
|
|
506
|
+
}
|
|
507
|
+
function boxedChildren(el) {
|
|
508
|
+
return [...el.children].flatMap(boxesOf);
|
|
509
|
+
}
|
|
510
|
+
var GHOST_STYLE = ["font", "letterSpacing", "lineHeight", "color", "textAlign", "whiteSpace", "textTransform"];
|
|
511
|
+
function roll(el, oldText, timing) {
|
|
512
|
+
const box = el.getBoundingClientRect();
|
|
513
|
+
const scale = layoutScale(el);
|
|
514
|
+
const computed = getComputedStyle(el);
|
|
515
|
+
const holder = document.createElement("span");
|
|
516
|
+
holder.setAttribute("aria-hidden", "true");
|
|
517
|
+
Object.assign(holder.style, {
|
|
518
|
+
position: "fixed",
|
|
519
|
+
left: `${box.left}px`,
|
|
520
|
+
top: `${box.top}px`,
|
|
521
|
+
width: `${box.width / scale}px`,
|
|
522
|
+
height: `${box.height / scale}px`,
|
|
523
|
+
transform: scale === 1 ? "" : `scale(${scale})`,
|
|
524
|
+
transformOrigin: "top left",
|
|
525
|
+
pointerEvents: "none",
|
|
526
|
+
zIndex: "2147483647"
|
|
527
|
+
});
|
|
528
|
+
const ghost = document.createElement("span");
|
|
529
|
+
ghost.setAttribute("data-change-ghost", "");
|
|
530
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
531
|
+
ghost.textContent = oldText;
|
|
532
|
+
for (const prop of GHOST_STYLE) ghost.style[prop] = computed[prop];
|
|
533
|
+
Object.assign(ghost.style, { display: "block", width: "100%", height: "100%", overflow: "visible" });
|
|
534
|
+
holder.appendChild(ghost);
|
|
535
|
+
document.body.appendChild(holder);
|
|
536
|
+
const out = ghost.animate(
|
|
537
|
+
[{ opacity: 1, transform: "none" }, { opacity: 0, transform: "translateY(-40%)", offset: 0.5 }, { opacity: 0, transform: "translateY(-40%)" }],
|
|
538
|
+
timing
|
|
539
|
+
);
|
|
540
|
+
if (out === void 0) holder.remove();
|
|
541
|
+
else {
|
|
542
|
+
out.addEventListener("finish", () => holder.remove());
|
|
543
|
+
out.addEventListener("cancel", () => holder.remove());
|
|
544
|
+
}
|
|
545
|
+
el.animate(
|
|
546
|
+
[{ opacity: 0, transform: "translateY(40%)" }, { opacity: 0, transform: "translateY(40%)", offset: 0.4 }, { opacity: 1, transform: "none" }],
|
|
547
|
+
timing
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
var textOnly = (el) => el instanceof HTMLElement && el.childNodes.length > 0 && [...el.childNodes].every((n) => n.nodeType === Node.TEXT_NODE);
|
|
551
|
+
function settle(root, added, changed) {
|
|
552
|
+
const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
|
|
553
|
+
if (timing === void 0) return;
|
|
554
|
+
for (const el of added) {
|
|
555
|
+
if (ownMotion(el) || el.parentElement !== null && [...added].some((other) => other !== el && other.contains(el))) continue;
|
|
556
|
+
for (const box of boxesOf(el)) {
|
|
557
|
+
if (ownMotion(box)) continue;
|
|
558
|
+
box.animate([{ opacity: 0, transform: "translateY(6px)" }, { opacity: 1, transform: "none" }], timing);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
for (const [el, oldText] of changed) {
|
|
562
|
+
if (ownMotion(el) || [...added].some((a) => a.contains(el))) continue;
|
|
563
|
+
if (oldText !== void 0 && oldText !== el.textContent && textOnly(el)) roll(el, oldText, timing);
|
|
564
|
+
else el.animate([{ opacity: 0.25 }, { opacity: 1 }], timing);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function blocks(root) {
|
|
568
|
+
const seen = /* @__PURE__ */ new Set();
|
|
569
|
+
for (const wrapper of root.querySelectorAll("[data-pattern]")) {
|
|
570
|
+
for (const box of boxesOf(wrapper)) if (!ownMotion(box)) seen.add(box);
|
|
571
|
+
}
|
|
572
|
+
return [...seen];
|
|
573
|
+
}
|
|
574
|
+
function glidePushed(root, places) {
|
|
575
|
+
const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
|
|
576
|
+
const origin = root.getBoundingClientRect();
|
|
577
|
+
const applied = /* @__PURE__ */ new Map();
|
|
578
|
+
const present = /* @__PURE__ */ new Set();
|
|
579
|
+
for (const el of blocks(root)) {
|
|
580
|
+
present.add(el);
|
|
581
|
+
if ((el.getAnimations?.() ?? []).length > 0) {
|
|
582
|
+
places.delete(el);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
const box = el.getBoundingClientRect();
|
|
586
|
+
const now = { x: box.left - origin.left, y: box.top - origin.top };
|
|
587
|
+
const was = places.get(el);
|
|
588
|
+
places.set(el, now);
|
|
589
|
+
if (was === void 0 || timing === void 0) continue;
|
|
590
|
+
let inherited = { x: 0, y: 0 };
|
|
591
|
+
for (let up = el.parentElement; up !== null && up !== root; up = up.parentElement) {
|
|
592
|
+
const a = applied.get(up);
|
|
593
|
+
if (a !== void 0) {
|
|
594
|
+
inherited = a;
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const dx = was.x - now.x - inherited.x;
|
|
599
|
+
const dy = was.y - now.y - inherited.y;
|
|
600
|
+
if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) {
|
|
601
|
+
if (inherited.x !== 0 || inherited.y !== 0) applied.set(el, inherited);
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
const scale = layoutScale(el);
|
|
605
|
+
el.animate([{ transform: `translate(${dx / scale}px, ${dy / scale}px)` }, { transform: "none" }], { ...timing, composite: "add" });
|
|
606
|
+
applied.set(el, { x: inherited.x + dx, y: inherited.y + dy });
|
|
607
|
+
}
|
|
608
|
+
for (const el of places.keys()) if (!present.has(el)) places.delete(el);
|
|
609
|
+
}
|
|
610
|
+
function easeHeight(root) {
|
|
611
|
+
if (typeof ResizeObserver === "undefined") return () => void 0;
|
|
612
|
+
let rest;
|
|
613
|
+
let glide;
|
|
614
|
+
const sizes = new ResizeObserver(() => {
|
|
615
|
+
const scale = layoutScale(root);
|
|
616
|
+
const shown = root.getBoundingClientRect().height / scale;
|
|
617
|
+
const gliding = glide !== void 0 && glide.playState === "running";
|
|
618
|
+
glide?.cancel();
|
|
619
|
+
glide = void 0;
|
|
620
|
+
const height = root.getBoundingClientRect().height / scale;
|
|
621
|
+
const from = gliding ? shown : rest;
|
|
622
|
+
rest = height;
|
|
623
|
+
const timing = motionTiming("--duration-normal", "--easing-emphasized", root);
|
|
624
|
+
if (from === void 0 || timing === void 0 || Math.abs(from - height) < 1) return;
|
|
625
|
+
glide = root.animate(
|
|
626
|
+
[{ height: `${from}px`, overflow: "clip" }, { height: `${height}px`, overflow: "clip" }],
|
|
627
|
+
timing
|
|
628
|
+
) ?? void 0;
|
|
629
|
+
});
|
|
630
|
+
let watched = [];
|
|
631
|
+
const watch = () => {
|
|
632
|
+
const next = boxedChildren(root);
|
|
633
|
+
if (next.length === watched.length && next.every((el, i) => el === watched[i])) return;
|
|
634
|
+
watched = next;
|
|
635
|
+
sizes.disconnect();
|
|
636
|
+
for (const el of next) sizes.observe(el);
|
|
637
|
+
};
|
|
638
|
+
watch();
|
|
639
|
+
const children = new MutationObserver(watch);
|
|
640
|
+
children.observe(root, { childList: true, subtree: true });
|
|
641
|
+
return () => {
|
|
642
|
+
children.disconnect();
|
|
643
|
+
sizes.disconnect();
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function useAnimateChanges(ref, enabled) {
|
|
647
|
+
useEffect(() => {
|
|
648
|
+
const root = ref.current;
|
|
649
|
+
if (!enabled || root === null) return;
|
|
650
|
+
return easeHeight(root);
|
|
651
|
+
}, [ref, enabled]);
|
|
652
|
+
useEffect(() => {
|
|
653
|
+
const root = ref.current;
|
|
654
|
+
if (!enabled || root === null || typeof MutationObserver === "undefined") return;
|
|
655
|
+
const observer = new MutationObserver((records) => {
|
|
656
|
+
const added = /* @__PURE__ */ new Set();
|
|
657
|
+
const changed = /* @__PURE__ */ new Map();
|
|
658
|
+
for (const record of records) {
|
|
659
|
+
if (record.type === "childList") {
|
|
660
|
+
for (const node of record.addedNodes) {
|
|
661
|
+
if (node instanceof Element) added.add(node);
|
|
662
|
+
else if (node.nodeType === Node.TEXT_NODE && node.parentElement !== null && changed.get(node.parentElement) === void 0) {
|
|
663
|
+
const removed = [...record.removedNodes].filter((n) => n.nodeType === Node.TEXT_NODE);
|
|
664
|
+
changed.set(node.parentElement, removed.length > 0 ? removed.map((n) => n.textContent ?? "").join("") : void 0);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} else if (record.type === "characterData") {
|
|
668
|
+
const el = record.target.parentElement;
|
|
669
|
+
if (el === null || changed.get(el) !== void 0) continue;
|
|
670
|
+
changed.set(el, [...el.childNodes].map((n) => n === record.target ? record.oldValue ?? "" : n.textContent ?? "").join(""));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
glidePushed(root, places);
|
|
674
|
+
settle(root, added, changed);
|
|
675
|
+
});
|
|
676
|
+
const places = /* @__PURE__ */ new Map();
|
|
677
|
+
glidePushed(root, places);
|
|
678
|
+
observer.observe(root, { subtree: true, childList: true, characterData: true, characterDataOldValue: true });
|
|
679
|
+
return () => observer.disconnect();
|
|
680
|
+
}, [ref, enabled]);
|
|
681
|
+
}
|
|
425
682
|
var paddingStyles = {
|
|
426
683
|
none: "p-0",
|
|
427
684
|
xs: "p-1",
|
|
@@ -486,7 +743,18 @@ var bgStyles = {
|
|
|
486
743
|
muted: "bg-muted text-foreground",
|
|
487
744
|
accent: "bg-accent text-accent-foreground",
|
|
488
745
|
surface: "bg-card",
|
|
489
|
-
overlay: "bg-card/80 backdrop-blur-sm"
|
|
746
|
+
overlay: "bg-card/80 backdrop-blur-sm",
|
|
747
|
+
gradient: "band-gradient",
|
|
748
|
+
inverse: "band-inverse"
|
|
749
|
+
};
|
|
750
|
+
var paddingRem = {
|
|
751
|
+
none: "0rem",
|
|
752
|
+
xs: "0.25rem",
|
|
753
|
+
sm: "0.5rem",
|
|
754
|
+
md: "1rem",
|
|
755
|
+
lg: "1.5rem",
|
|
756
|
+
xl: "2rem",
|
|
757
|
+
"2xl": "3rem"
|
|
490
758
|
};
|
|
491
759
|
var roundedStyles = {
|
|
492
760
|
none: "rounded-none",
|
|
@@ -549,6 +817,13 @@ var Box = React.forwardRef(
|
|
|
549
817
|
hoverEvent,
|
|
550
818
|
tapReveal = true,
|
|
551
819
|
maxWidth,
|
|
820
|
+
edgeTop = "none",
|
|
821
|
+
edgeBottom = "none",
|
|
822
|
+
edgeColor = "background",
|
|
823
|
+
edgeFlip = false,
|
|
824
|
+
texture = false,
|
|
825
|
+
animateChanges = false,
|
|
826
|
+
backdrop,
|
|
552
827
|
onClick,
|
|
553
828
|
onMouseEnter,
|
|
554
829
|
onMouseLeave,
|
|
@@ -556,6 +831,13 @@ var Box = React.forwardRef(
|
|
|
556
831
|
...rest
|
|
557
832
|
}, ref) => {
|
|
558
833
|
const eventBus = useEventBus();
|
|
834
|
+
const ownRef = useRef(null);
|
|
835
|
+
useAnimateChanges(ownRef, animateChanges);
|
|
836
|
+
const setRef = useCallback((node) => {
|
|
837
|
+
ownRef.current = node;
|
|
838
|
+
if (typeof ref === "function") ref(node);
|
|
839
|
+
else if (ref !== null) ref.current = node;
|
|
840
|
+
}, [ref]);
|
|
559
841
|
const { override: themeOverride } = useThemeScope();
|
|
560
842
|
const declaredTheme = rest["data-theme"];
|
|
561
843
|
const dataTheme = declaredTheme !== void 0 && themeOverride ? themeOverride : declaredTheme;
|
|
@@ -591,12 +873,25 @@ var Box = React.forwardRef(
|
|
|
591
873
|
if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
|
|
592
874
|
onPointerDown?.(e);
|
|
593
875
|
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
876
|
+
const isBand = backdrop != null || texture || edgeTop !== "none" || edgeBottom !== "none";
|
|
877
|
+
const padKey = paddingY ?? padding;
|
|
878
|
+
const bandStyle = {};
|
|
879
|
+
const padTop = padKey ? bandPadding(paddingRem[padKey], edgeTop) : void 0;
|
|
880
|
+
const padBottom = padKey ? bandPadding(paddingRem[padKey], edgeBottom) : void 0;
|
|
881
|
+
if (padTop) bandStyle.paddingTop = padTop;
|
|
882
|
+
if (padBottom) bandStyle.paddingBottom = padBottom;
|
|
883
|
+
const bandLayers = isBand ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
884
|
+
backdrop != null && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-backdrop", children: backdrop }),
|
|
885
|
+
texture && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-texture" }),
|
|
886
|
+
edgeTop !== "none" && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-edge band-edge-top", style: bandEdgeLayerStyle(edgeTop, "top", edgeFlip, edgeColor) }),
|
|
887
|
+
edgeBottom !== "none" && /* @__PURE__ */ jsx(Box, { as: "span", "data-band-layer": "", "aria-hidden": true, className: "band-edge band-edge-bottom", style: bandEdgeLayerStyle(edgeBottom, "bottom", edgeFlip, edgeColor) })
|
|
888
|
+
] }) : null;
|
|
594
889
|
const isClickable = action || onClick;
|
|
595
890
|
const actionControl = action && rest.role === void 0 && rest.tabIndex === void 0 ? pressableProps(handleClick) : {};
|
|
596
891
|
return React.createElement(
|
|
597
892
|
Component,
|
|
598
893
|
{
|
|
599
|
-
ref,
|
|
894
|
+
ref: setRef,
|
|
600
895
|
className: cn(
|
|
601
896
|
padding && paddingStyles[padding],
|
|
602
897
|
paddingX && paddingXStyles[paddingX],
|
|
@@ -614,6 +909,7 @@ var Box = React.forwardRef(
|
|
|
614
909
|
overflow && overflowStyles[overflow],
|
|
615
910
|
position && positionStyles[position],
|
|
616
911
|
isClickable && "cursor-pointer",
|
|
912
|
+
isBand && "band",
|
|
617
913
|
className
|
|
618
914
|
),
|
|
619
915
|
onClick: isClickable ? handleClick : void 0,
|
|
@@ -621,11 +917,15 @@ var Box = React.forwardRef(
|
|
|
621
917
|
onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
|
|
622
918
|
onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
|
|
623
919
|
onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
|
|
624
|
-
style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
|
|
920
|
+
style: maxWidth || isBand ? { ...maxWidth ? { maxWidth } : {}, ...bandStyle, ...rest.style } : rest.style,
|
|
625
921
|
...rest,
|
|
626
|
-
"data-theme": dataTheme
|
|
922
|
+
"data-theme": dataTheme,
|
|
923
|
+
"data-motion": animateChanges ? "presentation" : void 0
|
|
627
924
|
},
|
|
628
|
-
children
|
|
925
|
+
isBand ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
926
|
+
bandLayers,
|
|
927
|
+
children
|
|
928
|
+
] }) : children
|
|
629
929
|
);
|
|
630
930
|
}
|
|
631
931
|
);
|
|
@@ -1000,560 +1300,592 @@ function parseTransitionLevel(schema, orbitalName, traitName, transitionIndex) {
|
|
|
1000
1300
|
};
|
|
1001
1301
|
}
|
|
1002
1302
|
|
|
1003
|
-
// lib/
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
return typeof a.tileWidth === "number";
|
|
1303
|
+
// lib/drawable/contract.ts
|
|
1304
|
+
function isValidScenePos(pos) {
|
|
1305
|
+
return Number.isFinite(pos?.x) && Number.isFinite(pos?.y);
|
|
1007
1306
|
}
|
|
1008
|
-
function
|
|
1009
|
-
|
|
1010
|
-
return typeof
|
|
1307
|
+
function spriteAssetState(node) {
|
|
1308
|
+
if (!isValidScenePos(node.position)) return "invalid-position";
|
|
1309
|
+
return typeof node.asset?.url === "string" && node.asset.url.length > 0 ? "ready" : "asset-unset";
|
|
1011
1310
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
return
|
|
1311
|
+
var SPRITE_FALLBACK_FILL = "#9b8f7f";
|
|
1312
|
+
var SPRITE_FALLBACK_STROKE = "#5e564b";
|
|
1313
|
+
function spriteRect(projector, node, natural) {
|
|
1314
|
+
const tw = projector.tileWidth;
|
|
1315
|
+
const fallbackW = projector.worldPixelDirect ? natural?.w ?? 0 : tw;
|
|
1316
|
+
const fallbackH = projector.worldPixelDirect ? natural?.h ?? 0 : tw;
|
|
1317
|
+
const w = node.width !== void 0 ? node.width * tw : fallbackW;
|
|
1318
|
+
const h = node.height !== void 0 ? node.height * tw : fallbackH;
|
|
1319
|
+
const anchor = node.anchor ?? "top-left";
|
|
1320
|
+
const p = projector.anchorPoint(node.position, anchor);
|
|
1321
|
+
return {
|
|
1322
|
+
x: anchor === "top-left" ? p.x : p.x - w / 2,
|
|
1323
|
+
y: anchor === "ground" ? p.y - h : anchor === "center" ? p.y - h / 2 : p.y,
|
|
1324
|
+
w,
|
|
1325
|
+
h
|
|
1326
|
+
};
|
|
1023
1327
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1328
|
+
var DrawableRegistryContext = createContext(null);
|
|
1329
|
+
var NUMERIC_TRACKS = [
|
|
1330
|
+
"offsetX",
|
|
1331
|
+
"offsetY",
|
|
1332
|
+
"rotate",
|
|
1333
|
+
"opacity",
|
|
1334
|
+
"radiusX",
|
|
1335
|
+
"radiusY",
|
|
1336
|
+
"width",
|
|
1337
|
+
"height",
|
|
1338
|
+
"strokeWidth",
|
|
1339
|
+
"strokeDashOffset",
|
|
1340
|
+
"blur"
|
|
1341
|
+
];
|
|
1342
|
+
function isAnimatedShape(node) {
|
|
1343
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1026
1344
|
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1345
|
+
var lerp = (a, b, k) => a + (b - a) * k;
|
|
1346
|
+
function applyShapeAnimation(node, timeMs) {
|
|
1347
|
+
const anim = node.animation;
|
|
1348
|
+
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
|
|
1349
|
+
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
1350
|
+
const cycle = timeMs / anim.durationMs;
|
|
1351
|
+
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
1352
|
+
const out = { ...node };
|
|
1353
|
+
const trackValue = (key) => {
|
|
1354
|
+
const defined = frames.filter((f) => f[key] !== void 0);
|
|
1355
|
+
if (defined.length === 0) return void 0;
|
|
1356
|
+
let prev;
|
|
1357
|
+
let next;
|
|
1358
|
+
for (const f of defined) {
|
|
1359
|
+
if (f.at <= t) prev = f;
|
|
1360
|
+
else if (!next) next = f;
|
|
1040
1361
|
}
|
|
1041
|
-
if (!
|
|
1042
|
-
|
|
1043
|
-
const
|
|
1362
|
+
if (!prev) return defined[0][key];
|
|
1363
|
+
if (!next) return prev[key];
|
|
1364
|
+
const span = next.at - prev.at;
|
|
1365
|
+
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
1366
|
+
const a = prev[key];
|
|
1367
|
+
const b = next[key];
|
|
1368
|
+
if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
|
|
1369
|
+
return a;
|
|
1370
|
+
};
|
|
1371
|
+
for (const key of NUMERIC_TRACKS) {
|
|
1372
|
+
const v = trackValue(key);
|
|
1373
|
+
if (v !== void 0) out[key] = v;
|
|
1374
|
+
}
|
|
1375
|
+
const fill = trackValue("fill");
|
|
1376
|
+
if (fill !== void 0) out.fill = fill;
|
|
1377
|
+
const stroke = trackValue("stroke");
|
|
1378
|
+
if (stroke !== void 0) out.stroke = stroke;
|
|
1379
|
+
const shadowFrames = frames.filter((f) => f.shadow !== void 0);
|
|
1380
|
+
if (shadowFrames.length > 0) {
|
|
1381
|
+
const sh = trackValue("shadow");
|
|
1382
|
+
if (sh !== void 0) {
|
|
1383
|
+
let prevSh;
|
|
1384
|
+
let nextSh;
|
|
1385
|
+
for (const f of shadowFrames) {
|
|
1386
|
+
if (f.at <= t) prevSh = f;
|
|
1387
|
+
else if (!nextSh) nextSh = f;
|
|
1388
|
+
}
|
|
1389
|
+
if (prevSh?.shadow && nextSh?.shadow) {
|
|
1390
|
+
const span = nextSh.at - prevSh.at;
|
|
1391
|
+
const k = span > 0 ? (t - prevSh.at) / span : 1;
|
|
1392
|
+
out.shadow = { color: prevSh.shadow.color, blur: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
|
|
1393
|
+
} else {
|
|
1394
|
+
out.shadow = sh;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
return out;
|
|
1399
|
+
}
|
|
1400
|
+
function gradientStyle(g, ox, oy, scale) {
|
|
1401
|
+
if (g.kind === "linear") {
|
|
1402
|
+
if (!g.from || !g.to) return void 0;
|
|
1044
1403
|
return {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1404
|
+
kind: "linear",
|
|
1405
|
+
x1: ox + g.from.x * scale,
|
|
1406
|
+
y1: oy + g.from.y * scale,
|
|
1407
|
+
x2: ox + g.to.x * scale,
|
|
1408
|
+
y2: oy + g.to.y * scale,
|
|
1409
|
+
stops: g.stops
|
|
1049
1410
|
};
|
|
1050
1411
|
}
|
|
1051
|
-
if (
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
return atlasUrlOf(asset) !== void 0 && typeof asset?.sprite === "string" && asset.sprite.length > 0;
|
|
1061
|
-
}
|
|
1062
|
-
function resolveAssetSource(img, asset, onReady) {
|
|
1063
|
-
if (isAtlasAsset(asset)) {
|
|
1064
|
-
const atlas = getAtlas(asset.atlas, onReady);
|
|
1065
|
-
if (!atlas) return null;
|
|
1066
|
-
const rect = subRectFor(atlas, asset.sprite);
|
|
1067
|
-
if (!rect) return null;
|
|
1068
|
-
return { img, rect, aspect: rect.sw / rect.sh };
|
|
1412
|
+
if (g.kind === "conic") {
|
|
1413
|
+
if (!g.center) return void 0;
|
|
1414
|
+
return {
|
|
1415
|
+
kind: "conic",
|
|
1416
|
+
cx: ox + g.center.x * scale,
|
|
1417
|
+
cy: oy + g.center.y * scale,
|
|
1418
|
+
angle: g.angle ?? 0,
|
|
1419
|
+
stops: g.stops
|
|
1420
|
+
};
|
|
1069
1421
|
}
|
|
1070
|
-
|
|
1071
|
-
const natH = img.naturalHeight || 1;
|
|
1072
|
-
return { img, rect: null, aspect: natW / natH };
|
|
1073
|
-
}
|
|
1074
|
-
function blit(ctx, src, dx, dy, dw, dh) {
|
|
1075
|
-
if (src.rect) ctx.drawImage(src.img, src.rect.sx, src.rect.sy, src.rect.sw, src.rect.sh, dx, dy, dw, dh);
|
|
1076
|
-
else ctx.drawImage(src.img, dx, dy, dw, dh);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
// components/core/atoms/fx.ts
|
|
1080
|
-
function resolveFxView(item, presets) {
|
|
1081
|
-
const row = presets?.find((p) => p.type === item.type);
|
|
1082
|
-
if (!row) return item;
|
|
1422
|
+
if (!g.center || g.radius === void 0) return void 0;
|
|
1083
1423
|
return {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
vx: item.vx ?? row.vx,
|
|
1090
|
-
vy: item.vy ?? row.vy,
|
|
1091
|
-
vz: item.vz ?? row.vz,
|
|
1092
|
-
particleCount: item.particleCount ?? row.particleCount,
|
|
1093
|
-
animation: item.animation ?? row.animation,
|
|
1094
|
-
shape: row.shape,
|
|
1095
|
-
count: row.count,
|
|
1096
|
-
glow: row.glow,
|
|
1097
|
-
gravity: row.gravity,
|
|
1098
|
-
color2: row.color2
|
|
1424
|
+
kind: "radial",
|
|
1425
|
+
cx: ox + g.center.x * scale,
|
|
1426
|
+
cy: oy + g.center.y * scale,
|
|
1427
|
+
r: g.radius * scale,
|
|
1428
|
+
stops: g.stops
|
|
1099
1429
|
};
|
|
1100
1430
|
}
|
|
1101
|
-
function
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
|
|
1106
|
-
return { lifeMs, ageMs, progress, fade: 1 - progress };
|
|
1107
|
-
}
|
|
1108
|
-
function fxHash01(seed, salt) {
|
|
1109
|
-
let h = 2166136261 ^ salt;
|
|
1110
|
-
for (let i = 0; i < seed.length; i++) {
|
|
1111
|
-
h ^= seed.charCodeAt(i);
|
|
1112
|
-
h = Math.imul(h, 16777619);
|
|
1431
|
+
function patternStyle(p, unit) {
|
|
1432
|
+
if (p.kind === "image") {
|
|
1433
|
+
if (!p.url) return void 0;
|
|
1434
|
+
return { kind: "image", url: p.url, scale: (p.scale ?? 1) / unit };
|
|
1113
1435
|
}
|
|
1114
|
-
|
|
1115
|
-
h = Math.imul(h, 1274126177);
|
|
1116
|
-
h ^= h >>> 16;
|
|
1117
|
-
return (h >>> 0) / 4294967296;
|
|
1118
|
-
}
|
|
1119
|
-
var CONFETTI_COLORS = [
|
|
1120
|
-
"var(--color-primary)",
|
|
1121
|
-
"var(--color-success)",
|
|
1122
|
-
"var(--color-warning)",
|
|
1123
|
-
"var(--color-error)",
|
|
1124
|
-
"gold",
|
|
1125
|
-
"dodgerblue"
|
|
1126
|
-
];
|
|
1127
|
-
function createConfettiParticles(count, seed) {
|
|
1128
|
-
return Array.from({ length: count }, (_, i) => ({
|
|
1129
|
-
id: i,
|
|
1130
|
-
color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
|
|
1131
|
-
left: 30 + fxHash01(seed, i * 7 + 2) * 40,
|
|
1132
|
-
delay: fxHash01(seed, i * 7 + 3) * 300,
|
|
1133
|
-
angle: fxHash01(seed, i * 7 + 4) * 360,
|
|
1134
|
-
distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
|
|
1135
|
-
rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
|
|
1136
|
-
size: 4 + fxHash01(seed, i * 7 + 7) * 6
|
|
1137
|
-
}));
|
|
1436
|
+
return { kind: "noise", scale: (p.scale ?? 2) / unit, alpha: p.alpha, color: p.color };
|
|
1138
1437
|
}
|
|
1139
|
-
var
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1438
|
+
var paintShape = (painter, rawNode, dctx) => {
|
|
1439
|
+
const node = dctx.time > 0 ? applyShapeAnimation(rawNode, dctx.time) : rawNode;
|
|
1440
|
+
if (!isValidScenePos(node.position)) return;
|
|
1441
|
+
painter.save();
|
|
1442
|
+
if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
|
|
1443
|
+
const origin = dctx.projector.project(node.position);
|
|
1444
|
+
const tileWidth = dctx.projector.tileWidth;
|
|
1445
|
+
const groupScale = dctx.groupScale ?? 1;
|
|
1446
|
+
const strokePx = (node.strokeWidth ?? 1) / groupScale;
|
|
1447
|
+
if (node.blendMode) painter.setBlend(node.blendMode);
|
|
1448
|
+
if (node.strokeDash && node.strokeDash.length > 0) {
|
|
1449
|
+
painter.setLineDash(
|
|
1450
|
+
node.strokeDash.map((v) => v / groupScale),
|
|
1451
|
+
(node.strokeDashOffset ?? 0) / groupScale
|
|
1452
|
+
);
|
|
1144
1453
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1454
|
+
if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / groupScale);
|
|
1455
|
+
if (node.rotate) {
|
|
1456
|
+
const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
|
|
1457
|
+
const px = origin.x + pivot.x * tileWidth;
|
|
1458
|
+
const py = origin.y + pivot.y * tileWidth;
|
|
1459
|
+
painter.translate(px, py);
|
|
1460
|
+
painter.rotate(node.rotate);
|
|
1461
|
+
painter.translate(-px, -py);
|
|
1147
1462
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1463
|
+
if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth * groupScale });
|
|
1464
|
+
const fill = node.fill === "none" ? void 0 : node.fill;
|
|
1465
|
+
const stroke = node.stroke === "none" ? void 0 : node.stroke;
|
|
1466
|
+
const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
|
|
1467
|
+
const pxStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, origin.x, origin.y, tileWidth) ?? stroke : stroke;
|
|
1468
|
+
const patFill = node.fillPattern ? patternStyle(node.fillPattern, groupScale) : void 0;
|
|
1469
|
+
switch (node.shape) {
|
|
1470
|
+
case "cell": {
|
|
1471
|
+
const pts = dctx.projector.cellPath(node.position);
|
|
1472
|
+
if (pxFill) painter.fillPoly(pts, pxFill);
|
|
1473
|
+
if (patFill) painter.fillPoly(pts, patFill);
|
|
1474
|
+
if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
case "rect": {
|
|
1478
|
+
const p = dctx.projector.anchorPoint(node.position, node.anchor ?? "top-left");
|
|
1479
|
+
const tw = dctx.projector.tileWidth;
|
|
1480
|
+
const x = p.x + (node.offsetX ?? 0) * tw;
|
|
1481
|
+
const y = p.y + (node.offsetY ?? 0) * tw;
|
|
1482
|
+
const w = (node.width ?? 0) * tw;
|
|
1483
|
+
const h = (node.height ?? 0) * tw;
|
|
1484
|
+
if (pxFill) painter.fillRect(x, y, w, h, pxFill);
|
|
1485
|
+
if (patFill) painter.fillRect(x, y, w, h, patFill);
|
|
1486
|
+
if (pxStroke) painter.strokeRect(x, y, w, h, pxStroke, strokePx);
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
case "ellipse": {
|
|
1490
|
+
const p = dctx.projector.anchorPoint(node.position, node.anchor ?? "ground");
|
|
1491
|
+
const tw = dctx.projector.tileWidth;
|
|
1492
|
+
const cx = p.x + (node.offsetX ?? 0) * tw;
|
|
1493
|
+
const cy = p.y + (node.offsetY ?? 0) * tw;
|
|
1494
|
+
const rx = (node.radiusX ?? 0) * tw;
|
|
1495
|
+
const ry = (node.radiusY ?? node.radiusX ?? 0) * tw;
|
|
1496
|
+
if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
|
|
1497
|
+
if (patFill) painter.fillEllipse(cx, cy, rx, ry, patFill);
|
|
1498
|
+
if (pxStroke) painter.strokeEllipse(cx, cy, rx, ry, pxStroke, strokePx);
|
|
1499
|
+
break;
|
|
1500
|
+
}
|
|
1501
|
+
case "poly": {
|
|
1502
|
+
const pts = (node.points ?? []).map((pt) => ({
|
|
1503
|
+
x: origin.x + ((node.offsetX ?? 0) + pt.x) * tileWidth,
|
|
1504
|
+
y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
|
|
1505
|
+
}));
|
|
1506
|
+
if (pxFill) painter.fillPoly(pts, pxFill);
|
|
1507
|
+
if (patFill) painter.fillPoly(pts, patFill);
|
|
1508
|
+
if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
|
|
1509
|
+
break;
|
|
1510
|
+
}
|
|
1511
|
+
case "path": {
|
|
1512
|
+
if (!node.d) break;
|
|
1513
|
+
painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
|
|
1514
|
+
painter.scale(tileWidth, tileWidth);
|
|
1515
|
+
if (node.strokeDash && node.strokeDash.length > 0) {
|
|
1516
|
+
painter.setLineDash(
|
|
1517
|
+
node.strokeDash.map((v) => v / (groupScale * tileWidth)),
|
|
1518
|
+
(node.strokeDashOffset ?? 0) / (groupScale * tileWidth)
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / (groupScale * tileWidth));
|
|
1522
|
+
const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
|
|
1523
|
+
const localStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, 0, 0, 1) ?? stroke : stroke;
|
|
1524
|
+
const localPat = node.fillPattern ? patternStyle(node.fillPattern, groupScale * tileWidth) : void 0;
|
|
1525
|
+
if (localFill) painter.fillPath(node.d, localFill);
|
|
1526
|
+
if (localPat) painter.fillPath(node.d, localPat);
|
|
1527
|
+
if (localStroke) painter.strokePath(node.d, localStroke, strokePx / tileWidth);
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
painter.restore();
|
|
1532
|
+
};
|
|
1533
|
+
function DrawShape(props) {
|
|
1534
|
+
const register = useContext(DrawableRegistryContext);
|
|
1535
|
+
if (register) {
|
|
1536
|
+
const { opacity, ...rest } = props;
|
|
1537
|
+
const node = {
|
|
1538
|
+
...rest,
|
|
1539
|
+
type: "draw-shape",
|
|
1540
|
+
...opacity !== void 0 && opacity > 0 ? { opacity } : {}
|
|
1541
|
+
};
|
|
1542
|
+
register(node);
|
|
1151
1543
|
}
|
|
1544
|
+
return null;
|
|
1152
1545
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
var toneVar = (tone) => `--color-${DIAGRAM_TONE_COLOR_KEYS[tone]}`;
|
|
1156
|
-
var SERIES_TONES = ["series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8"];
|
|
1157
|
-
var TONE_SET = new Set(DIAGRAM_TONES);
|
|
1158
|
-
function isDiagramTone(value) {
|
|
1159
|
-
return TONE_SET.has(value);
|
|
1546
|
+
function isAnimatedGroup(node) {
|
|
1547
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1160
1548
|
}
|
|
1161
|
-
function
|
|
1162
|
-
|
|
1549
|
+
function DrawGroup(props) {
|
|
1550
|
+
const register = useContext(DrawableRegistryContext);
|
|
1551
|
+
if (register) register({ ...props, type: "draw-group" });
|
|
1552
|
+
return null;
|
|
1163
1553
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1554
|
+
|
|
1555
|
+
// lib/imageCache.ts
|
|
1556
|
+
var RETRY_AFTER_MS = 5e3;
|
|
1557
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1558
|
+
function startLoad(url, onReady, existing) {
|
|
1559
|
+
const img = new Image();
|
|
1560
|
+
img.crossOrigin = "anonymous";
|
|
1561
|
+
const entry = existing ?? { img, status: "pending" };
|
|
1562
|
+
entry.img = img;
|
|
1563
|
+
if (onReady) entry.onReady = onReady;
|
|
1564
|
+
img.onload = () => {
|
|
1565
|
+
entry.status = "loaded";
|
|
1566
|
+
updateAssetStatus(url, "loaded");
|
|
1567
|
+
entry.onReady?.();
|
|
1568
|
+
};
|
|
1569
|
+
img.onerror = () => {
|
|
1570
|
+
entry.status = "failed";
|
|
1571
|
+
entry.failedAt = Date.now();
|
|
1572
|
+
updateAssetStatus(url, "failed");
|
|
1573
|
+
entry.onReady?.();
|
|
1574
|
+
};
|
|
1575
|
+
img.src = url;
|
|
1576
|
+
return entry;
|
|
1169
1577
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
this.probe.style.pointerEvents = "none";
|
|
1182
|
-
scope.appendChild(this.probe);
|
|
1183
|
-
}
|
|
1184
|
-
dispose() {
|
|
1185
|
-
this.probe.remove();
|
|
1186
|
-
}
|
|
1187
|
-
raw(name) {
|
|
1188
|
-
return this.style.getPropertyValue(name).trim();
|
|
1189
|
-
}
|
|
1190
|
-
/** A length var in px: a plain value parses directly, a derived one (`calc`, `var`) through the probe. */
|
|
1191
|
-
length(name, fallback) {
|
|
1192
|
-
const raw = this.raw(name);
|
|
1193
|
-
const plain = PLAIN_LENGTH.exec(raw);
|
|
1194
|
-
if (plain) return Number(plain[1]);
|
|
1195
|
-
if (raw === "") return fallback;
|
|
1196
|
-
this.probe.style.width = `var(${name})`;
|
|
1197
|
-
const computed = PLAIN_LENGTH.exec(getComputedStyle(this.probe).width.trim());
|
|
1198
|
-
this.probe.style.width = "";
|
|
1199
|
-
if (computed) return Number(computed[1]);
|
|
1200
|
-
log2.warn("unresolvable-theme-length", { var: name, raw });
|
|
1201
|
-
return fallback;
|
|
1202
|
-
}
|
|
1203
|
-
number(name, fallback) {
|
|
1204
|
-
const raw = this.raw(name);
|
|
1205
|
-
if (raw === "") return fallback;
|
|
1206
|
-
const n = Number(raw);
|
|
1207
|
-
if (Number.isFinite(n)) return n;
|
|
1208
|
-
log2.warn("unresolvable-theme-number", { var: name, raw });
|
|
1209
|
-
return fallback;
|
|
1210
|
-
}
|
|
1211
|
-
/** A duration var in ms (`250ms`, `0.25s`, or a `var()` alias resolved through the probe). */
|
|
1212
|
-
durationMs(name, fallback) {
|
|
1213
|
-
const parse = (v) => {
|
|
1214
|
-
const m = /^([\d.]+)(ms|s)$/.exec(v.trim());
|
|
1215
|
-
return m ? Number(m[1]) * (m[2] === "s" ? 1e3 : 1) : null;
|
|
1216
|
-
};
|
|
1217
|
-
const raw = this.raw(name);
|
|
1218
|
-
const direct = parse(raw);
|
|
1219
|
-
if (direct !== null) return direct;
|
|
1220
|
-
if (raw === "") return fallback;
|
|
1221
|
-
this.probe.style.transitionDuration = `var(${name})`;
|
|
1222
|
-
const computed = parse(getComputedStyle(this.probe).transitionDuration.split(",")[0] ?? "");
|
|
1223
|
-
this.probe.style.transitionDuration = "";
|
|
1224
|
-
return computed ?? fallback;
|
|
1578
|
+
function getOrLoadImage(url, onReady) {
|
|
1579
|
+
if (!url || typeof window === "undefined") return null;
|
|
1580
|
+
const existing = cache.get(url);
|
|
1581
|
+
if (existing) {
|
|
1582
|
+
if (existing.status === "loaded") return existing.img;
|
|
1583
|
+
if (existing.status === "pending" && onReady) existing.onReady = onReady;
|
|
1584
|
+
if (existing.status === "failed" && Date.now() - (existing.failedAt ?? 0) >= RETRY_AFTER_MS) {
|
|
1585
|
+
existing.failedAt = Date.now();
|
|
1586
|
+
startLoad(url, onReady, existing);
|
|
1587
|
+
}
|
|
1588
|
+
return null;
|
|
1225
1589
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
shadow(name) {
|
|
1231
|
-
const raw = this.raw(name);
|
|
1232
|
-
if (raw === "" || raw === "none") return null;
|
|
1233
|
-
this.probe.style.boxShadow = `var(${name})`;
|
|
1234
|
-
const computed = getComputedStyle(this.probe).boxShadow.trim() || raw;
|
|
1235
|
-
this.probe.style.boxShadow = "";
|
|
1236
|
-
const colorMatch = /(rgba?\([^)]*\)|#[0-9a-f]{3,8}|oklch\([^)]*\))/i.exec(computed);
|
|
1237
|
-
const lengths = computed.replace(colorMatch?.[0] ?? "", "").trim().split(/\s+/).map((t) => PLAIN_LENGTH.exec(t)).filter((m) => m !== null).map((m) => Number(m[1]));
|
|
1238
|
-
if (lengths.length < 2) return null;
|
|
1239
|
-
const color = colorMatch ? parseComputedColor(colorMatch[0]) : null;
|
|
1240
|
-
return { offsetX: lengths[0], offsetY: lengths[1], blur: lengths[2] ?? 0, color: color ? colorCss(color) : "rgba(0, 0, 0, 0.25)" };
|
|
1241
|
-
}
|
|
1242
|
-
family(name, fallback) {
|
|
1243
|
-
return this.raw(name) || fallback;
|
|
1244
|
-
}
|
|
1245
|
-
};
|
|
1246
|
-
function dashList(raw, fallback) {
|
|
1247
|
-
if (raw === "") return fallback;
|
|
1248
|
-
const parts = raw.split(/[\s,]+/).map(Number);
|
|
1249
|
-
return parts.every((n) => Number.isFinite(n) && n >= 0) ? parts : fallback;
|
|
1590
|
+
const entry = startLoad(url, onReady);
|
|
1591
|
+
cache.set(url, entry);
|
|
1592
|
+
updateAssetStatus(url, "pending");
|
|
1593
|
+
return null;
|
|
1250
1594
|
}
|
|
1251
|
-
function
|
|
1252
|
-
|
|
1253
|
-
return typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1595
|
+
function getImageStatus(url) {
|
|
1596
|
+
return cache.get(url)?.status;
|
|
1254
1597
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
const body = r.family("--font-family-body", r.family("--font-family", "system-ui, sans-serif"));
|
|
1268
|
-
return {
|
|
1269
|
-
ground: ground ? colorCss(ground) : "transparent",
|
|
1270
|
-
tones,
|
|
1271
|
-
series: SERIES_TONES.map((t) => tones[t]),
|
|
1272
|
-
strokes: {
|
|
1273
|
-
thin: r.length("--diagram-stroke-thin", 1),
|
|
1274
|
-
normal: r.length("--diagram-stroke-normal", 1.5),
|
|
1275
|
-
bold: r.length("--diagram-stroke-bold", 3)
|
|
1276
|
-
},
|
|
1277
|
-
lineCap: oneOf(r.raw("--diagram-line-cap"), ["butt", "round", "square"], "round", "--diagram-line-cap"),
|
|
1278
|
-
lineJoin: oneOf(r.raw("--diagram-line-join"), ["miter", "round", "bevel"], "round", "--diagram-line-join"),
|
|
1279
|
-
dash: dashList(r.raw("--diagram-dash"), [6, 4]),
|
|
1280
|
-
dot: dashList(r.raw("--diagram-dot"), [1, 4]),
|
|
1281
|
-
fillStyle: oneOf(r.raw("--diagram-fill-style"), ["solid", "tint", "outline", "hatch"], "tint", "--diagram-fill-style"),
|
|
1282
|
-
fillOpacity: r.number("--diagram-fill-opacity", 0.18),
|
|
1283
|
-
roughness: r.number("--diagram-roughness", 0),
|
|
1284
|
-
glow: r.number("--diagram-glow", 0),
|
|
1285
|
-
shadow: r.shadow("--diagram-shadow"),
|
|
1286
|
-
marker: oneOf(r.raw("--diagram-marker"), ["triangle", "open", "line", "dot"], "triangle", "--diagram-marker"),
|
|
1287
|
-
faces: {
|
|
1288
|
-
body,
|
|
1289
|
-
display: r.family("--font-family-display", body),
|
|
1290
|
-
mono: r.family("--font-family-mono", "ui-monospace, monospace")
|
|
1291
|
-
},
|
|
1292
|
-
label: {
|
|
1293
|
-
font: oneOf(r.raw("--diagram-label-font"), ["body", "display", "mono"], "body", "--diagram-label-font"),
|
|
1294
|
-
size: oneOf(r.raw("--diagram-label-size"), DIAGRAM_TEXT_SIZES, "sm", "--diagram-label-size"),
|
|
1295
|
-
weight: r.raw("--diagram-label-weight") || "500",
|
|
1296
|
-
uppercase: oneOf(r.raw("--diagram-label-case"), ["none", "uppercase"], "none", "--diagram-label-case") === "uppercase"
|
|
1297
|
-
},
|
|
1298
|
-
text,
|
|
1299
|
-
corner: r.length("--diagram-corner", 4),
|
|
1300
|
-
motion: {
|
|
1301
|
-
enabled: r.raw("--motion-enable") !== "off" && !prefersReducedMotion(scope),
|
|
1302
|
-
fastMs: r.durationMs("--duration-fast", 150),
|
|
1303
|
-
normalMs: r.durationMs("--duration-normal", 250),
|
|
1304
|
-
slowMs: r.durationMs("--duration-slow", 400),
|
|
1305
|
-
easing: r.raw("--easing-standard") || "ease"
|
|
1306
|
-
},
|
|
1307
|
-
scene: {
|
|
1308
|
-
roughness: r.number("--material-roughness", 0.6),
|
|
1309
|
-
metalness: r.number("--material-metalness", 0),
|
|
1310
|
-
flat: r.number("--material-flat", 0) >= 1,
|
|
1311
|
-
outline: r.number("--material-outline", 0),
|
|
1312
|
-
ambient: r.number("--light-ambient", 0.55),
|
|
1313
|
-
key: r.number("--light-key", 1),
|
|
1314
|
-
keyColor: (() => {
|
|
1315
|
-
const c = r.color("--light-key-color");
|
|
1316
|
-
return c ? colorCss(c) : "#ffffff";
|
|
1317
|
-
})(),
|
|
1318
|
-
fog: r.number("--scene-fog", 0)
|
|
1319
|
-
}
|
|
1320
|
-
};
|
|
1321
|
-
} finally {
|
|
1322
|
-
r.dispose();
|
|
1323
|
-
}
|
|
1598
|
+
|
|
1599
|
+
// lib/skinning.ts
|
|
1600
|
+
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
1601
|
+
function composeAffine(a, b) {
|
|
1602
|
+
return [
|
|
1603
|
+
a[0] * b[0] + a[2] * b[1],
|
|
1604
|
+
a[1] * b[0] + a[3] * b[1],
|
|
1605
|
+
a[0] * b[2] + a[2] * b[3],
|
|
1606
|
+
a[1] * b[2] + a[3] * b[3],
|
|
1607
|
+
a[0] * b[4] + a[2] * b[5] + a[4],
|
|
1608
|
+
a[1] * b[4] + a[3] * b[5] + a[5]
|
|
1609
|
+
];
|
|
1324
1610
|
}
|
|
1325
|
-
function
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1611
|
+
function invertAffine(m) {
|
|
1612
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
1613
|
+
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
|
|
1614
|
+
const inv = 1 / det;
|
|
1615
|
+
return [
|
|
1616
|
+
m[3] * inv,
|
|
1617
|
+
-m[1] * inv,
|
|
1618
|
+
-m[2] * inv,
|
|
1619
|
+
m[0] * inv,
|
|
1620
|
+
(m[2] * m[5] - m[3] * m[4]) * inv,
|
|
1621
|
+
(m[1] * m[4] - m[0] * m[5]) * inv
|
|
1622
|
+
];
|
|
1333
1623
|
}
|
|
1334
|
-
function
|
|
1335
|
-
|
|
1336
|
-
if (isDiagramTone(value)) return theme.tones[value];
|
|
1337
|
-
if (value.startsWith("var(") || value.startsWith("color-mix(")) {
|
|
1338
|
-
const resolved = resolveThemeColor(value, scope);
|
|
1339
|
-
return resolved ? colorCss(resolved) : theme.tones[fallback];
|
|
1340
|
-
}
|
|
1341
|
-
return value;
|
|
1624
|
+
function applyAffine(m, x, y) {
|
|
1625
|
+
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
1342
1626
|
}
|
|
1343
|
-
function
|
|
1344
|
-
|
|
1627
|
+
function rotationAbout(pivot, degrees) {
|
|
1628
|
+
const r = degrees * Math.PI / 180;
|
|
1629
|
+
const cos = Math.cos(r);
|
|
1630
|
+
const sin = Math.sin(r);
|
|
1631
|
+
const [px, py] = pivot;
|
|
1632
|
+
return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
|
|
1345
1633
|
}
|
|
1346
|
-
function
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1634
|
+
function computeWorldMatrices(bones, pose) {
|
|
1635
|
+
const indexByName = /* @__PURE__ */ new Map();
|
|
1636
|
+
bones.forEach((b, i) => indexByName.set(b.name, i));
|
|
1637
|
+
const world = new Array(bones.length);
|
|
1638
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
1639
|
+
const resolve = (i) => {
|
|
1640
|
+
const cached = world[i];
|
|
1641
|
+
if (cached) return cached;
|
|
1642
|
+
if (visiting.has(i)) return IDENTITY;
|
|
1643
|
+
visiting.add(i);
|
|
1644
|
+
const b = bones[i];
|
|
1645
|
+
const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
|
|
1646
|
+
const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
|
|
1647
|
+
const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
|
|
1648
|
+
const m = composeAffine(parentWorld, local);
|
|
1649
|
+
visiting.delete(i);
|
|
1650
|
+
world[i] = m;
|
|
1651
|
+
return m;
|
|
1652
|
+
};
|
|
1653
|
+
return bones.map((_, i) => resolve(i));
|
|
1350
1654
|
}
|
|
1351
|
-
function
|
|
1352
|
-
|
|
1655
|
+
function skinVertices(mesh, bindWorld, curWorld) {
|
|
1656
|
+
const skinMats = /* @__PURE__ */ new Map();
|
|
1657
|
+
const skinMat = (bone) => {
|
|
1658
|
+
let m = skinMats.get(bone);
|
|
1659
|
+
if (!m) {
|
|
1660
|
+
m = composeAffine(curWorld[bone] ?? IDENTITY, invertAffine(bindWorld[bone] ?? IDENTITY));
|
|
1661
|
+
skinMats.set(bone, m);
|
|
1662
|
+
}
|
|
1663
|
+
return m;
|
|
1664
|
+
};
|
|
1665
|
+
return mesh.vertices.map((v) => {
|
|
1666
|
+
if (v.weights.length === 0) return [v.x, v.y];
|
|
1667
|
+
let x = 0;
|
|
1668
|
+
let y = 0;
|
|
1669
|
+
for (const { bone, w } of v.weights) {
|
|
1670
|
+
const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
|
|
1671
|
+
x += w * bx;
|
|
1672
|
+
y += w * by;
|
|
1673
|
+
}
|
|
1674
|
+
return [x, y];
|
|
1675
|
+
});
|
|
1353
1676
|
}
|
|
1354
|
-
function
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1677
|
+
function triangleAffine(src, dst) {
|
|
1678
|
+
const [s0, s1, s2] = src;
|
|
1679
|
+
const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
|
|
1680
|
+
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
|
|
1681
|
+
const [d0, d1, d2] = dst;
|
|
1682
|
+
const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
|
|
1683
|
+
const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
|
|
1684
|
+
const e = (d0[0] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[0] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[0] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
|
|
1685
|
+
const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
|
|
1686
|
+
const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
|
|
1687
|
+
const f = (d0[1] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[1] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[1] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
|
|
1688
|
+
return [a, b, c, d, e, f];
|
|
1363
1689
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1690
|
+
var meshCache = /* @__PURE__ */ new Map();
|
|
1691
|
+
function isRigBundle(json) {
|
|
1692
|
+
return json.mesh !== void 0 || json.bones !== void 0;
|
|
1366
1693
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1694
|
+
function getRigBundle(url, onReady) {
|
|
1695
|
+
if (meshCache.has(url)) return meshCache.get(url) ?? void 0;
|
|
1696
|
+
meshCache.set(url, void 0);
|
|
1697
|
+
fetch(url).then((r) => {
|
|
1698
|
+
if (!r.ok) throw new Error(`rigbundle fetch failed: HTTP ${r.status}`);
|
|
1699
|
+
return r.json();
|
|
1700
|
+
}).then((json) => {
|
|
1701
|
+
const ok = isRigBundle(json) || Array.isArray(json.vertices);
|
|
1702
|
+
if (!ok) throw new Error("rigbundle payload is neither a SkinMesh nor a rigbundle");
|
|
1703
|
+
meshCache.set(url, json);
|
|
1704
|
+
onReady();
|
|
1705
|
+
}).catch(() => {
|
|
1706
|
+
meshCache.set(url, null);
|
|
1707
|
+
onReady();
|
|
1708
|
+
});
|
|
1709
|
+
return void 0;
|
|
1369
1710
|
}
|
|
1370
|
-
function
|
|
1371
|
-
|
|
1372
|
-
ctx.lineJoin = theme.lineJoin;
|
|
1373
|
-
if (theme.glow > 0) {
|
|
1374
|
-
ctx.shadowColor = strokeColor;
|
|
1375
|
-
ctx.shadowBlur = theme.glow;
|
|
1376
|
-
ctx.shadowOffsetX = 0;
|
|
1377
|
-
ctx.shadowOffsetY = 0;
|
|
1378
|
-
}
|
|
1711
|
+
function skinMeshFailed(url) {
|
|
1712
|
+
return meshCache.get(url) === null;
|
|
1379
1713
|
}
|
|
1380
|
-
function
|
|
1381
|
-
if (
|
|
1382
|
-
|
|
1383
|
-
|
|
1714
|
+
function vertexBounds(points) {
|
|
1715
|
+
if (points.length === 0) return null;
|
|
1716
|
+
let minX = Infinity;
|
|
1717
|
+
let minY = Infinity;
|
|
1718
|
+
let maxX = -Infinity;
|
|
1719
|
+
let maxY = -Infinity;
|
|
1720
|
+
for (const [x, y] of points) {
|
|
1721
|
+
if (x < minX) minX = x;
|
|
1722
|
+
if (y < minY) minY = y;
|
|
1723
|
+
if (x > maxX) maxX = x;
|
|
1724
|
+
if (y > maxY) maxY = y;
|
|
1725
|
+
}
|
|
1726
|
+
return { minX, minY, maxX, maxY };
|
|
1384
1727
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1728
|
+
|
|
1729
|
+
// components/game/atoms/DrawSkinnedMesh.tsx
|
|
1730
|
+
var skinnedLog = createLogger("almadar:ui:draw-skinned-mesh");
|
|
1731
|
+
var loggedMissing = /* @__PURE__ */ new Set();
|
|
1732
|
+
function warnMissingOnce(reason, node) {
|
|
1733
|
+
const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
|
|
1734
|
+
if (loggedMissing.has(key)) return;
|
|
1735
|
+
loggedMissing.add(key);
|
|
1736
|
+
skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
|
|
1388
1737
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
const b = pts[i];
|
|
1398
|
-
if (amp === 0) {
|
|
1399
|
-
ctx.lineTo(b.x, b.y);
|
|
1400
|
-
continue;
|
|
1401
|
-
}
|
|
1402
|
-
const steps = Math.max(1, Math.round(Math.hypot(b.x - a.x, b.y - a.y) / 24));
|
|
1403
|
-
for (let s = 1; s <= steps; s++) {
|
|
1404
|
-
const t = s / steps;
|
|
1405
|
-
const x = a.x + (b.x - a.x) * t;
|
|
1406
|
-
const y = a.y + (b.y - a.y) * t;
|
|
1407
|
-
ctx.lineTo(x + amp * wobble(x, y, 2), y + amp * wobble(y, x, 3));
|
|
1738
|
+
var WEIGHT_PALETTE = Array.from({ length: 10 }, (_, i) => `hsl(${i * 36 % 360}, 70%, 55%)`);
|
|
1739
|
+
function dominantBone(v) {
|
|
1740
|
+
let best = -1;
|
|
1741
|
+
let bestW = 0;
|
|
1742
|
+
for (const { bone, w } of v.weights) {
|
|
1743
|
+
if (w > bestW) {
|
|
1744
|
+
bestW = w;
|
|
1745
|
+
best = bone;
|
|
1408
1746
|
}
|
|
1409
1747
|
}
|
|
1410
|
-
|
|
1748
|
+
return best;
|
|
1411
1749
|
}
|
|
1412
|
-
function
|
|
1413
|
-
const
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
const t = startRad + (endRad - startRad) * i / steps;
|
|
1417
|
-
out.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });
|
|
1750
|
+
function resolveSkinPose(node, clip) {
|
|
1751
|
+
const effectiveClip = clip ?? node.clip;
|
|
1752
|
+
if (effectiveClip && node.frame !== void 0 && effectiveClip.frames.length > 0) {
|
|
1753
|
+
return effectiveClip.frames[node.frame % effectiveClip.frames.length];
|
|
1418
1754
|
}
|
|
1419
|
-
return
|
|
1755
|
+
return node.pose ?? {};
|
|
1420
1756
|
}
|
|
1421
|
-
function
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
if (
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1432
|
-
if (style === "hatch") {
|
|
1433
|
-
ctx.clip();
|
|
1434
|
-
const span = ctx.canvas.width + ctx.canvas.height;
|
|
1435
|
-
ctx.strokeStyle = color;
|
|
1436
|
-
ctx.lineWidth = theme.strokes.thin;
|
|
1437
|
-
ctx.setLineDash([]);
|
|
1438
|
-
ctx.beginPath();
|
|
1439
|
-
for (let d = -span; d < span; d += 6) {
|
|
1440
|
-
ctx.moveTo(d, 0);
|
|
1441
|
-
ctx.lineTo(d + span, span);
|
|
1442
|
-
}
|
|
1443
|
-
ctx.stroke();
|
|
1444
|
-
} else {
|
|
1445
|
-
ctx.globalAlpha *= style === "tint" ? theme.fillOpacity : 1;
|
|
1446
|
-
ctx.fillStyle = color;
|
|
1447
|
-
ctx.fill();
|
|
1448
|
-
}
|
|
1449
|
-
ctx.restore();
|
|
1757
|
+
function resolveRig(node, dctx) {
|
|
1758
|
+
const fetched = node.meshUrl ? getRigBundle(node.meshUrl, dctx.invalidate) : void 0;
|
|
1759
|
+
if (node.meshUrl && fetched === void 0) return void 0;
|
|
1760
|
+
const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
|
|
1761
|
+
const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
|
|
1762
|
+
if (!mesh) return void 0;
|
|
1763
|
+
const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
|
|
1764
|
+
if (bones.length === 0) return void 0;
|
|
1765
|
+
const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
|
|
1766
|
+
return { mesh, bones, clip };
|
|
1450
1767
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
ctx.lineWidth = width;
|
|
1460
|
-
ctx.setLineDash([]);
|
|
1461
|
-
switch (theme.marker) {
|
|
1462
|
-
case "triangle": {
|
|
1463
|
-
ctx.beginPath();
|
|
1464
|
-
ctx.moveTo(x2, y2);
|
|
1465
|
-
ctx.lineTo(left.x, left.y);
|
|
1466
|
-
ctx.lineTo(right.x, right.y);
|
|
1467
|
-
ctx.closePath();
|
|
1468
|
-
ctx.fill();
|
|
1469
|
-
break;
|
|
1470
|
-
}
|
|
1471
|
-
case "open":
|
|
1472
|
-
case "line": {
|
|
1473
|
-
ctx.beginPath();
|
|
1474
|
-
ctx.moveTo(left.x, left.y);
|
|
1475
|
-
ctx.lineTo(x2, y2);
|
|
1476
|
-
ctx.lineTo(right.x, right.y);
|
|
1477
|
-
if (theme.marker === "open") ctx.stroke();
|
|
1478
|
-
else {
|
|
1479
|
-
ctx.moveTo(x2 - size * 0.5 * Math.cos(angle + Math.PI / 2), y2 - size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
1480
|
-
ctx.lineTo(x2 + size * 0.5 * Math.cos(angle + Math.PI / 2), y2 + size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
1481
|
-
ctx.stroke();
|
|
1482
|
-
}
|
|
1768
|
+
var pixelUVByMesh = /* @__PURE__ */ new WeakMap();
|
|
1769
|
+
function meshHasPixelUVs(mesh) {
|
|
1770
|
+
const cached = pixelUVByMesh.get(mesh);
|
|
1771
|
+
if (cached !== void 0) return cached;
|
|
1772
|
+
let pixel = false;
|
|
1773
|
+
for (const v of mesh.vertices) {
|
|
1774
|
+
if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
|
|
1775
|
+
pixel = true;
|
|
1483
1776
|
break;
|
|
1484
1777
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1778
|
+
}
|
|
1779
|
+
pixelUVByMesh.set(mesh, pixel);
|
|
1780
|
+
return pixel;
|
|
1781
|
+
}
|
|
1782
|
+
function paintMeshPass(painter, node, bones, mesh, tex, dctx, pose, opacity) {
|
|
1783
|
+
const bindWorld = computeWorldMatrices(bones, {});
|
|
1784
|
+
const curWorld = computeWorldMatrices(bones, pose);
|
|
1785
|
+
const skinned = skinVertices(mesh, bindWorld, curWorld);
|
|
1786
|
+
const origin = dctx.projector.project(node.position);
|
|
1787
|
+
const tw = dctx.projector.tileWidth;
|
|
1788
|
+
painter.save();
|
|
1789
|
+
if (opacity !== 1) painter.setAlpha(opacity);
|
|
1790
|
+
for (const [i0, i1, i2] of mesh.triangles) {
|
|
1791
|
+
const v0 = mesh.vertices[i0];
|
|
1792
|
+
const v1 = mesh.vertices[i1];
|
|
1793
|
+
const v2 = mesh.vertices[i2];
|
|
1794
|
+
const p0 = skinned[i0];
|
|
1795
|
+
const p1 = skinned[i1];
|
|
1796
|
+
const p2 = skinned[i2];
|
|
1797
|
+
if (!v0 || !v1 || !v2 || !p0 || !p1 || !p2) continue;
|
|
1798
|
+
const dst = [
|
|
1799
|
+
[origin.x + p0[0] * tw, origin.y + p0[1] * tw],
|
|
1800
|
+
[origin.x + p1[0] * tw, origin.y + p1[1] * tw],
|
|
1801
|
+
[origin.x + p2[0] * tw, origin.y + p2[1] * tw]
|
|
1802
|
+
];
|
|
1803
|
+
if (node.weightsOverlay || !tex) {
|
|
1804
|
+
if (!node.weightsOverlay) continue;
|
|
1805
|
+
const bone = dominantBone(v0);
|
|
1806
|
+
painter.fillPoly(
|
|
1807
|
+
dst.map(([x, y]) => ({ x, y })),
|
|
1808
|
+
WEIGHT_PALETTE[(bone % WEIGHT_PALETTE.length + WEIGHT_PALETTE.length) % WEIGHT_PALETTE.length]
|
|
1809
|
+
);
|
|
1810
|
+
continue;
|
|
1490
1811
|
}
|
|
1812
|
+
const pixelUV = meshHasPixelUVs(mesh);
|
|
1813
|
+
const uv = pixelUV ? [[v0.u, v0.v], [v1.u, v1.v], [v2.u, v2.v]] : [
|
|
1814
|
+
[v0.u * tex.width, v0.v * tex.height],
|
|
1815
|
+
[v1.u * tex.width, v1.v * tex.height],
|
|
1816
|
+
[v2.u * tex.width, v2.v * tex.height]
|
|
1817
|
+
];
|
|
1818
|
+
const affine = triangleAffine(uv, dst);
|
|
1819
|
+
if (!affine) continue;
|
|
1820
|
+
painter.save();
|
|
1821
|
+
painter.clipPath(`M ${dst[0][0]} ${dst[0][1]} L ${dst[1][0]} ${dst[1][1]} L ${dst[2][0]} ${dst[2][1]} Z`);
|
|
1822
|
+
painter.blitTransformed(tex, { x: 0, y: 0, w: tex.width, h: tex.height }, affine);
|
|
1823
|
+
painter.restore();
|
|
1491
1824
|
}
|
|
1492
|
-
|
|
1825
|
+
painter.restore();
|
|
1493
1826
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1827
|
+
var paintSkinnedMesh = (painter, node, dctx) => {
|
|
1828
|
+
if (!isValidScenePos(node.position)) return;
|
|
1829
|
+
const rig = resolveRig(node, dctx);
|
|
1830
|
+
if (!rig) {
|
|
1831
|
+
if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce("mesh-failed", node);
|
|
1832
|
+
else if (!node.mesh && !node.meshUrl) warnMissingOnce("mesh-missing", node);
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
const tex = node.weightsOverlay ? null : painter.resolveTexture(node.asset.url);
|
|
1836
|
+
if (!node.weightsOverlay && !tex) {
|
|
1837
|
+
if (getImageStatus(node.asset.url) === "failed") warnMissingOnce("texture-failed", node);
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
if (node.ghost && rig.clip && rig.clip.frames.length > 0) {
|
|
1841
|
+
paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, rig.clip.frames[node.ghost.frame % rig.clip.frames.length], node.ghost.opacity);
|
|
1842
|
+
}
|
|
1843
|
+
paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
|
|
1844
|
+
};
|
|
1845
|
+
function DrawSkinnedMesh(props) {
|
|
1846
|
+
const register = useContext(DrawableRegistryContext);
|
|
1847
|
+
if (register) register({ ...props, type: "draw-skinned-mesh" });
|
|
1848
|
+
return null;
|
|
1505
1849
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1850
|
+
var MESH_SEGMENTS_DEFAULT = 24;
|
|
1851
|
+
function polyhedronBounds(vertices) {
|
|
1852
|
+
if (!vertices || vertices.length < 3) return null;
|
|
1853
|
+
const min = [Infinity, Infinity, Infinity];
|
|
1854
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
1855
|
+
for (const v of vertices) {
|
|
1856
|
+
if (v.length < 3 || !v.every((c) => Number.isFinite(c))) return null;
|
|
1857
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1858
|
+
min[axis] = Math.min(min[axis], v[axis]);
|
|
1859
|
+
max[axis] = Math.max(max[axis], v[axis]);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
return { min, max };
|
|
1510
1863
|
}
|
|
1511
|
-
function
|
|
1512
|
-
|
|
1513
|
-
return
|
|
1864
|
+
function clampSegments(segments) {
|
|
1865
|
+
const s = segments ?? MESH_SEGMENTS_DEFAULT;
|
|
1866
|
+
return Math.min(64, Math.max(3, Math.round(s)));
|
|
1514
1867
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
function spriteRect(projector, node, natural) {
|
|
1518
|
-
const tw = projector.tileWidth;
|
|
1519
|
-
const fallbackW = projector.worldPixelDirect ? natural?.w ?? 0 : tw;
|
|
1520
|
-
const fallbackH = projector.worldPixelDirect ? natural?.h ?? 0 : tw;
|
|
1521
|
-
const w = node.width !== void 0 ? node.width * tw : fallbackW;
|
|
1522
|
-
const h = node.height !== void 0 ? node.height * tw : fallbackH;
|
|
1523
|
-
const anchor = node.anchor ?? "top-left";
|
|
1524
|
-
const p = projector.anchorPoint(node.position, anchor);
|
|
1525
|
-
return {
|
|
1526
|
-
x: anchor === "top-left" ? p.x : p.x - w / 2,
|
|
1527
|
-
y: anchor === "ground" ? p.y - h : anchor === "center" ? p.y - h / 2 : p.y,
|
|
1528
|
-
w,
|
|
1529
|
-
h
|
|
1530
|
-
};
|
|
1868
|
+
function isAnimatedMesh(node) {
|
|
1869
|
+
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1531
1870
|
}
|
|
1532
|
-
var
|
|
1533
|
-
var
|
|
1871
|
+
var lerp2 = (a, b, k) => a + (b - a) * k;
|
|
1872
|
+
var NUMERIC_TRACKS2 = [
|
|
1534
1873
|
"offsetX",
|
|
1535
1874
|
"offsetY",
|
|
1536
|
-
"
|
|
1875
|
+
"offsetZ",
|
|
1876
|
+
"rotateX",
|
|
1877
|
+
"rotateY",
|
|
1878
|
+
"rotateZ",
|
|
1879
|
+
"scale",
|
|
1537
1880
|
"opacity",
|
|
1538
|
-
"
|
|
1539
|
-
"radiusY",
|
|
1540
|
-
"width",
|
|
1541
|
-
"height",
|
|
1542
|
-
"strokeWidth",
|
|
1543
|
-
"strokeDashOffset",
|
|
1544
|
-
"blur"
|
|
1881
|
+
"emissiveIntensity"
|
|
1545
1882
|
];
|
|
1546
|
-
function
|
|
1547
|
-
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1548
|
-
}
|
|
1549
|
-
var lerp = (a, b, k) => a + (b - a) * k;
|
|
1550
|
-
function applyShapeAnimation(node, timeMs) {
|
|
1883
|
+
function applyMeshAnimation(node, timeMs) {
|
|
1551
1884
|
const anim = node.animation;
|
|
1552
|
-
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return
|
|
1885
|
+
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
|
|
1553
1886
|
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
1554
1887
|
const cycle = timeMs / anim.durationMs;
|
|
1555
1888
|
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
1556
|
-
const out = { ...node };
|
|
1557
1889
|
const trackValue = (key) => {
|
|
1558
1890
|
const defined = frames.filter((f) => f[key] !== void 0);
|
|
1559
1891
|
if (defined.length === 0) return void 0;
|
|
@@ -1569,736 +1901,704 @@ function applyShapeAnimation(node, timeMs) {
|
|
|
1569
1901
|
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
1570
1902
|
const a = prev[key];
|
|
1571
1903
|
const b = next[key];
|
|
1572
|
-
if (typeof a === "number" && typeof b === "number") return
|
|
1904
|
+
if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
|
|
1573
1905
|
return a;
|
|
1574
1906
|
};
|
|
1575
|
-
|
|
1907
|
+
const num = {};
|
|
1908
|
+
for (const key of NUMERIC_TRACKS2) {
|
|
1576
1909
|
const v = trackValue(key);
|
|
1577
|
-
if (v !== void 0)
|
|
1910
|
+
if (v !== void 0) num[key] = v;
|
|
1578
1911
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1912
|
+
return {
|
|
1913
|
+
offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
|
|
1914
|
+
rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
|
|
1915
|
+
scale: num.scale ?? 1,
|
|
1916
|
+
opacity: num.opacity,
|
|
1917
|
+
emissiveIntensity: num.emissiveIntensity,
|
|
1918
|
+
color: trackValue("color"),
|
|
1919
|
+
emissive: trackValue("emissive")
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function DrawMesh(props) {
|
|
1923
|
+
const register = useContext(DrawableRegistryContext);
|
|
1924
|
+
if (register) register({ ...props, type: "draw-mesh" });
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// components/core/atoms/fx.ts
|
|
1929
|
+
function resolveFxView(item, presets) {
|
|
1930
|
+
const row = presets?.find((p) => p.type === item.type);
|
|
1931
|
+
if (!row) return item;
|
|
1932
|
+
return {
|
|
1933
|
+
...item,
|
|
1934
|
+
space: item.space ?? row.space,
|
|
1935
|
+
effect: item.effect ?? row.effect,
|
|
1936
|
+
color: item.color ?? row.color,
|
|
1937
|
+
size: item.size ?? row.size,
|
|
1938
|
+
vx: item.vx ?? row.vx,
|
|
1939
|
+
vy: item.vy ?? row.vy,
|
|
1940
|
+
vz: item.vz ?? row.vz,
|
|
1941
|
+
particleCount: item.particleCount ?? row.particleCount,
|
|
1942
|
+
animation: item.animation ?? row.animation,
|
|
1943
|
+
shape: row.shape,
|
|
1944
|
+
count: row.count,
|
|
1945
|
+
glow: row.glow,
|
|
1946
|
+
gravity: row.gravity,
|
|
1947
|
+
color2: row.color2
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function fxLifecycle(item, epochNowMs, tickMs) {
|
|
1951
|
+
const maxTtl = Math.max(item.maxTtl ?? item.ttl, item.ttl, 1);
|
|
1952
|
+
const lifeMs = maxTtl * tickMs;
|
|
1953
|
+
const ageMs = item.bornAt !== void 0 && epochNowMs > 0 ? Math.max(0, epochNowMs - item.bornAt) : (1 - item.ttl / maxTtl) * lifeMs;
|
|
1954
|
+
const progress = Math.min(1, Math.max(0, ageMs / lifeMs));
|
|
1955
|
+
return { lifeMs, ageMs, progress, fade: 1 - progress };
|
|
1956
|
+
}
|
|
1957
|
+
function fxHash01(seed, salt) {
|
|
1958
|
+
let h = 2166136261 ^ salt;
|
|
1959
|
+
for (let i = 0; i < seed.length; i++) {
|
|
1960
|
+
h ^= seed.charCodeAt(i);
|
|
1961
|
+
h = Math.imul(h, 16777619);
|
|
1601
1962
|
}
|
|
1602
|
-
|
|
1963
|
+
h ^= h >>> 13;
|
|
1964
|
+
h = Math.imul(h, 1274126177);
|
|
1965
|
+
h ^= h >>> 16;
|
|
1966
|
+
return (h >>> 0) / 4294967296;
|
|
1603
1967
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1968
|
+
var CONFETTI_COLORS = [
|
|
1969
|
+
"var(--color-primary)",
|
|
1970
|
+
"var(--color-success)",
|
|
1971
|
+
"var(--color-warning)",
|
|
1972
|
+
"var(--color-error)",
|
|
1973
|
+
"gold",
|
|
1974
|
+
"dodgerblue"
|
|
1975
|
+
];
|
|
1976
|
+
function createConfettiParticles(count, seed) {
|
|
1977
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
1978
|
+
id: i,
|
|
1979
|
+
color: CONFETTI_COLORS[Math.floor(fxHash01(seed, i * 7 + 1) * CONFETTI_COLORS.length)],
|
|
1980
|
+
left: 30 + fxHash01(seed, i * 7 + 2) * 40,
|
|
1981
|
+
delay: fxHash01(seed, i * 7 + 3) * 300,
|
|
1982
|
+
angle: fxHash01(seed, i * 7 + 4) * 360,
|
|
1983
|
+
distance: 40 + fxHash01(seed, i * 7 + 5) * 80,
|
|
1984
|
+
rotation: fxHash01(seed, i * 7 + 6) * 720 - 360,
|
|
1985
|
+
size: 4 + fxHash01(seed, i * 7 + 7) * 6
|
|
1986
|
+
}));
|
|
1987
|
+
}
|
|
1988
|
+
var CONFETTI_BURST_KEYFRAMES = `
|
|
1989
|
+
@keyframes confetti-burst {
|
|
1990
|
+
0% {
|
|
1991
|
+
opacity: 1;
|
|
1992
|
+
transform: translate(0, 0) rotate(0deg) scale(1);
|
|
1615
1993
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1994
|
+
70% {
|
|
1995
|
+
opacity: 1;
|
|
1996
|
+
}
|
|
1997
|
+
100% {
|
|
1998
|
+
opacity: 0;
|
|
1999
|
+
transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
`;
|
|
2003
|
+
|
|
2004
|
+
// components/game/molecules/DrawFxLayer.tsx
|
|
2005
|
+
var DEFAULT_TICK_MS = 500;
|
|
2006
|
+
var DEFAULT_TEXT_COLOR = "#ffe066";
|
|
2007
|
+
var DEFAULT_SPARK_COLOR = "#ffffff";
|
|
2008
|
+
var easeOut = (t) => 1 - (1 - t) * (1 - t);
|
|
2009
|
+
function fxPosition(view, dim, ageSec) {
|
|
2010
|
+
const g = view.gravity ?? 0;
|
|
2011
|
+
const fall = 0.5 * g * ageSec * ageSec;
|
|
2012
|
+
if (dim === "2d") {
|
|
2013
|
+
const base2 = view.position ?? { x: view.x, y: view.z ?? view.y ?? 0 };
|
|
2014
|
+
if (!Number.isFinite(base2.x) || !Number.isFinite(base2.y)) return void 0;
|
|
1618
2015
|
return {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
cy: oy + g.center.y * scale,
|
|
1622
|
-
angle: g.angle ?? 0,
|
|
1623
|
-
stops: g.stops
|
|
2016
|
+
x: base2.x + (view.vx ?? 0) * ageSec,
|
|
2017
|
+
y: base2.y + (view.vz ?? 0) * ageSec + fall
|
|
1624
2018
|
};
|
|
1625
2019
|
}
|
|
1626
|
-
|
|
2020
|
+
const base = view.position ?? { x: view.x, y: view.z ?? 0, z: view.y };
|
|
2021
|
+
if (!Number.isFinite(base.x) || !Number.isFinite(base.y)) return void 0;
|
|
1627
2022
|
return {
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
r: g.radius * scale,
|
|
1632
|
-
stops: g.stops
|
|
2023
|
+
x: base.x + (view.vx ?? 0) * ageSec,
|
|
2024
|
+
y: base.y + (view.vz ?? 0) * ageSec,
|
|
2025
|
+
...base.z !== void 0 || view.vy !== void 0 || g !== 0 ? { z: (base.z ?? 0) + (view.vy ?? 0) * ageSec - fall } : {}
|
|
1633
2026
|
};
|
|
1634
2027
|
}
|
|
1635
|
-
function
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2028
|
+
function sparkShape(view, pos, i, fade, progress) {
|
|
2029
|
+
const size = view.size ?? 0.5;
|
|
2030
|
+
const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
|
|
2031
|
+
const dist = size * (0.4 + 0.6 * fxHash01(view.id, i * 3 + 1)) * easeOut(progress);
|
|
2032
|
+
const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
|
|
2033
|
+
const color = view.color ?? DEFAULT_SPARK_COLOR;
|
|
2034
|
+
return {
|
|
2035
|
+
type: "draw-shape",
|
|
2036
|
+
shape: "ellipse",
|
|
2037
|
+
position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
|
|
2038
|
+
anchor: "center",
|
|
2039
|
+
radiusX: r,
|
|
2040
|
+
fill: color,
|
|
2041
|
+
blendMode: "lighter",
|
|
2042
|
+
opacity: fade,
|
|
2043
|
+
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2044
|
+
};
|
|
1641
2045
|
}
|
|
1642
|
-
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
const px = origin.x + pivot.x * tileWidth;
|
|
1662
|
-
const py = origin.y + pivot.y * tileWidth;
|
|
1663
|
-
painter.translate(px, py);
|
|
1664
|
-
painter.rotate(node.rotate);
|
|
1665
|
-
painter.translate(-px, -py);
|
|
1666
|
-
}
|
|
1667
|
-
if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth * groupScale });
|
|
1668
|
-
const fill = node.fill === "none" ? void 0 : node.fill;
|
|
1669
|
-
const stroke = node.stroke === "none" ? void 0 : node.stroke;
|
|
1670
|
-
const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
|
|
1671
|
-
const pxStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, origin.x, origin.y, tileWidth) ?? stroke : stroke;
|
|
1672
|
-
const patFill = node.fillPattern ? patternStyle(node.fillPattern, groupScale) : void 0;
|
|
1673
|
-
switch (node.shape) {
|
|
1674
|
-
case "cell": {
|
|
1675
|
-
const pts = dctx.projector.cellPath(node.position);
|
|
1676
|
-
if (pxFill) painter.fillPoly(pts, pxFill);
|
|
1677
|
-
if (patFill) painter.fillPoly(pts, patFill);
|
|
1678
|
-
if (pxStroke) painter.strokePoly(pts, pxStroke, strokePx, true);
|
|
1679
|
-
break;
|
|
1680
|
-
}
|
|
1681
|
-
case "rect": {
|
|
1682
|
-
const p = dctx.projector.anchorPoint(node.position, node.anchor ?? "top-left");
|
|
1683
|
-
const tw = dctx.projector.tileWidth;
|
|
1684
|
-
const x = p.x + (node.offsetX ?? 0) * tw;
|
|
1685
|
-
const y = p.y + (node.offsetY ?? 0) * tw;
|
|
1686
|
-
const w = (node.width ?? 0) * tw;
|
|
1687
|
-
const h = (node.height ?? 0) * tw;
|
|
1688
|
-
if (pxFill) painter.fillRect(x, y, w, h, pxFill);
|
|
1689
|
-
if (patFill) painter.fillRect(x, y, w, h, patFill);
|
|
1690
|
-
if (pxStroke) painter.strokeRect(x, y, w, h, pxStroke, strokePx);
|
|
1691
|
-
break;
|
|
2046
|
+
function proceduralShapes(view, pos, fade, progress) {
|
|
2047
|
+
const size = view.size ?? 0.5;
|
|
2048
|
+
const color = view.color ?? DEFAULT_SPARK_COLOR;
|
|
2049
|
+
switch (view.shape ?? "spark") {
|
|
2050
|
+
case "ring": {
|
|
2051
|
+
return [
|
|
2052
|
+
{
|
|
2053
|
+
type: "draw-shape",
|
|
2054
|
+
shape: "ellipse",
|
|
2055
|
+
position: pos,
|
|
2056
|
+
anchor: "center",
|
|
2057
|
+
radiusX: size * easeOut(progress),
|
|
2058
|
+
stroke: view.color2 ?? color,
|
|
2059
|
+
strokeWidth: 2,
|
|
2060
|
+
blendMode: "lighter",
|
|
2061
|
+
opacity: fade,
|
|
2062
|
+
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2063
|
+
}
|
|
2064
|
+
];
|
|
1692
2065
|
}
|
|
1693
|
-
case "
|
|
1694
|
-
const
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2066
|
+
case "puff": {
|
|
2067
|
+
const count = view.count ?? 3;
|
|
2068
|
+
return Array.from({ length: count }, (_, i) => {
|
|
2069
|
+
const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
|
|
2070
|
+
const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut(progress);
|
|
2071
|
+
return {
|
|
2072
|
+
type: "draw-shape",
|
|
2073
|
+
shape: "ellipse",
|
|
2074
|
+
position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
|
|
2075
|
+
anchor: "center",
|
|
2076
|
+
radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
|
|
2077
|
+
fill: i === 0 && view.color2 ? view.color2 : color,
|
|
2078
|
+
opacity: fade * 0.6,
|
|
2079
|
+
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2080
|
+
};
|
|
2081
|
+
});
|
|
1704
2082
|
}
|
|
1705
|
-
case "
|
|
1706
|
-
const
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
2083
|
+
case "streak": {
|
|
2084
|
+
const count = view.count ?? 5;
|
|
2085
|
+
return Array.from({ length: count }, (_, i) => {
|
|
2086
|
+
const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
|
|
2087
|
+
const inner = size * (0.2 + 0.8 * easeOut(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
|
|
2088
|
+
const len = size * 0.35;
|
|
2089
|
+
return {
|
|
2090
|
+
type: "draw-shape",
|
|
2091
|
+
shape: "ellipse",
|
|
2092
|
+
position: pos,
|
|
2093
|
+
anchor: "center",
|
|
2094
|
+
offsetX: inner + len / 2,
|
|
2095
|
+
offsetY: 0,
|
|
2096
|
+
radiusX: len / 2,
|
|
2097
|
+
radiusY: size * 0.04,
|
|
2098
|
+
rotate: angle,
|
|
2099
|
+
fill: color,
|
|
2100
|
+
blendMode: "lighter",
|
|
2101
|
+
opacity: fade,
|
|
2102
|
+
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2103
|
+
};
|
|
2104
|
+
});
|
|
1714
2105
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
painter.scale(tileWidth, tileWidth);
|
|
1719
|
-
if (node.strokeDash && node.strokeDash.length > 0) {
|
|
1720
|
-
painter.setLineDash(
|
|
1721
|
-
node.strokeDash.map((v) => v / (groupScale * tileWidth)),
|
|
1722
|
-
(node.strokeDashOffset ?? 0) / (groupScale * tileWidth)
|
|
1723
|
-
);
|
|
1724
|
-
}
|
|
1725
|
-
if (node.blur !== void 0 && node.blur > 0) painter.setBlur(node.blur / (groupScale * tileWidth));
|
|
1726
|
-
const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
|
|
1727
|
-
const localStroke = node.strokeGradient ? gradientStyle(node.strokeGradient, 0, 0, 1) ?? stroke : stroke;
|
|
1728
|
-
const localPat = node.fillPattern ? patternStyle(node.fillPattern, groupScale * tileWidth) : void 0;
|
|
1729
|
-
if (localFill) painter.fillPath(node.d, localFill);
|
|
1730
|
-
if (localPat) painter.fillPath(node.d, localPat);
|
|
1731
|
-
if (localStroke) painter.strokePath(node.d, localStroke, strokePx / tileWidth);
|
|
1732
|
-
break;
|
|
2106
|
+
default: {
|
|
2107
|
+
const count = view.count ?? 6;
|
|
2108
|
+
return Array.from({ length: count }, (_, i) => sparkShape(view, pos, i, fade, progress));
|
|
1733
2109
|
}
|
|
1734
2110
|
}
|
|
1735
|
-
painter.restore();
|
|
1736
|
-
};
|
|
1737
|
-
function DrawShape(props) {
|
|
1738
|
-
const register = useContext(DrawableRegistryContext);
|
|
1739
|
-
if (register) {
|
|
1740
|
-
const { opacity, ...rest } = props;
|
|
1741
|
-
const node = {
|
|
1742
|
-
...rest,
|
|
1743
|
-
type: "draw-shape",
|
|
1744
|
-
...opacity !== void 0 && opacity > 0 ? { opacity } : {}
|
|
1745
|
-
};
|
|
1746
|
-
register(node);
|
|
1747
|
-
}
|
|
1748
|
-
return null;
|
|
1749
|
-
}
|
|
1750
|
-
function isAnimatedGroup(node) {
|
|
1751
|
-
return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
|
|
1752
|
-
}
|
|
1753
|
-
function DrawGroup(props) {
|
|
1754
|
-
const register = useContext(DrawableRegistryContext);
|
|
1755
|
-
if (register) register({ ...props, type: "draw-group" });
|
|
1756
|
-
return null;
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// lib/imageCache.ts
|
|
1760
|
-
var RETRY_AFTER_MS = 5e3;
|
|
1761
|
-
var cache = /* @__PURE__ */ new Map();
|
|
1762
|
-
function startLoad(url, onReady, existing) {
|
|
1763
|
-
const img = new Image();
|
|
1764
|
-
img.crossOrigin = "anonymous";
|
|
1765
|
-
const entry = existing ?? { img, status: "pending" };
|
|
1766
|
-
entry.img = img;
|
|
1767
|
-
if (onReady) entry.onReady = onReady;
|
|
1768
|
-
img.onload = () => {
|
|
1769
|
-
entry.status = "loaded";
|
|
1770
|
-
updateAssetStatus(url, "loaded");
|
|
1771
|
-
entry.onReady?.();
|
|
1772
|
-
};
|
|
1773
|
-
img.onerror = () => {
|
|
1774
|
-
entry.status = "failed";
|
|
1775
|
-
entry.failedAt = Date.now();
|
|
1776
|
-
updateAssetStatus(url, "failed");
|
|
1777
|
-
entry.onReady?.();
|
|
1778
|
-
};
|
|
1779
|
-
img.src = url;
|
|
1780
|
-
return entry;
|
|
1781
2111
|
}
|
|
1782
|
-
function
|
|
1783
|
-
if (
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
2112
|
+
function expandFxItem(view, node, epochNowMs, dim, projector, fontFamily) {
|
|
2113
|
+
if (view.space === "screen") return [];
|
|
2114
|
+
const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
|
|
2115
|
+
const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
|
|
2116
|
+
if (progress >= 1) return [];
|
|
2117
|
+
const pos = fxPosition(view, dim, ageMs / 1e3);
|
|
2118
|
+
if (!pos) return [];
|
|
2119
|
+
const out = [];
|
|
2120
|
+
const artItems = node.art?.[view.type]?.["idle"];
|
|
2121
|
+
const sprite = node.sprites?.[view.type];
|
|
2122
|
+
if (Array.isArray(artItems)) {
|
|
2123
|
+
out.push({
|
|
2124
|
+
type: "draw-group",
|
|
2125
|
+
position: pos,
|
|
2126
|
+
opacity: fade,
|
|
2127
|
+
...view.size !== void 0 ? { scale: view.size } : {},
|
|
2128
|
+
items: artItems
|
|
2129
|
+
});
|
|
2130
|
+
} else if (sprite?.url) {
|
|
2131
|
+
const spriteSize = view.size ?? 0.6;
|
|
2132
|
+
out.push({
|
|
2133
|
+
type: "draw-sprite",
|
|
2134
|
+
position: pos,
|
|
2135
|
+
asset: sprite,
|
|
2136
|
+
anchor: "center",
|
|
2137
|
+
width: spriteSize,
|
|
2138
|
+
height: spriteSize,
|
|
2139
|
+
opacity: fade,
|
|
2140
|
+
animation: view.animation ?? sprite.animations?.[0],
|
|
2141
|
+
// One-shot fx rows (burst) play from the item's birth, not wall-time.
|
|
2142
|
+
clockMs: ageMs
|
|
2143
|
+
});
|
|
2144
|
+
} else {
|
|
2145
|
+
out.push(...proceduralShapes(view, pos, fade, progress));
|
|
1793
2146
|
}
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
a[0] * b[2] + a[2] * b[3],
|
|
1810
|
-
a[1] * b[2] + a[3] * b[3],
|
|
1811
|
-
a[0] * b[4] + a[2] * b[5] + a[4],
|
|
1812
|
-
a[1] * b[4] + a[3] * b[5] + a[5]
|
|
1813
|
-
];
|
|
1814
|
-
}
|
|
1815
|
-
function invertAffine(m) {
|
|
1816
|
-
const det = m[0] * m[3] - m[1] * m[2];
|
|
1817
|
-
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return IDENTITY;
|
|
1818
|
-
const inv = 1 / det;
|
|
1819
|
-
return [
|
|
1820
|
-
m[3] * inv,
|
|
1821
|
-
-m[1] * inv,
|
|
1822
|
-
-m[2] * inv,
|
|
1823
|
-
m[0] * inv,
|
|
1824
|
-
(m[2] * m[5] - m[3] * m[4]) * inv,
|
|
1825
|
-
(m[1] * m[4] - m[0] * m[5]) * inv
|
|
1826
|
-
];
|
|
1827
|
-
}
|
|
1828
|
-
function applyAffine(m, x, y) {
|
|
1829
|
-
return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
|
|
1830
|
-
}
|
|
1831
|
-
function rotationAbout(pivot, degrees) {
|
|
1832
|
-
const r = degrees * Math.PI / 180;
|
|
1833
|
-
const cos = Math.cos(r);
|
|
1834
|
-
const sin = Math.sin(r);
|
|
1835
|
-
const [px, py] = pivot;
|
|
1836
|
-
return [cos, sin, -sin, cos, px - cos * px + sin * py, py - sin * px - cos * py];
|
|
1837
|
-
}
|
|
1838
|
-
function computeWorldMatrices(bones, pose) {
|
|
1839
|
-
const indexByName = /* @__PURE__ */ new Map();
|
|
1840
|
-
bones.forEach((b, i) => indexByName.set(b.name, i));
|
|
1841
|
-
const world = new Array(bones.length);
|
|
1842
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
1843
|
-
const resolve = (i) => {
|
|
1844
|
-
const cached = world[i];
|
|
1845
|
-
if (cached) return cached;
|
|
1846
|
-
if (visiting.has(i)) return IDENTITY;
|
|
1847
|
-
visiting.add(i);
|
|
1848
|
-
const b = bones[i];
|
|
1849
|
-
const parentIndex = b.parent === null ? void 0 : indexByName.get(b.parent);
|
|
1850
|
-
const parentWorld = parentIndex === void 0 ? IDENTITY : resolve(parentIndex);
|
|
1851
|
-
const local = rotationAbout(b.pivot, pose[b.name] ?? 0);
|
|
1852
|
-
const m = composeAffine(parentWorld, local);
|
|
1853
|
-
visiting.delete(i);
|
|
1854
|
-
world[i] = m;
|
|
1855
|
-
return m;
|
|
1856
|
-
};
|
|
1857
|
-
return bones.map((_, i) => resolve(i));
|
|
2147
|
+
if (view.message) {
|
|
2148
|
+
const pxSize = projector && view.size !== void 0 ? Math.max(10, Math.round(view.size * projector.tileWidth)) : void 0;
|
|
2149
|
+
const family = fontFamily ?? "system-ui, sans-serif";
|
|
2150
|
+
const text = {
|
|
2151
|
+
type: "draw-text",
|
|
2152
|
+
text: view.message,
|
|
2153
|
+
position: pos,
|
|
2154
|
+
offsetY: -(0.15 + 0.55 * progress),
|
|
2155
|
+
color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
|
|
2156
|
+
opacity: fade,
|
|
2157
|
+
font: `bold ${pxSize ?? 14}px ${family}`
|
|
2158
|
+
};
|
|
2159
|
+
out.push(text);
|
|
2160
|
+
}
|
|
2161
|
+
return out;
|
|
1858
2162
|
}
|
|
1859
|
-
function
|
|
1860
|
-
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
if (!
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
return m;
|
|
1868
|
-
};
|
|
1869
|
-
return mesh.vertices.map((v) => {
|
|
1870
|
-
if (v.weights.length === 0) return [v.x, v.y];
|
|
1871
|
-
let x = 0;
|
|
1872
|
-
let y = 0;
|
|
1873
|
-
for (const { bone, w } of v.weights) {
|
|
1874
|
-
const [bx, by] = applyAffine(skinMat(bone), v.x, v.y);
|
|
1875
|
-
x += w * bx;
|
|
1876
|
-
y += w * by;
|
|
1877
|
-
}
|
|
1878
|
-
return [x, y];
|
|
1879
|
-
});
|
|
2163
|
+
function expandFxLayer(node, epochNowMs, dim, projector, fontFamily) {
|
|
2164
|
+
if (!Array.isArray(node.items)) return [];
|
|
2165
|
+
const out = [];
|
|
2166
|
+
for (const item of node.items) {
|
|
2167
|
+
if (!item || typeof item.id !== "string") continue;
|
|
2168
|
+
out.push(...expandFxItem(resolveFxView(item, node.presets), node, epochNowMs, dim, projector, fontFamily));
|
|
2169
|
+
}
|
|
2170
|
+
return out;
|
|
1880
2171
|
}
|
|
1881
|
-
function
|
|
1882
|
-
|
|
1883
|
-
const det = s0[0] * (s1[1] - s2[1]) + s1[0] * (s2[1] - s0[1]) + s2[0] * (s0[1] - s1[1]);
|
|
1884
|
-
if (!Number.isFinite(det) || Math.abs(det) < 1e-12) return null;
|
|
1885
|
-
const [d0, d1, d2] = dst;
|
|
1886
|
-
const a = (d0[0] * (s1[1] - s2[1]) + d1[0] * (s2[1] - s0[1]) + d2[0] * (s0[1] - s1[1])) / det;
|
|
1887
|
-
const c = (d0[0] * (s2[0] - s1[0]) + d1[0] * (s0[0] - s2[0]) + d2[0] * (s1[0] - s0[0])) / det;
|
|
1888
|
-
const e = (d0[0] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[0] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[0] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
|
|
1889
|
-
const b = (d0[1] * (s1[1] - s2[1]) + d1[1] * (s2[1] - s0[1]) + d2[1] * (s0[1] - s1[1])) / det;
|
|
1890
|
-
const d = (d0[1] * (s2[0] - s1[0]) + d1[1] * (s0[0] - s2[0]) + d2[1] * (s1[0] - s0[0])) / det;
|
|
1891
|
-
const f = (d0[1] * (s1[0] * s2[1] - s2[0] * s1[1]) + d1[1] * (s2[0] * s0[1] - s0[0] * s2[1]) + d2[1] * (s0[0] * s1[1] - s1[0] * s0[1])) / det;
|
|
1892
|
-
return [a, b, c, d, e, f];
|
|
2172
|
+
function DrawFxLayer(_props) {
|
|
2173
|
+
return null;
|
|
1893
2174
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2175
|
+
|
|
2176
|
+
// lib/atlasSlice.ts
|
|
2177
|
+
var atlasCache = /* @__PURE__ */ new Map();
|
|
2178
|
+
function isTilesheet(a) {
|
|
2179
|
+
return typeof a.tileWidth === "number";
|
|
1897
2180
|
}
|
|
1898
|
-
function
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
meshCache.set(url, json);
|
|
2181
|
+
function isSpriteSheetAtlas(a) {
|
|
2182
|
+
const s = a;
|
|
2183
|
+
return typeof s.frameWidth === "number" && typeof s.frameHeight === "number" && typeof s.animations === "object";
|
|
2184
|
+
}
|
|
2185
|
+
function getAtlas(url, onReady) {
|
|
2186
|
+
if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
|
|
2187
|
+
atlasCache.set(url, void 0);
|
|
2188
|
+
fetch(url).then((r) => r.json()).then((json) => {
|
|
2189
|
+
atlasCache.set(url, json);
|
|
1908
2190
|
onReady();
|
|
1909
2191
|
}).catch(() => {
|
|
1910
|
-
|
|
2192
|
+
atlasCache.set(url, null);
|
|
1911
2193
|
onReady();
|
|
1912
2194
|
});
|
|
1913
2195
|
return void 0;
|
|
1914
2196
|
}
|
|
1915
|
-
function
|
|
1916
|
-
return
|
|
2197
|
+
function atlasFailed(url) {
|
|
2198
|
+
return atlasCache.get(url) === null;
|
|
1917
2199
|
}
|
|
1918
|
-
function
|
|
1919
|
-
if (
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2200
|
+
function subRectFor(atlas, sprite) {
|
|
2201
|
+
if (isTilesheet(atlas)) {
|
|
2202
|
+
let col;
|
|
2203
|
+
let row;
|
|
2204
|
+
if (sprite.includes(",")) {
|
|
2205
|
+
const [c, r] = sprite.split(",").map((n) => Number(n.trim()));
|
|
2206
|
+
col = c;
|
|
2207
|
+
row = r;
|
|
2208
|
+
} else {
|
|
2209
|
+
const i = Number(sprite);
|
|
2210
|
+
if (!Number.isFinite(i)) return null;
|
|
2211
|
+
col = i % atlas.columns;
|
|
2212
|
+
row = Math.floor(i / atlas.columns);
|
|
2213
|
+
}
|
|
2214
|
+
if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
|
|
2215
|
+
const margin = atlas.margin ?? 0;
|
|
2216
|
+
const spacing = atlas.spacing ?? 0;
|
|
2217
|
+
return {
|
|
2218
|
+
sx: margin + col * (atlas.tileWidth + spacing),
|
|
2219
|
+
sy: margin + row * (atlas.tileHeight + spacing),
|
|
2220
|
+
sw: atlas.tileWidth,
|
|
2221
|
+
sh: atlas.tileHeight
|
|
2222
|
+
};
|
|
1929
2223
|
}
|
|
1930
|
-
|
|
2224
|
+
if (isSpriteSheetAtlas(atlas)) return null;
|
|
2225
|
+
const st = atlas.subTextures[sprite];
|
|
2226
|
+
if (!st) return null;
|
|
2227
|
+
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
1931
2228
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
var skinnedLog = createLogger("almadar:ui:draw-skinned-mesh");
|
|
1935
|
-
var loggedMissing = /* @__PURE__ */ new Set();
|
|
1936
|
-
function warnMissingOnce(reason, node) {
|
|
1937
|
-
const key = `${reason}:${node.asset.url}:${String(node.meshUrl)}`;
|
|
1938
|
-
if (loggedMissing.has(key)) return;
|
|
1939
|
-
loggedMissing.add(key);
|
|
1940
|
-
skinnedLog.warn("draw-skinned-mesh unresolvable \u2014 painting fallback", { reason, url: node.asset.url, meshUrl: node.meshUrl });
|
|
2229
|
+
function atlasUrlOf(asset) {
|
|
2230
|
+
return typeof asset?.atlas === "string" && asset.atlas.length > 0 ? asset.atlas : void 0;
|
|
1941
2231
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
let best = -1;
|
|
1945
|
-
let bestW = 0;
|
|
1946
|
-
for (const { bone, w } of v.weights) {
|
|
1947
|
-
if (w > bestW) {
|
|
1948
|
-
bestW = w;
|
|
1949
|
-
best = bone;
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
return best;
|
|
2232
|
+
function isAtlasAsset(asset) {
|
|
2233
|
+
return atlasUrlOf(asset) !== void 0 && typeof asset?.sprite === "string" && asset.sprite.length > 0;
|
|
1953
2234
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2235
|
+
function resolveAssetSource(img, asset, onReady) {
|
|
2236
|
+
if (isAtlasAsset(asset)) {
|
|
2237
|
+
const atlas = getAtlas(asset.atlas, onReady);
|
|
2238
|
+
if (!atlas) return null;
|
|
2239
|
+
const rect = subRectFor(atlas, asset.sprite);
|
|
2240
|
+
if (!rect) return null;
|
|
2241
|
+
return { img, rect, aspect: rect.sw / rect.sh };
|
|
1958
2242
|
}
|
|
1959
|
-
|
|
2243
|
+
const natW = img.naturalWidth || 1;
|
|
2244
|
+
const natH = img.naturalHeight || 1;
|
|
2245
|
+
return { img, rect: null, aspect: natW / natH };
|
|
1960
2246
|
}
|
|
1961
|
-
function
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
const rb = fetched && isRigBundle(fetched) ? fetched : void 0;
|
|
1965
|
-
const mesh = node.mesh ?? rb?.mesh ?? (!rb ? fetched : void 0);
|
|
1966
|
-
if (!mesh) return void 0;
|
|
1967
|
-
const bones = node.bones.length > 0 ? node.bones : rb?.bones ?? [];
|
|
1968
|
-
if (bones.length === 0) return void 0;
|
|
1969
|
-
const clip = node.clip ?? (node.clipName ? rb?.clips?.[node.clipName] : void 0);
|
|
1970
|
-
return { mesh, bones, clip };
|
|
2247
|
+
function blit(ctx, src, dx, dy, dw, dh) {
|
|
2248
|
+
if (src.rect) ctx.drawImage(src.img, src.rect.sx, src.rect.sy, src.rect.sw, src.rect.sh, dx, dy, dw, dh);
|
|
2249
|
+
else ctx.drawImage(src.img, dx, dy, dw, dh);
|
|
1971
2250
|
}
|
|
1972
|
-
var
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
if (v.u < 0 || v.u > 1 || v.v < 0 || v.v > 1) {
|
|
1979
|
-
pixel = true;
|
|
1980
|
-
break;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
pixelUVByMesh.set(mesh, pixel);
|
|
1984
|
-
return pixel;
|
|
2251
|
+
var log2 = createLogger("almadar:ui:canvas-theme");
|
|
2252
|
+
var toneVar = (tone) => `--color-${DIAGRAM_TONE_COLOR_KEYS[tone]}`;
|
|
2253
|
+
var SERIES_TONES = ["series-1", "series-2", "series-3", "series-4", "series-5", "series-6", "series-7", "series-8"];
|
|
2254
|
+
var TONE_SET = new Set(DIAGRAM_TONES);
|
|
2255
|
+
function isDiagramTone(value) {
|
|
2256
|
+
return TONE_SET.has(value);
|
|
1985
2257
|
}
|
|
1986
|
-
function
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
const
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2258
|
+
function colorCss(c) {
|
|
2259
|
+
return c.a >= 1 ? c.css : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;
|
|
2260
|
+
}
|
|
2261
|
+
function oneOf(value, allowed, fallback, name) {
|
|
2262
|
+
const hit = allowed.find((a) => a === value);
|
|
2263
|
+
if (hit !== void 0) return hit;
|
|
2264
|
+
if (value !== "") log2.warn("invalid-theme-keyword", { var: name, value, allowed });
|
|
2265
|
+
return fallback;
|
|
2266
|
+
}
|
|
2267
|
+
var PLAIN_LENGTH = /^(-?[\d.]+)(px)?$/;
|
|
2268
|
+
var ScopeReader = class {
|
|
2269
|
+
constructor(scope) {
|
|
2270
|
+
__publicField(this, "scope", scope);
|
|
2271
|
+
__publicField(this, "style");
|
|
2272
|
+
__publicField(this, "probe");
|
|
2273
|
+
this.style = getComputedStyle(scope);
|
|
2274
|
+
const doc = scope.ownerDocument;
|
|
2275
|
+
this.probe = doc.createElement("span");
|
|
2276
|
+
this.probe.style.position = "absolute";
|
|
2277
|
+
this.probe.style.visibility = "hidden";
|
|
2278
|
+
this.probe.style.pointerEvents = "none";
|
|
2279
|
+
scope.appendChild(this.probe);
|
|
2280
|
+
}
|
|
2281
|
+
dispose() {
|
|
2282
|
+
this.probe.remove();
|
|
2283
|
+
}
|
|
2284
|
+
raw(name) {
|
|
2285
|
+
return this.style.getPropertyValue(name).trim();
|
|
2286
|
+
}
|
|
2287
|
+
/** A length var in px: a plain value parses directly, a derived one (`calc`, `var`) through the probe. */
|
|
2288
|
+
length(name, fallback) {
|
|
2289
|
+
const raw = this.raw(name);
|
|
2290
|
+
const plain = PLAIN_LENGTH.exec(raw);
|
|
2291
|
+
if (plain) return Number(plain[1]);
|
|
2292
|
+
if (raw === "") return fallback;
|
|
2293
|
+
this.probe.style.width = `var(${name})`;
|
|
2294
|
+
const computed = PLAIN_LENGTH.exec(getComputedStyle(this.probe).width.trim());
|
|
2295
|
+
this.probe.style.width = "";
|
|
2296
|
+
if (computed) return Number(computed[1]);
|
|
2297
|
+
log2.warn("unresolvable-theme-length", { var: name, raw });
|
|
2298
|
+
return fallback;
|
|
2299
|
+
}
|
|
2300
|
+
number(name, fallback) {
|
|
2301
|
+
const raw = this.raw(name);
|
|
2302
|
+
if (raw === "") return fallback;
|
|
2303
|
+
const n = Number(raw);
|
|
2304
|
+
if (Number.isFinite(n)) return n;
|
|
2305
|
+
log2.warn("unresolvable-theme-number", { var: name, raw });
|
|
2306
|
+
return fallback;
|
|
2307
|
+
}
|
|
2308
|
+
/** A duration var in ms (`250ms`, `0.25s`, or a `var()` alias resolved through the probe). */
|
|
2309
|
+
durationMs(name, fallback) {
|
|
2310
|
+
const parse = (v) => {
|
|
2311
|
+
const m = /^([\d.]+)(ms|s)$/.exec(v.trim());
|
|
2312
|
+
return m ? Number(m[1]) * (m[2] === "s" ? 1e3 : 1) : null;
|
|
2313
|
+
};
|
|
2314
|
+
const raw = this.raw(name);
|
|
2315
|
+
const direct = parse(raw);
|
|
2316
|
+
if (direct !== null) return direct;
|
|
2317
|
+
if (raw === "") return fallback;
|
|
2318
|
+
this.probe.style.transitionDuration = `var(${name})`;
|
|
2319
|
+
const computed = parse(getComputedStyle(this.probe).transitionDuration.split(",")[0] ?? "");
|
|
2320
|
+
this.probe.style.transitionDuration = "";
|
|
2321
|
+
return computed ?? fallback;
|
|
2028
2322
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
if (!isValidScenePos(node.position)) return;
|
|
2033
|
-
const rig = resolveRig(node, dctx);
|
|
2034
|
-
if (!rig) {
|
|
2035
|
-
if (node.meshUrl && skinMeshFailed(node.meshUrl)) warnMissingOnce("mesh-failed", node);
|
|
2036
|
-
else if (!node.mesh && !node.meshUrl) warnMissingOnce("mesh-missing", node);
|
|
2037
|
-
return;
|
|
2323
|
+
color(name) {
|
|
2324
|
+
const raw = this.raw(name);
|
|
2325
|
+
return parseComputedColor(raw) ?? resolveThemeColor(`var(${name})`, this.scope);
|
|
2038
2326
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
if (
|
|
2042
|
-
|
|
2327
|
+
shadow(name) {
|
|
2328
|
+
const raw = this.raw(name);
|
|
2329
|
+
if (raw === "" || raw === "none") return null;
|
|
2330
|
+
this.probe.style.boxShadow = `var(${name})`;
|
|
2331
|
+
const computed = getComputedStyle(this.probe).boxShadow.trim() || raw;
|
|
2332
|
+
this.probe.style.boxShadow = "";
|
|
2333
|
+
const colorMatch = /(rgba?\([^)]*\)|#[0-9a-f]{3,8}|oklch\([^)]*\))/i.exec(computed);
|
|
2334
|
+
const lengths = computed.replace(colorMatch?.[0] ?? "", "").trim().split(/\s+/).map((t) => PLAIN_LENGTH.exec(t)).filter((m) => m !== null).map((m) => Number(m[1]));
|
|
2335
|
+
if (lengths.length < 2) return null;
|
|
2336
|
+
const color = colorMatch ? parseComputedColor(colorMatch[0]) : null;
|
|
2337
|
+
return { offsetX: lengths[0], offsetY: lengths[1], blur: lengths[2] ?? 0, color: color ? colorCss(color) : "rgba(0, 0, 0, 0.25)" };
|
|
2043
2338
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2339
|
+
family(name, fallback) {
|
|
2340
|
+
return this.raw(name) || fallback;
|
|
2046
2341
|
}
|
|
2047
|
-
paintMeshPass(painter, node, rig.bones, rig.mesh, tex, dctx, resolveSkinPose(node, rig.clip), node.opacity ?? 1);
|
|
2048
2342
|
};
|
|
2049
|
-
function
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
return
|
|
2343
|
+
function dashList(raw, fallback) {
|
|
2344
|
+
if (raw === "") return fallback;
|
|
2345
|
+
const parts = raw.split(/[\s,]+/).map(Number);
|
|
2346
|
+
return parts.every((n) => Number.isFinite(n) && n >= 0) ? parts : fallback;
|
|
2053
2347
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2348
|
+
function prefersReducedMotion(scope) {
|
|
2349
|
+
const view = scope.ownerDocument.defaultView;
|
|
2350
|
+
return typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2351
|
+
}
|
|
2352
|
+
function resolveCanvasTheme(scope) {
|
|
2353
|
+
const r = new ScopeReader(scope);
|
|
2354
|
+
try {
|
|
2355
|
+
const tones = {};
|
|
2356
|
+
for (const tone of DIAGRAM_TONES) {
|
|
2357
|
+
const c = r.color(toneVar(tone));
|
|
2358
|
+
tones[tone] = c ? colorCss(c) : "currentColor";
|
|
2064
2359
|
}
|
|
2360
|
+
const ground = r.color("--surface-diagram");
|
|
2361
|
+
const text = {};
|
|
2362
|
+
const TEXT_FALLBACK = { xs: 12, sm: 14, base: 16, lg: 18, xl: 20, "2xl": 24 };
|
|
2363
|
+
for (const size of DIAGRAM_TEXT_SIZES) text[size] = r.length(`--text-${size}`, TEXT_FALLBACK[size]);
|
|
2364
|
+
const body = r.family("--font-family-body", r.family("--font-family", "system-ui, sans-serif"));
|
|
2365
|
+
return {
|
|
2366
|
+
ground: ground ? colorCss(ground) : "transparent",
|
|
2367
|
+
tones,
|
|
2368
|
+
series: SERIES_TONES.map((t) => tones[t]),
|
|
2369
|
+
strokes: {
|
|
2370
|
+
thin: r.length("--diagram-stroke-thin", 1),
|
|
2371
|
+
normal: r.length("--diagram-stroke-normal", 1.5),
|
|
2372
|
+
bold: r.length("--diagram-stroke-bold", 3)
|
|
2373
|
+
},
|
|
2374
|
+
lineCap: oneOf(r.raw("--diagram-line-cap"), ["butt", "round", "square"], "round", "--diagram-line-cap"),
|
|
2375
|
+
lineJoin: oneOf(r.raw("--diagram-line-join"), ["miter", "round", "bevel"], "round", "--diagram-line-join"),
|
|
2376
|
+
dash: dashList(r.raw("--diagram-dash"), [6, 4]),
|
|
2377
|
+
dot: dashList(r.raw("--diagram-dot"), [1, 4]),
|
|
2378
|
+
fillStyle: oneOf(r.raw("--diagram-fill-style"), ["solid", "tint", "outline", "hatch"], "tint", "--diagram-fill-style"),
|
|
2379
|
+
fillOpacity: r.number("--diagram-fill-opacity", 0.18),
|
|
2380
|
+
roughness: r.number("--diagram-roughness", 0),
|
|
2381
|
+
glow: r.number("--diagram-glow", 0),
|
|
2382
|
+
shadow: r.shadow("--diagram-shadow"),
|
|
2383
|
+
marker: oneOf(r.raw("--diagram-marker"), ["triangle", "open", "line", "dot"], "triangle", "--diagram-marker"),
|
|
2384
|
+
faces: {
|
|
2385
|
+
body,
|
|
2386
|
+
display: r.family("--font-family-display", body),
|
|
2387
|
+
mono: r.family("--font-family-mono", "ui-monospace, monospace")
|
|
2388
|
+
},
|
|
2389
|
+
label: {
|
|
2390
|
+
font: oneOf(r.raw("--diagram-label-font"), ["body", "display", "mono"], "body", "--diagram-label-font"),
|
|
2391
|
+
size: oneOf(r.raw("--diagram-label-size"), DIAGRAM_TEXT_SIZES, "sm", "--diagram-label-size"),
|
|
2392
|
+
weight: r.raw("--diagram-label-weight") || "500",
|
|
2393
|
+
uppercase: oneOf(r.raw("--diagram-label-case"), ["none", "uppercase"], "none", "--diagram-label-case") === "uppercase"
|
|
2394
|
+
},
|
|
2395
|
+
text,
|
|
2396
|
+
corner: r.length("--diagram-corner", 4),
|
|
2397
|
+
motion: {
|
|
2398
|
+
enabled: r.raw("--motion-enable") !== "off" && !prefersReducedMotion(scope),
|
|
2399
|
+
fastMs: r.durationMs("--duration-fast", 150),
|
|
2400
|
+
normalMs: r.durationMs("--duration-normal", 250),
|
|
2401
|
+
slowMs: r.durationMs("--duration-slow", 400),
|
|
2402
|
+
easing: r.raw("--easing-standard") || "ease"
|
|
2403
|
+
},
|
|
2404
|
+
scene: {
|
|
2405
|
+
roughness: r.number("--material-roughness", 0.6),
|
|
2406
|
+
metalness: r.number("--material-metalness", 0),
|
|
2407
|
+
flat: r.number("--material-flat", 0) >= 1,
|
|
2408
|
+
outline: r.number("--material-outline", 0),
|
|
2409
|
+
ambient: r.number("--light-ambient", 0.55),
|
|
2410
|
+
key: r.number("--light-key", 1),
|
|
2411
|
+
keyColor: (() => {
|
|
2412
|
+
const c = r.color("--light-key-color");
|
|
2413
|
+
return c ? colorCss(c) : "#ffffff";
|
|
2414
|
+
})(),
|
|
2415
|
+
fog: r.number("--scene-fog", 0)
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
} finally {
|
|
2419
|
+
r.dispose();
|
|
2065
2420
|
}
|
|
2066
|
-
return { min, max };
|
|
2067
|
-
}
|
|
2068
|
-
function clampSegments(segments) {
|
|
2069
|
-
const s = segments ?? MESH_SEGMENTS_DEFAULT;
|
|
2070
|
-
return Math.min(64, Math.max(3, Math.round(s)));
|
|
2071
2421
|
}
|
|
2072
|
-
function
|
|
2073
|
-
|
|
2422
|
+
function withSeries(theme, series, scope) {
|
|
2423
|
+
if (!theme || !series || series.length === 0 || !scope) return theme;
|
|
2424
|
+
const colors = series.map((c, i) => markColor(theme, c, scope, SERIES_TONES[i % SERIES_TONES.length]));
|
|
2425
|
+
const tones = { ...theme.tones };
|
|
2426
|
+
SERIES_TONES.forEach((tone, i) => {
|
|
2427
|
+
tones[tone] = colors[i % colors.length];
|
|
2428
|
+
});
|
|
2429
|
+
return { ...theme, tones, series: SERIES_TONES.map((t) => tones[t]) };
|
|
2074
2430
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
"
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
"rotateY",
|
|
2082
|
-
"rotateZ",
|
|
2083
|
-
"scale",
|
|
2084
|
-
"opacity",
|
|
2085
|
-
"emissiveIntensity"
|
|
2086
|
-
];
|
|
2087
|
-
function applyMeshAnimation(node, timeMs) {
|
|
2088
|
-
const anim = node.animation;
|
|
2089
|
-
if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return null;
|
|
2090
|
-
const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
|
|
2091
|
-
const cycle = timeMs / anim.durationMs;
|
|
2092
|
-
const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
|
|
2093
|
-
const trackValue = (key) => {
|
|
2094
|
-
const defined = frames.filter((f) => f[key] !== void 0);
|
|
2095
|
-
if (defined.length === 0) return void 0;
|
|
2096
|
-
let prev;
|
|
2097
|
-
let next;
|
|
2098
|
-
for (const f of defined) {
|
|
2099
|
-
if (f.at <= t) prev = f;
|
|
2100
|
-
else if (!next) next = f;
|
|
2101
|
-
}
|
|
2102
|
-
if (!prev) return defined[0][key];
|
|
2103
|
-
if (!next) return prev[key];
|
|
2104
|
-
const span = next.at - prev.at;
|
|
2105
|
-
const k = span > 0 ? (t - prev.at) / span : 1;
|
|
2106
|
-
const a = prev[key];
|
|
2107
|
-
const b = next[key];
|
|
2108
|
-
if (typeof a === "number" && typeof b === "number") return lerp2(a, b, k);
|
|
2109
|
-
return a;
|
|
2110
|
-
};
|
|
2111
|
-
const num = {};
|
|
2112
|
-
for (const key of NUMERIC_TRACKS2) {
|
|
2113
|
-
const v = trackValue(key);
|
|
2114
|
-
if (v !== void 0) num[key] = v;
|
|
2431
|
+
function markColor(theme, value, scope, fallback) {
|
|
2432
|
+
if (value === void 0 || value === "") return theme.tones[fallback];
|
|
2433
|
+
if (isDiagramTone(value)) return theme.tones[value];
|
|
2434
|
+
if (value.startsWith("var(") || value.startsWith("color-mix(")) {
|
|
2435
|
+
const resolved = resolveThemeColor(value, scope);
|
|
2436
|
+
return resolved ? colorCss(resolved) : theme.tones[fallback];
|
|
2115
2437
|
}
|
|
2116
|
-
return
|
|
2117
|
-
offset: [num.offsetX ?? 0, num.offsetY ?? 0, num.offsetZ ?? 0],
|
|
2118
|
-
rotate: [num.rotateX ?? 0, num.rotateY ?? 0, num.rotateZ ?? 0],
|
|
2119
|
-
scale: num.scale ?? 1,
|
|
2120
|
-
opacity: num.opacity,
|
|
2121
|
-
emissiveIntensity: num.emissiveIntensity,
|
|
2122
|
-
color: trackValue("color"),
|
|
2123
|
-
emissive: trackValue("emissive")
|
|
2124
|
-
};
|
|
2438
|
+
return value;
|
|
2125
2439
|
}
|
|
2126
|
-
function
|
|
2127
|
-
|
|
2128
|
-
if (register) register({ ...props, type: "draw-mesh" });
|
|
2129
|
-
return null;
|
|
2440
|
+
function markStrokeWidth(theme, literal, role, fallback = "normal") {
|
|
2441
|
+
return literal ?? theme.strokes[role ?? fallback];
|
|
2130
2442
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
if (
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2443
|
+
function labelFont(theme, roles, literalPx, literalFamily) {
|
|
2444
|
+
const px = literalPx ?? theme.text[roles.textSize ?? theme.label.size];
|
|
2445
|
+
const family = literalFamily ?? theme.faces[roles.font ?? theme.label.font];
|
|
2446
|
+
return { font: `${theme.label.weight} ${px}px ${family}`, px };
|
|
2447
|
+
}
|
|
2448
|
+
function labelText(theme, text, textCase = "theme") {
|
|
2449
|
+
return theme.label.uppercase && textCase === "theme" ? text.toUpperCase() : text;
|
|
2450
|
+
}
|
|
2451
|
+
function fillLabel(ctx, theme, text, x, y) {
|
|
2452
|
+
if (!text) return;
|
|
2453
|
+
ctx.save();
|
|
2454
|
+
ctx.strokeStyle = theme.ground;
|
|
2455
|
+
ctx.lineWidth = Math.max(2, theme.text.xs * 0.3);
|
|
2456
|
+
ctx.lineJoin = "round";
|
|
2457
|
+
ctx.strokeText(text, x, y);
|
|
2458
|
+
ctx.restore();
|
|
2459
|
+
ctx.fillText(text, x, y);
|
|
2460
|
+
}
|
|
2461
|
+
function outlineStroke(stroke, fill, style, fillTone) {
|
|
2462
|
+
return outlineCarriesFill(fill, style, fillTone) && fill !== void 0 ? fill : stroke;
|
|
2463
|
+
}
|
|
2464
|
+
function outlineCarriesFill(fill, style, fillTone) {
|
|
2465
|
+
return style === "outline" && fill !== void 0 && fillTone !== "fill";
|
|
2466
|
+
}
|
|
2467
|
+
function applyLineCharacter(ctx, theme, strokeColor) {
|
|
2468
|
+
ctx.lineCap = theme.lineCap;
|
|
2469
|
+
ctx.lineJoin = theme.lineJoin;
|
|
2470
|
+
if (theme.glow > 0) {
|
|
2471
|
+
ctx.shadowColor = strokeColor;
|
|
2472
|
+
ctx.shadowBlur = theme.glow;
|
|
2473
|
+
ctx.shadowOffsetX = 0;
|
|
2474
|
+
ctx.shadowOffsetY = 0;
|
|
2147
2475
|
}
|
|
2148
|
-
const base = view.position ?? { x: view.x, y: view.z ?? 0, z: view.y };
|
|
2149
|
-
if (!Number.isFinite(base.x) || !Number.isFinite(base.y)) return void 0;
|
|
2150
|
-
return {
|
|
2151
|
-
x: base.x + (view.vx ?? 0) * ageSec,
|
|
2152
|
-
y: base.y + (view.vz ?? 0) * ageSec,
|
|
2153
|
-
...base.z !== void 0 || view.vy !== void 0 || g !== 0 ? { z: (base.z ?? 0) + (view.vy ?? 0) * ageSec - fall } : {}
|
|
2154
|
-
};
|
|
2155
2476
|
}
|
|
2156
|
-
function
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
const r = size * (0.06 + 0.06 * fxHash01(view.id, i * 3 + 2));
|
|
2161
|
-
const color = view.color ?? DEFAULT_SPARK_COLOR;
|
|
2162
|
-
return {
|
|
2163
|
-
type: "draw-shape",
|
|
2164
|
-
shape: "ellipse",
|
|
2165
|
-
position: { ...pos, x: pos.x + Math.cos(angle) * dist, y: pos.y + Math.sin(angle) * dist },
|
|
2166
|
-
anchor: "center",
|
|
2167
|
-
radiusX: r,
|
|
2168
|
-
fill: color,
|
|
2169
|
-
blendMode: "lighter",
|
|
2170
|
-
opacity: fade,
|
|
2171
|
-
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2172
|
-
};
|
|
2477
|
+
function markDash(theme, dash) {
|
|
2478
|
+
if (dash === "dashed") return theme.dash;
|
|
2479
|
+
if (dash === "dotted") return theme.dot;
|
|
2480
|
+
return [];
|
|
2173
2481
|
}
|
|
2174
|
-
function
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2191
|
-
}
|
|
2192
|
-
];
|
|
2193
|
-
}
|
|
2194
|
-
case "puff": {
|
|
2195
|
-
const count = view.count ?? 3;
|
|
2196
|
-
return Array.from({ length: count }, (_, i) => {
|
|
2197
|
-
const angle = fxHash01(view.id, i * 5) * Math.PI * 2;
|
|
2198
|
-
const drift = size * 0.3 * fxHash01(view.id, i * 5 + 1) * easeOut(progress);
|
|
2199
|
-
return {
|
|
2200
|
-
type: "draw-shape",
|
|
2201
|
-
shape: "ellipse",
|
|
2202
|
-
position: { ...pos, x: pos.x + Math.cos(angle) * drift, y: pos.y + Math.sin(angle) * drift },
|
|
2203
|
-
anchor: "center",
|
|
2204
|
-
radiusX: size * (0.15 + 0.35 * progress) * (0.7 + 0.6 * fxHash01(view.id, i * 5 + 2)),
|
|
2205
|
-
fill: i === 0 && view.color2 ? view.color2 : color,
|
|
2206
|
-
opacity: fade * 0.6,
|
|
2207
|
-
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2208
|
-
};
|
|
2209
|
-
});
|
|
2210
|
-
}
|
|
2211
|
-
case "streak": {
|
|
2212
|
-
const count = view.count ?? 5;
|
|
2213
|
-
return Array.from({ length: count }, (_, i) => {
|
|
2214
|
-
const angle = fxHash01(view.id, i * 3) * Math.PI * 2;
|
|
2215
|
-
const inner = size * (0.2 + 0.8 * easeOut(progress)) * (0.6 + 0.4 * fxHash01(view.id, i * 3 + 1));
|
|
2216
|
-
const len = size * 0.35;
|
|
2217
|
-
return {
|
|
2218
|
-
type: "draw-shape",
|
|
2219
|
-
shape: "ellipse",
|
|
2220
|
-
position: pos,
|
|
2221
|
-
anchor: "center",
|
|
2222
|
-
offsetX: inner + len / 2,
|
|
2223
|
-
offsetY: 0,
|
|
2224
|
-
radiusX: len / 2,
|
|
2225
|
-
radiusY: size * 0.04,
|
|
2226
|
-
rotate: angle,
|
|
2227
|
-
fill: color,
|
|
2228
|
-
blendMode: "lighter",
|
|
2229
|
-
opacity: fade,
|
|
2230
|
-
...view.glow ? { shadow: { color, blur: view.glow } } : {}
|
|
2231
|
-
};
|
|
2232
|
-
});
|
|
2482
|
+
function wobble(x, y, k) {
|
|
2483
|
+
const s = Math.sin(x * 12.9898 + y * 78.233 + k * 37.719) * 43758.5453;
|
|
2484
|
+
return (s - Math.floor(s)) * 2 - 1;
|
|
2485
|
+
}
|
|
2486
|
+
function tracePolyline(ctx, theme, points, closed) {
|
|
2487
|
+
ctx.beginPath();
|
|
2488
|
+
if (points.length === 0) return;
|
|
2489
|
+
const amp = theme.roughness;
|
|
2490
|
+
const pts = closed ? [...points, points[0]] : [...points];
|
|
2491
|
+
ctx.moveTo(pts[0].x + amp * wobble(pts[0].x, pts[0].y, 0), pts[0].y + amp * wobble(pts[0].y, pts[0].x, 1));
|
|
2492
|
+
for (let i = 1; i < pts.length; i++) {
|
|
2493
|
+
const a = pts[i - 1];
|
|
2494
|
+
const b = pts[i];
|
|
2495
|
+
if (amp === 0) {
|
|
2496
|
+
ctx.lineTo(b.x, b.y);
|
|
2497
|
+
continue;
|
|
2233
2498
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2499
|
+
const steps = Math.max(1, Math.round(Math.hypot(b.x - a.x, b.y - a.y) / 24));
|
|
2500
|
+
for (let s = 1; s <= steps; s++) {
|
|
2501
|
+
const t = s / steps;
|
|
2502
|
+
const x = a.x + (b.x - a.x) * t;
|
|
2503
|
+
const y = a.y + (b.y - a.y) * t;
|
|
2504
|
+
ctx.lineTo(x + amp * wobble(x, y, 2), y + amp * wobble(y, x, 3));
|
|
2237
2505
|
}
|
|
2238
2506
|
}
|
|
2507
|
+
if (closed) ctx.closePath();
|
|
2239
2508
|
}
|
|
2240
|
-
function
|
|
2241
|
-
|
|
2242
|
-
const tickMs = node.tickMs ?? DEFAULT_TICK_MS;
|
|
2243
|
-
const { ageMs, progress, fade } = fxLifecycle(view, epochNowMs, tickMs);
|
|
2244
|
-
if (progress >= 1) return [];
|
|
2245
|
-
const pos = fxPosition(view, dim, ageMs / 1e3);
|
|
2246
|
-
if (!pos) return [];
|
|
2509
|
+
function arcPoints(cx, cy, rx, ry, startRad = 0, endRad = Math.PI * 2) {
|
|
2510
|
+
const steps = Math.max(12, Math.round(Math.abs(endRad - startRad) * Math.max(rx, ry) / 8));
|
|
2247
2511
|
const out = [];
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
out.push({
|
|
2252
|
-
type: "draw-group",
|
|
2253
|
-
position: pos,
|
|
2254
|
-
opacity: fade,
|
|
2255
|
-
...view.size !== void 0 ? { scale: view.size } : {},
|
|
2256
|
-
items: artItems
|
|
2257
|
-
});
|
|
2258
|
-
} else if (sprite?.url) {
|
|
2259
|
-
const spriteSize = view.size ?? 0.6;
|
|
2260
|
-
out.push({
|
|
2261
|
-
type: "draw-sprite",
|
|
2262
|
-
position: pos,
|
|
2263
|
-
asset: sprite,
|
|
2264
|
-
anchor: "center",
|
|
2265
|
-
width: spriteSize,
|
|
2266
|
-
height: spriteSize,
|
|
2267
|
-
opacity: fade,
|
|
2268
|
-
animation: view.animation ?? sprite.animations?.[0],
|
|
2269
|
-
// One-shot fx rows (burst) play from the item's birth, not wall-time.
|
|
2270
|
-
clockMs: ageMs
|
|
2271
|
-
});
|
|
2272
|
-
} else {
|
|
2273
|
-
out.push(...proceduralShapes(view, pos, fade, progress));
|
|
2274
|
-
}
|
|
2275
|
-
if (view.message) {
|
|
2276
|
-
const pxSize = projector && view.size !== void 0 ? Math.max(10, Math.round(view.size * projector.tileWidth)) : void 0;
|
|
2277
|
-
const family = fontFamily ?? "system-ui, sans-serif";
|
|
2278
|
-
const text = {
|
|
2279
|
-
type: "draw-text",
|
|
2280
|
-
text: view.message,
|
|
2281
|
-
position: pos,
|
|
2282
|
-
offsetY: -(0.15 + 0.55 * progress),
|
|
2283
|
-
color: view.color ?? node.textColor ?? DEFAULT_TEXT_COLOR,
|
|
2284
|
-
opacity: fade,
|
|
2285
|
-
font: `bold ${pxSize ?? 14}px ${family}`
|
|
2286
|
-
};
|
|
2287
|
-
out.push(text);
|
|
2512
|
+
for (let i = 0; i <= steps; i++) {
|
|
2513
|
+
const t = startRad + (endRad - startRad) * i / steps;
|
|
2514
|
+
out.push({ x: cx + rx * Math.cos(t), y: cy + ry * Math.sin(t) });
|
|
2288
2515
|
}
|
|
2289
2516
|
return out;
|
|
2290
2517
|
}
|
|
2291
|
-
function
|
|
2292
|
-
if (
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2518
|
+
function fillMark(ctx, theme, color, style = theme.fillStyle) {
|
|
2519
|
+
if (style === "outline") return;
|
|
2520
|
+
ctx.save();
|
|
2521
|
+
ctx.shadowBlur = 0;
|
|
2522
|
+
ctx.shadowColor = "transparent";
|
|
2523
|
+
if (theme.shadow) {
|
|
2524
|
+
ctx.shadowColor = theme.shadow.color;
|
|
2525
|
+
ctx.shadowOffsetX = theme.shadow.offsetX;
|
|
2526
|
+
ctx.shadowOffsetY = theme.shadow.offsetY;
|
|
2527
|
+
ctx.shadowBlur = theme.shadow.blur;
|
|
2297
2528
|
}
|
|
2298
|
-
|
|
2529
|
+
if (style === "hatch") {
|
|
2530
|
+
ctx.clip();
|
|
2531
|
+
const span = ctx.canvas.width + ctx.canvas.height;
|
|
2532
|
+
ctx.strokeStyle = color;
|
|
2533
|
+
ctx.lineWidth = theme.strokes.thin;
|
|
2534
|
+
ctx.setLineDash([]);
|
|
2535
|
+
ctx.beginPath();
|
|
2536
|
+
for (let d = -span; d < span; d += 6) {
|
|
2537
|
+
ctx.moveTo(d, 0);
|
|
2538
|
+
ctx.lineTo(d + span, span);
|
|
2539
|
+
}
|
|
2540
|
+
ctx.stroke();
|
|
2541
|
+
} else {
|
|
2542
|
+
ctx.globalAlpha *= style === "tint" ? theme.fillOpacity : 1;
|
|
2543
|
+
ctx.fillStyle = color;
|
|
2544
|
+
ctx.fill();
|
|
2545
|
+
}
|
|
2546
|
+
ctx.restore();
|
|
2299
2547
|
}
|
|
2300
|
-
function
|
|
2301
|
-
|
|
2548
|
+
function drawMarker(ctx, theme, x1, y1, x2, y2, color, width) {
|
|
2549
|
+
const size = Math.max(8, width * 4);
|
|
2550
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
2551
|
+
const left = { x: x2 - size * Math.cos(angle - Math.PI / 6), y: y2 - size * Math.sin(angle - Math.PI / 6) };
|
|
2552
|
+
const right = { x: x2 - size * Math.cos(angle + Math.PI / 6), y: y2 - size * Math.sin(angle + Math.PI / 6) };
|
|
2553
|
+
ctx.save();
|
|
2554
|
+
ctx.strokeStyle = color;
|
|
2555
|
+
ctx.fillStyle = color;
|
|
2556
|
+
ctx.lineWidth = width;
|
|
2557
|
+
ctx.setLineDash([]);
|
|
2558
|
+
switch (theme.marker) {
|
|
2559
|
+
case "triangle": {
|
|
2560
|
+
ctx.beginPath();
|
|
2561
|
+
ctx.moveTo(x2, y2);
|
|
2562
|
+
ctx.lineTo(left.x, left.y);
|
|
2563
|
+
ctx.lineTo(right.x, right.y);
|
|
2564
|
+
ctx.closePath();
|
|
2565
|
+
ctx.fill();
|
|
2566
|
+
break;
|
|
2567
|
+
}
|
|
2568
|
+
case "open":
|
|
2569
|
+
case "line": {
|
|
2570
|
+
ctx.beginPath();
|
|
2571
|
+
ctx.moveTo(left.x, left.y);
|
|
2572
|
+
ctx.lineTo(x2, y2);
|
|
2573
|
+
ctx.lineTo(right.x, right.y);
|
|
2574
|
+
if (theme.marker === "open") ctx.stroke();
|
|
2575
|
+
else {
|
|
2576
|
+
ctx.moveTo(x2 - size * 0.5 * Math.cos(angle + Math.PI / 2), y2 - size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
2577
|
+
ctx.lineTo(x2 + size * 0.5 * Math.cos(angle + Math.PI / 2), y2 + size * 0.5 * Math.sin(angle + Math.PI / 2));
|
|
2578
|
+
ctx.stroke();
|
|
2579
|
+
}
|
|
2580
|
+
break;
|
|
2581
|
+
}
|
|
2582
|
+
case "dot": {
|
|
2583
|
+
ctx.beginPath();
|
|
2584
|
+
ctx.arc(x2, y2, Math.max(2.5, width * 1.5), 0, Math.PI * 2);
|
|
2585
|
+
ctx.fill();
|
|
2586
|
+
break;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
ctx.restore();
|
|
2590
|
+
}
|
|
2591
|
+
function useCanvasTheme(scopeRef) {
|
|
2592
|
+
const { appliedTheme } = useTheme();
|
|
2593
|
+
const scopeKey = JSON.stringify(useThemeScope());
|
|
2594
|
+
const [state, setState] = useState({ theme: null, version: 0 });
|
|
2595
|
+
useLayoutEffect(() => {
|
|
2596
|
+
const scope = scopeRef.current;
|
|
2597
|
+
if (!scope) return;
|
|
2598
|
+
const theme = resolveCanvasTheme(scope);
|
|
2599
|
+
setState((prev) => ({ theme, version: prev.version + 1 }));
|
|
2600
|
+
}, [appliedTheme, scopeKey, scopeRef]);
|
|
2601
|
+
return state;
|
|
2302
2602
|
}
|
|
2303
2603
|
|
|
2304
2604
|
// lib/drawable/themeDrawables.ts
|
|
@@ -2499,4 +2799,4 @@ function rotatePoint(p, center, radians) {
|
|
|
2499
2799
|
return { x: center.x + dx * cos - dy * sin, y: center.y + dx * sin + dy * cos };
|
|
2500
2800
|
}
|
|
2501
2801
|
|
|
2502
|
-
export { AVL_FONT, AVL_INK, AVL_STROKE, Box, CONFETTI_BURST_KEYFRAMES, CONNECTION_COLORS, DrawFxLayer, DrawGroup, DrawMesh, DrawShape, DrawSkinnedMesh, DrawableRegistryContext, EFFECT_CATEGORY_COLORS, HStack, OPERATOR_CATEGORY_COLORS, SPRITE_FALLBACK_FILL, SPRITE_FALLBACK_STROKE, STATE_COLORS, STRETCHED_ABOVE, STRETCHED_CONTROL, Stack, THEME_SERIES, VStack, applyLineCharacter, applyMeshAnimation, applySceneMaterials, applyShapeAnimation, arcPoints, asEffectOperator, atlasFailed, atlasUrlOf, avlBlend, avlSeries, avlTint, blit, buildHitIndex, clampSegments, collectDrawnItems, createConfettiParticles, drawMarker, effectCategoryOf, effectZoneOf, expandFxLayer, fillLabel, fillMark, fxHash01, getAtlas, getImageStatus, getOrLoadImage, getStateRole, hitTestSprites, isAnimatedGroup, isAnimatedMesh, isAnimatedShape, isAtlasAsset, isSpriteSheetAtlas, isValidScenePos, labelFont, labelText, markColor, markDash, markStrokeWidth, outlineCarriesFill, outlineStroke, paintShape, paintSkinnedMesh, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, polyhedronBounds, pressableProps, resolveAssetSource, resolveDrawableColors, resolveThemeColor, rowOpenControlProps, spriteAssetState, spriteRect, subRectFor, tracePolyline, traitEventWires, traitLevelFromTrait, traitTransitionRenderUi, useCanvasTheme, withPreviewPosition, withSeries };
|
|
2802
|
+
export { AVL_FONT, AVL_INK, AVL_STROKE, Box, CONFETTI_BURST_KEYFRAMES, CONNECTION_COLORS, DrawFxLayer, DrawGroup, DrawMesh, DrawShape, DrawSkinnedMesh, DrawableRegistryContext, EFFECT_CATEGORY_COLORS, HStack, OPERATOR_CATEGORY_COLORS, SPRITE_FALLBACK_FILL, SPRITE_FALLBACK_STROKE, STATE_COLORS, STRETCHED_ABOVE, STRETCHED_CONTROL, Stack, THEME_SERIES, VStack, applyLineCharacter, applyMeshAnimation, applySceneMaterials, applyShapeAnimation, arcPoints, asEffectOperator, atlasFailed, atlasUrlOf, avlBlend, avlSeries, avlTint, blit, buildHitIndex, clampSegments, collectDrawnItems, createConfettiParticles, drawMarker, effectCategoryOf, effectZoneOf, expandFxLayer, fillLabel, fillMark, fxHash01, getAtlas, getImageStatus, getOrLoadImage, getStateRole, hitTestSprites, isAnimatedGroup, isAnimatedMesh, isAnimatedShape, isAtlasAsset, isSpriteSheetAtlas, isValidScenePos, labelFont, labelText, layoutScale, markColor, markDash, markStrokeWidth, motionTiming, outlineCarriesFill, outlineStroke, paintShape, paintSkinnedMesh, parseApplicationLevel, parseOrbitalLevel, parseTraitLevel, parseTransitionLevel, polyhedronBounds, pressableProps, resolveAssetSource, resolveDrawableColors, resolveThemeColor, rowOpenControlProps, spriteAssetState, spriteRect, subRectFor, tracePolyline, traitEventWires, traitLevelFromTrait, traitTransitionRenderUi, useCanvasTheme, withPreviewPosition, withSeries };
|