@almadar/ui 2.16.0 → 2.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +91 -1
- package/dist/avl/index.d.cts +7 -5
- package/dist/avl/index.js +91 -1
- package/dist/components/index.cjs +131 -2411
- package/dist/components/index.css +0 -508
- package/dist/components/index.js +132 -2411
- package/dist/components/molecules/avl/AvlSlotMap.d.ts +5 -4
- package/dist/components/molecules/avl/avl-layout.d.ts +2 -1
- package/dist/components/organisms/component-registry.generated.d.ts +1 -1
- package/dist/components/organisms/game/three/index.cjs +1067 -0
- package/dist/components/organisms/game/three/index.css +192 -0
- package/dist/components/organisms/game/three/index.d.ts +4 -0
- package/dist/components/organisms/game/three/index.js +1068 -5
- package/dist/illustrations/index.cjs +91 -1
- package/dist/illustrations/index.d.cts +5 -4
- package/dist/illustrations/index.js +91 -1
- package/dist/providers/index.cjs +152 -1521
- package/dist/providers/index.css +0 -508
- package/dist/providers/index.js +62 -1430
- package/dist/runtime/index.cjs +145 -1514
- package/dist/runtime/index.css +0 -508
- package/dist/runtime/index.js +63 -1431
- package/package.json +1 -1
|
@@ -19,12 +19,6 @@ require('leaflet/dist/leaflet.css');
|
|
|
19
19
|
var context = require('@almadar/ui/context');
|
|
20
20
|
var patterns = require('@almadar/patterns');
|
|
21
21
|
var reactRouterDom = require('react-router-dom');
|
|
22
|
-
var fiber = require('@react-three/fiber');
|
|
23
|
-
var drei = require('@react-three/drei');
|
|
24
|
-
var THREE = require('three');
|
|
25
|
-
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
26
|
-
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
27
|
-
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
28
22
|
var reactQuery = require('@tanstack/react-query');
|
|
29
23
|
|
|
30
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -56,7 +50,6 @@ var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
|
56
50
|
var SyntaxHighlighter__default = /*#__PURE__*/_interopDefault(SyntaxHighlighter);
|
|
57
51
|
var dark__default = /*#__PURE__*/_interopDefault(dark);
|
|
58
52
|
var L__default = /*#__PURE__*/_interopDefault(L);
|
|
59
|
-
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
60
53
|
|
|
61
54
|
var __defProp = Object.defineProperty;
|
|
62
55
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -1057,7 +1050,7 @@ var Box = React87__namespace.default.forwardRef(
|
|
|
1057
1050
|
position,
|
|
1058
1051
|
className,
|
|
1059
1052
|
children,
|
|
1060
|
-
as:
|
|
1053
|
+
as: Component = "div",
|
|
1061
1054
|
action,
|
|
1062
1055
|
actionPayload,
|
|
1063
1056
|
hoverEvent,
|
|
@@ -1087,7 +1080,7 @@ var Box = React87__namespace.default.forwardRef(
|
|
|
1087
1080
|
onMouseLeave?.(e);
|
|
1088
1081
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
1089
1082
|
const isClickable = action || onClick;
|
|
1090
|
-
const Comp =
|
|
1083
|
+
const Comp = Component;
|
|
1091
1084
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1092
1085
|
Comp,
|
|
1093
1086
|
{
|
|
@@ -1142,10 +1135,10 @@ var Center = ({
|
|
|
1142
1135
|
className,
|
|
1143
1136
|
style,
|
|
1144
1137
|
children,
|
|
1145
|
-
as:
|
|
1138
|
+
as: Component = "div"
|
|
1146
1139
|
}) => {
|
|
1147
1140
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
1148
|
-
const Comp =
|
|
1141
|
+
const Comp = Component;
|
|
1149
1142
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1150
1143
|
Comp,
|
|
1151
1144
|
{
|
|
@@ -1630,7 +1623,7 @@ var Stack = ({
|
|
|
1630
1623
|
className,
|
|
1631
1624
|
style,
|
|
1632
1625
|
children,
|
|
1633
|
-
as:
|
|
1626
|
+
as: Component = "div",
|
|
1634
1627
|
onClick,
|
|
1635
1628
|
onKeyDown,
|
|
1636
1629
|
role,
|
|
@@ -1648,7 +1641,7 @@ var Stack = ({
|
|
|
1648
1641
|
};
|
|
1649
1642
|
const isHorizontal = direction === "horizontal";
|
|
1650
1643
|
const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
|
|
1651
|
-
const Comp =
|
|
1644
|
+
const Comp = Component;
|
|
1652
1645
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1653
1646
|
Comp,
|
|
1654
1647
|
{
|
|
@@ -1819,8 +1812,8 @@ var Typography = ({
|
|
|
1819
1812
|
children
|
|
1820
1813
|
}) => {
|
|
1821
1814
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
1822
|
-
const
|
|
1823
|
-
const Comp =
|
|
1815
|
+
const Component = as || defaultElements[variant];
|
|
1816
|
+
const Comp = Component;
|
|
1824
1817
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1825
1818
|
Comp,
|
|
1826
1819
|
{
|
|
@@ -5659,10 +5652,10 @@ var Container = ({
|
|
|
5659
5652
|
center = true,
|
|
5660
5653
|
className,
|
|
5661
5654
|
children,
|
|
5662
|
-
as:
|
|
5655
|
+
as: Component = "div"
|
|
5663
5656
|
}) => {
|
|
5664
5657
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
5665
|
-
const Comp =
|
|
5658
|
+
const Comp = Component;
|
|
5666
5659
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5667
5660
|
Comp,
|
|
5668
5661
|
{
|
|
@@ -5725,7 +5718,7 @@ var Flex = ({
|
|
|
5725
5718
|
basis,
|
|
5726
5719
|
className,
|
|
5727
5720
|
children,
|
|
5728
|
-
as:
|
|
5721
|
+
as: Component = "div"
|
|
5729
5722
|
}) => {
|
|
5730
5723
|
const flexStyle = {};
|
|
5731
5724
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -5737,7 +5730,7 @@ var Flex = ({
|
|
|
5737
5730
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
5738
5731
|
}
|
|
5739
5732
|
}
|
|
5740
|
-
const Comp =
|
|
5733
|
+
const Comp = Component;
|
|
5741
5734
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5742
5735
|
Comp,
|
|
5743
5736
|
{
|
|
@@ -5994,10 +5987,10 @@ var Grid = ({
|
|
|
5994
5987
|
className,
|
|
5995
5988
|
style,
|
|
5996
5989
|
children,
|
|
5997
|
-
as:
|
|
5990
|
+
as: Component = "div"
|
|
5998
5991
|
}) => {
|
|
5999
5992
|
const mergedStyle = rows ? { gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`, ...style } : style;
|
|
6000
|
-
const Comp =
|
|
5993
|
+
const Comp = Component;
|
|
6001
5994
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6002
5995
|
Comp,
|
|
6003
5996
|
{
|
|
@@ -17593,10 +17586,10 @@ var Section = ({
|
|
|
17593
17586
|
children,
|
|
17594
17587
|
headerClassName,
|
|
17595
17588
|
contentClassName,
|
|
17596
|
-
as:
|
|
17589
|
+
as: Component = "section"
|
|
17597
17590
|
}) => {
|
|
17598
17591
|
const hasHeader = title || description || action;
|
|
17599
|
-
const Comp =
|
|
17592
|
+
const Comp = Component;
|
|
17600
17593
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17601
17594
|
Comp,
|
|
17602
17595
|
{
|
|
@@ -28369,216 +28362,6 @@ function DividerPattern({
|
|
|
28369
28362
|
);
|
|
28370
28363
|
}
|
|
28371
28364
|
DividerPattern.displayName = "DividerPattern";
|
|
28372
|
-
var Camera3D = React87.forwardRef(
|
|
28373
|
-
({
|
|
28374
|
-
mode = "isometric",
|
|
28375
|
-
position = [10, 10, 10],
|
|
28376
|
-
target = [0, 0, 0],
|
|
28377
|
-
zoom = 1,
|
|
28378
|
-
fov = 45,
|
|
28379
|
-
enableOrbit = true,
|
|
28380
|
-
minDistance = 2,
|
|
28381
|
-
maxDistance = 100,
|
|
28382
|
-
onChange
|
|
28383
|
-
}, ref) => {
|
|
28384
|
-
const { camera, set, viewport } = fiber.useThree();
|
|
28385
|
-
const controlsRef = React87.useRef(null);
|
|
28386
|
-
const initialPosition = React87.useRef(new THREE__namespace.Vector3(...position));
|
|
28387
|
-
const initialTarget = React87.useRef(new THREE__namespace.Vector3(...target));
|
|
28388
|
-
React87.useEffect(() => {
|
|
28389
|
-
let newCamera;
|
|
28390
|
-
if (mode === "isometric") {
|
|
28391
|
-
const aspect = viewport.aspect;
|
|
28392
|
-
const size = 10 / zoom;
|
|
28393
|
-
newCamera = new THREE__namespace.OrthographicCamera(
|
|
28394
|
-
-size * aspect,
|
|
28395
|
-
size * aspect,
|
|
28396
|
-
size,
|
|
28397
|
-
-size,
|
|
28398
|
-
0.1,
|
|
28399
|
-
1e3
|
|
28400
|
-
);
|
|
28401
|
-
} else {
|
|
28402
|
-
newCamera = new THREE__namespace.PerspectiveCamera(fov, viewport.aspect, 0.1, 1e3);
|
|
28403
|
-
}
|
|
28404
|
-
newCamera.position.copy(initialPosition.current);
|
|
28405
|
-
newCamera.lookAt(initialTarget.current);
|
|
28406
|
-
set({ camera: newCamera });
|
|
28407
|
-
if (mode === "top-down") {
|
|
28408
|
-
newCamera.position.set(0, 20 / zoom, 0);
|
|
28409
|
-
newCamera.lookAt(0, 0, 0);
|
|
28410
|
-
}
|
|
28411
|
-
return () => {
|
|
28412
|
-
};
|
|
28413
|
-
}, [mode, fov, zoom, viewport.aspect, set]);
|
|
28414
|
-
fiber.useFrame(() => {
|
|
28415
|
-
if (onChange) {
|
|
28416
|
-
onChange(camera);
|
|
28417
|
-
}
|
|
28418
|
-
});
|
|
28419
|
-
React87.useImperativeHandle(ref, () => ({
|
|
28420
|
-
getCamera: () => camera,
|
|
28421
|
-
setPosition: (x, y, z) => {
|
|
28422
|
-
camera.position.set(x, y, z);
|
|
28423
|
-
if (controlsRef.current) {
|
|
28424
|
-
controlsRef.current.update();
|
|
28425
|
-
}
|
|
28426
|
-
},
|
|
28427
|
-
lookAt: (x, y, z) => {
|
|
28428
|
-
camera.lookAt(x, y, z);
|
|
28429
|
-
if (controlsRef.current) {
|
|
28430
|
-
controlsRef.current.target.set(x, y, z);
|
|
28431
|
-
controlsRef.current.update();
|
|
28432
|
-
}
|
|
28433
|
-
},
|
|
28434
|
-
reset: () => {
|
|
28435
|
-
camera.position.copy(initialPosition.current);
|
|
28436
|
-
camera.lookAt(initialTarget.current);
|
|
28437
|
-
if (controlsRef.current) {
|
|
28438
|
-
controlsRef.current.target.copy(initialTarget.current);
|
|
28439
|
-
controlsRef.current.update();
|
|
28440
|
-
}
|
|
28441
|
-
},
|
|
28442
|
-
getViewBounds: () => {
|
|
28443
|
-
const min = new THREE__namespace.Vector3(-10, -10, -10);
|
|
28444
|
-
const max = new THREE__namespace.Vector3(10, 10, 10);
|
|
28445
|
-
return { min, max };
|
|
28446
|
-
}
|
|
28447
|
-
}));
|
|
28448
|
-
const maxPolarAngle = mode === "top-down" ? 0.1 : Math.PI / 2 - 0.1;
|
|
28449
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28450
|
-
drei.OrbitControls,
|
|
28451
|
-
{
|
|
28452
|
-
ref: controlsRef,
|
|
28453
|
-
camera,
|
|
28454
|
-
enabled: enableOrbit,
|
|
28455
|
-
target: initialTarget.current,
|
|
28456
|
-
minDistance,
|
|
28457
|
-
maxDistance,
|
|
28458
|
-
maxPolarAngle,
|
|
28459
|
-
enableDamping: true,
|
|
28460
|
-
dampingFactor: 0.05
|
|
28461
|
-
}
|
|
28462
|
-
);
|
|
28463
|
-
}
|
|
28464
|
-
);
|
|
28465
|
-
Camera3D.displayName = "Camera3D";
|
|
28466
|
-
var Canvas3DErrorBoundary = class extends React87.Component {
|
|
28467
|
-
constructor(props) {
|
|
28468
|
-
super(props);
|
|
28469
|
-
__publicField(this, "handleReset", () => {
|
|
28470
|
-
this.setState({
|
|
28471
|
-
hasError: false,
|
|
28472
|
-
error: null,
|
|
28473
|
-
errorInfo: null
|
|
28474
|
-
});
|
|
28475
|
-
this.props.onReset?.();
|
|
28476
|
-
});
|
|
28477
|
-
this.state = {
|
|
28478
|
-
hasError: false,
|
|
28479
|
-
error: null,
|
|
28480
|
-
errorInfo: null
|
|
28481
|
-
};
|
|
28482
|
-
}
|
|
28483
|
-
static getDerivedStateFromError(error) {
|
|
28484
|
-
return {
|
|
28485
|
-
hasError: true,
|
|
28486
|
-
error,
|
|
28487
|
-
errorInfo: null
|
|
28488
|
-
};
|
|
28489
|
-
}
|
|
28490
|
-
componentDidCatch(error, errorInfo) {
|
|
28491
|
-
this.setState({ errorInfo });
|
|
28492
|
-
this.props.onError?.(error, errorInfo);
|
|
28493
|
-
console.error("[Canvas3DErrorBoundary] Error caught:", error);
|
|
28494
|
-
console.error("[Canvas3DErrorBoundary] Component stack:", errorInfo.componentStack);
|
|
28495
|
-
}
|
|
28496
|
-
render() {
|
|
28497
|
-
if (this.state.hasError) {
|
|
28498
|
-
if (this.props.fallback) {
|
|
28499
|
-
return this.props.fallback;
|
|
28500
|
-
}
|
|
28501
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
28502
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
28503
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
28504
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
28505
|
-
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
28506
|
-
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
28507
|
-
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
28508
|
-
this.state.error.message,
|
|
28509
|
-
"\n",
|
|
28510
|
-
this.state.error.stack
|
|
28511
|
-
] }),
|
|
28512
|
-
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
28513
|
-
] }),
|
|
28514
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
28515
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28516
|
-
"button",
|
|
28517
|
-
{
|
|
28518
|
-
className: "error__button error__button--primary",
|
|
28519
|
-
onClick: this.handleReset,
|
|
28520
|
-
children: "Try Again"
|
|
28521
|
-
}
|
|
28522
|
-
),
|
|
28523
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28524
|
-
"button",
|
|
28525
|
-
{
|
|
28526
|
-
className: "error__button error__button--secondary",
|
|
28527
|
-
onClick: () => window.location.reload(),
|
|
28528
|
-
children: "Reload Page"
|
|
28529
|
-
}
|
|
28530
|
-
)
|
|
28531
|
-
] })
|
|
28532
|
-
] }) });
|
|
28533
|
-
}
|
|
28534
|
-
return this.props.children;
|
|
28535
|
-
}
|
|
28536
|
-
};
|
|
28537
|
-
function Canvas3DLoadingState({
|
|
28538
|
-
progress = 0,
|
|
28539
|
-
loaded = 0,
|
|
28540
|
-
total = 0,
|
|
28541
|
-
message = "Loading 3D Scene...",
|
|
28542
|
-
details,
|
|
28543
|
-
showSpinner = true,
|
|
28544
|
-
className
|
|
28545
|
-
}) {
|
|
28546
|
-
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
28547
|
-
const hasProgress = total > 0;
|
|
28548
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
28549
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
28550
|
-
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
28551
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
28552
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
28553
|
-
] }),
|
|
28554
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
28555
|
-
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
28556
|
-
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
28557
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
28558
|
-
"div",
|
|
28559
|
-
{
|
|
28560
|
-
className: "progress__fill",
|
|
28561
|
-
style: { width: `${clampedProgress}%` }
|
|
28562
|
-
}
|
|
28563
|
-
) }),
|
|
28564
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
28565
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
28566
|
-
clampedProgress,
|
|
28567
|
-
"%"
|
|
28568
|
-
] }),
|
|
28569
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
28570
|
-
"(",
|
|
28571
|
-
loaded,
|
|
28572
|
-
"/",
|
|
28573
|
-
total,
|
|
28574
|
-
")"
|
|
28575
|
-
] })
|
|
28576
|
-
] })
|
|
28577
|
-
] })
|
|
28578
|
-
] }),
|
|
28579
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
28580
|
-
] });
|
|
28581
|
-
}
|
|
28582
28365
|
function CastleTemplate({
|
|
28583
28366
|
entity,
|
|
28584
28367
|
scale = 0.45,
|
|
@@ -29939,1771 +29722,113 @@ var DocumentViewer = ({
|
|
|
29939
29722
|
);
|
|
29940
29723
|
}
|
|
29941
29724
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
29942
|
-
Box,
|
|
29943
|
-
{
|
|
29944
|
-
className: "w-full overflow-auto p-4 font-mono text-sm",
|
|
29945
|
-
style: { height, fontSize: `${zoom}%` },
|
|
29946
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: activeContent })
|
|
29947
|
-
}
|
|
29948
|
-
);
|
|
29949
|
-
};
|
|
29950
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
|
|
29951
|
-
tabItems && tabItems.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
29952
|
-
Tabs,
|
|
29953
|
-
{
|
|
29954
|
-
tabs: tabItems,
|
|
29955
|
-
activeTab: `doc-${activeDocIndex}`,
|
|
29956
|
-
onTabChange: (id) => {
|
|
29957
|
-
const idx = parseInt(id.replace("doc-", ""), 10);
|
|
29958
|
-
setActiveDocIndex(idx);
|
|
29959
|
-
}
|
|
29960
|
-
}
|
|
29961
|
-
) }),
|
|
29962
|
-
showToolbar && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
29963
|
-
HStack,
|
|
29964
|
-
{
|
|
29965
|
-
gap: "sm",
|
|
29966
|
-
align: "center",
|
|
29967
|
-
justify: "between",
|
|
29968
|
-
className: "px-4 py-2 border-b border-[var(--color-border)] bg-[var(--color-muted)]/30",
|
|
29969
|
-
children: [
|
|
29970
|
-
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
|
|
29971
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons.FileText, size: "sm", className: "text-[var(--color-muted-foreground)]" }),
|
|
29972
|
-
title && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "truncate max-w-[200px]", children: title })
|
|
29973
|
-
] }),
|
|
29974
|
-
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
29975
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ZoomOut, onClick: handleZoomOut }),
|
|
29976
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", className: "tabular-nums w-10 text-center", children: [
|
|
29977
|
-
zoom,
|
|
29978
|
-
"%"
|
|
29979
|
-
] }),
|
|
29980
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ZoomIn, onClick: handleZoomIn }),
|
|
29981
|
-
totalPages && totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
29982
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-px h-4 bg-[var(--color-border)] mx-1" }),
|
|
29983
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ChevronLeft, onClick: handlePagePrev, disabled: currentPage <= 1 }),
|
|
29984
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: [
|
|
29985
|
-
currentPage,
|
|
29986
|
-
" / ",
|
|
29987
|
-
totalPages
|
|
29988
|
-
] }),
|
|
29989
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ChevronRight, onClick: handlePageNext, disabled: currentPage >= totalPages })
|
|
29990
|
-
] }),
|
|
29991
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-px h-4 bg-[var(--color-border)] mx-1" }),
|
|
29992
|
-
showDownload && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.Download, onClick: handleDownload }),
|
|
29993
|
-
showPrint && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.Printer, onClick: handlePrint }),
|
|
29994
|
-
actions?.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
29995
|
-
Badge,
|
|
29996
|
-
{
|
|
29997
|
-
variant: "default",
|
|
29998
|
-
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
29999
|
-
onClick: () => handleAction(action),
|
|
30000
|
-
children: action.label
|
|
30001
|
-
},
|
|
30002
|
-
idx
|
|
30003
|
-
))
|
|
30004
|
-
] })
|
|
30005
|
-
]
|
|
30006
|
-
}
|
|
30007
|
-
),
|
|
30008
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full overflow-hidden bg-[var(--color-muted)]/20", children: renderContent() })
|
|
30009
|
-
] }) });
|
|
30010
|
-
};
|
|
30011
|
-
DocumentViewer.displayName = "DocumentViewer";
|
|
30012
|
-
function extractTitle(children) {
|
|
30013
|
-
if (!React87__namespace.default.isValidElement(children)) return void 0;
|
|
30014
|
-
const props = children.props;
|
|
30015
|
-
if (typeof props.title === "string") {
|
|
30016
|
-
return props.title;
|
|
30017
|
-
}
|
|
30018
|
-
return void 0;
|
|
30019
|
-
}
|
|
30020
|
-
var DrawerSlot = ({
|
|
30021
|
-
children,
|
|
30022
|
-
title: overrideTitle,
|
|
30023
|
-
position = "right",
|
|
30024
|
-
size = "md",
|
|
30025
|
-
className
|
|
30026
|
-
}) => {
|
|
30027
|
-
const eventBus = useEventBus();
|
|
30028
|
-
const isOpen = Boolean(children);
|
|
30029
|
-
const title = overrideTitle || extractTitle(children);
|
|
30030
|
-
const handleClose = () => {
|
|
30031
|
-
eventBus.emit("UI:CLOSE");
|
|
30032
|
-
eventBus.emit("UI:CANCEL");
|
|
30033
|
-
};
|
|
30034
|
-
if (!isOpen) return null;
|
|
30035
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
30036
|
-
Drawer,
|
|
30037
|
-
{
|
|
30038
|
-
isOpen,
|
|
30039
|
-
onClose: handleClose,
|
|
30040
|
-
title,
|
|
30041
|
-
position,
|
|
30042
|
-
width: size,
|
|
30043
|
-
className,
|
|
30044
|
-
children
|
|
30045
|
-
}
|
|
30046
|
-
);
|
|
30047
|
-
};
|
|
30048
|
-
DrawerSlot.displayName = "DrawerSlot";
|
|
30049
|
-
var DEFAULT_FEATURE_CONFIGS = {
|
|
30050
|
-
tree: { color: 2263842, height: 1.5, scale: 1, geometry: "tree" },
|
|
30051
|
-
rock: { color: 8421504, height: 0.5, scale: 0.8, geometry: "rock" },
|
|
30052
|
-
bush: { color: 3329330, height: 0.4, scale: 0.6, geometry: "bush" },
|
|
30053
|
-
house: { color: 9127187, height: 1.2, scale: 1.2, geometry: "house" },
|
|
30054
|
-
tower: { color: 6908265, height: 2.5, scale: 1, geometry: "tower" },
|
|
30055
|
-
wall: { color: 8421504, height: 1, scale: 1, geometry: "wall" },
|
|
30056
|
-
mountain: { color: 5597999, height: 2, scale: 1.5, geometry: "mountain" },
|
|
30057
|
-
hill: { color: 7048739, height: 0.8, scale: 1.2, geometry: "hill" },
|
|
30058
|
-
water: { color: 4491468, height: 0.1, scale: 1, geometry: "water" },
|
|
30059
|
-
chest: { color: 16766720, height: 0.3, scale: 0.4, geometry: "chest" },
|
|
30060
|
-
sign: { color: 9127187, height: 0.8, scale: 0.3, geometry: "sign" },
|
|
30061
|
-
portal: { color: 10040012, height: 1.5, scale: 1, geometry: "portal" }
|
|
30062
|
-
};
|
|
30063
|
-
function TreeFeature({ height, color }) {
|
|
30064
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30065
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.3, 0], children: [
|
|
30066
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.08, 0.1, height * 0.6, 6] }),
|
|
30067
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
30068
|
-
] }),
|
|
30069
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.7, 0], children: [
|
|
30070
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.4, height * 0.5, 8] }),
|
|
30071
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30072
|
-
] }),
|
|
30073
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.9, 0], children: [
|
|
30074
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.3, height * 0.4, 8] }),
|
|
30075
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30076
|
-
] }),
|
|
30077
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 1.05, 0], children: [
|
|
30078
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.15, height * 0.25, 8] }),
|
|
30079
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30080
|
-
] })
|
|
30081
|
-
] });
|
|
30082
|
-
}
|
|
30083
|
-
function RockFeature({ height, color }) {
|
|
30084
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.4, 0], children: [
|
|
30085
|
-
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [height * 0.5, 0] }),
|
|
30086
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, roughness: 0.9 })
|
|
30087
|
-
] });
|
|
30088
|
-
}
|
|
30089
|
-
function BushFeature({ height, color }) {
|
|
30090
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30091
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.3, 0], children: [
|
|
30092
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [height * 0.4, 8, 8] }),
|
|
30093
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30094
|
-
] }),
|
|
30095
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0.1, height * 0.4, 0.1], children: [
|
|
30096
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [height * 0.25, 8, 8] }),
|
|
30097
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30098
|
-
] })
|
|
30099
|
-
] });
|
|
30100
|
-
}
|
|
30101
|
-
function HouseFeature({ height, color }) {
|
|
30102
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30103
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.4, 0], children: [
|
|
30104
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, height * 0.8, 0.8] }),
|
|
30105
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 13808780 })
|
|
30106
|
-
] }),
|
|
30107
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.9, 0], children: [
|
|
30108
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.6, height * 0.4, 4] }),
|
|
30109
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30110
|
-
] }),
|
|
30111
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.25, 0.41], children: [
|
|
30112
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.25, height * 0.5] }),
|
|
30113
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 4863784 })
|
|
30114
|
-
] })
|
|
30115
|
-
] });
|
|
30116
|
-
}
|
|
30117
|
-
function TowerFeature({ height, color }) {
|
|
30118
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30119
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
|
|
30120
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.35, height, 8] }),
|
|
30121
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30122
|
-
] }),
|
|
30123
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height + 0.05, 0], children: [
|
|
30124
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.35, 0.35, 0.1, 8] }),
|
|
30125
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30126
|
-
] })
|
|
30127
|
-
] });
|
|
30128
|
-
}
|
|
30129
|
-
function ChestFeature({ height, color }) {
|
|
30130
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30131
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
|
|
30132
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.3, height, 0.2] }),
|
|
30133
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
|
|
30134
|
-
] }),
|
|
30135
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height + 0.05, 0], children: [
|
|
30136
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.15, 0.15, 0.3, 8, 1, false, 0, Math.PI] }),
|
|
30137
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, metalness: 0.6, roughness: 0.3 })
|
|
30138
|
-
] })
|
|
30139
|
-
] });
|
|
30140
|
-
}
|
|
30141
|
-
function DefaultFeature({ height, color }) {
|
|
30142
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, height * 0.5, 0], children: [
|
|
30143
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.5, height, 0.5] }),
|
|
30144
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
30145
|
-
] });
|
|
30146
|
-
}
|
|
30147
|
-
function FeatureVisual({
|
|
30148
|
-
feature,
|
|
30149
|
-
position,
|
|
30150
|
-
isSelected,
|
|
30151
|
-
onClick,
|
|
30152
|
-
onHover
|
|
30153
|
-
}) {
|
|
30154
|
-
const config = DEFAULT_FEATURE_CONFIGS[feature.type] || {
|
|
30155
|
-
color: 8947848,
|
|
30156
|
-
height: 0.5,
|
|
30157
|
-
scale: 1,
|
|
30158
|
-
geometry: "default"
|
|
30159
|
-
};
|
|
30160
|
-
const color = feature.color ? parseInt(feature.color.replace("#", ""), 16) : config.color;
|
|
30161
|
-
const renderGeometry = () => {
|
|
30162
|
-
switch (config.geometry) {
|
|
30163
|
-
case "tree":
|
|
30164
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TreeFeature, { height: config.height, color });
|
|
30165
|
-
case "rock":
|
|
30166
|
-
return /* @__PURE__ */ jsxRuntime.jsx(RockFeature, { height: config.height, color });
|
|
30167
|
-
case "bush":
|
|
30168
|
-
return /* @__PURE__ */ jsxRuntime.jsx(BushFeature, { height: config.height, color });
|
|
30169
|
-
case "house":
|
|
30170
|
-
return /* @__PURE__ */ jsxRuntime.jsx(HouseFeature, { height: config.height, color });
|
|
30171
|
-
case "tower":
|
|
30172
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TowerFeature, { height: config.height, color });
|
|
30173
|
-
case "chest":
|
|
30174
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ChestFeature, { height: config.height, color });
|
|
30175
|
-
default:
|
|
30176
|
-
return /* @__PURE__ */ jsxRuntime.jsx(DefaultFeature, { height: config.height, color });
|
|
30177
|
-
}
|
|
30178
|
-
};
|
|
30179
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30180
|
-
"group",
|
|
30181
|
-
{
|
|
30182
|
-
position,
|
|
30183
|
-
scale: config.scale,
|
|
30184
|
-
onClick,
|
|
30185
|
-
onPointerEnter: () => onHover(true),
|
|
30186
|
-
onPointerLeave: () => onHover(false),
|
|
30187
|
-
userData: { type: "feature", featureId: feature.id, featureType: feature.type },
|
|
30188
|
-
children: [
|
|
30189
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30190
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
30191
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
30192
|
-
] }),
|
|
30193
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30194
|
-
/* @__PURE__ */ jsxRuntime.jsx("circleGeometry", { args: [0.35, 16] }),
|
|
30195
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
|
|
30196
|
-
] }),
|
|
30197
|
-
renderGeometry()
|
|
30198
|
-
]
|
|
30199
|
-
}
|
|
30200
|
-
);
|
|
30201
|
-
}
|
|
30202
|
-
function FeatureRenderer({
|
|
30203
|
-
features,
|
|
30204
|
-
cellSize = 1,
|
|
30205
|
-
offsetX = 0,
|
|
30206
|
-
offsetZ = 0,
|
|
30207
|
-
onFeatureClick,
|
|
30208
|
-
onFeatureHover,
|
|
30209
|
-
selectedFeatureIds = [],
|
|
30210
|
-
featureColors
|
|
30211
|
-
}) {
|
|
30212
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { children: features.map((feature) => {
|
|
30213
|
-
const x = (feature.x - offsetX) * cellSize;
|
|
30214
|
-
const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
|
|
30215
|
-
const y = (feature.elevation ?? 0) * 0.1;
|
|
30216
|
-
const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
|
|
30217
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
30218
|
-
FeatureVisual,
|
|
30219
|
-
{
|
|
30220
|
-
feature,
|
|
30221
|
-
position: [x, y, z],
|
|
30222
|
-
isSelected,
|
|
30223
|
-
onClick: () => onFeatureClick?.(feature),
|
|
30224
|
-
onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
|
|
30225
|
-
},
|
|
30226
|
-
feature.id ?? `feature-${feature.x}-${feature.y}`
|
|
30227
|
-
);
|
|
30228
|
-
}) });
|
|
30229
|
-
}
|
|
30230
|
-
function detectAssetRoot(modelUrl) {
|
|
30231
|
-
const idx = modelUrl.indexOf("/3d/");
|
|
30232
|
-
if (idx !== -1) {
|
|
30233
|
-
return modelUrl.substring(0, idx + 4);
|
|
30234
|
-
}
|
|
30235
|
-
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
30236
|
-
}
|
|
30237
|
-
function useGLTFModel(url) {
|
|
30238
|
-
const [model, setModel] = React87.useState(null);
|
|
30239
|
-
const [isLoading, setIsLoading] = React87.useState(false);
|
|
30240
|
-
const [error, setError] = React87.useState(null);
|
|
30241
|
-
React87.useEffect(() => {
|
|
30242
|
-
if (!url) {
|
|
30243
|
-
setModel(null);
|
|
30244
|
-
return;
|
|
30245
|
-
}
|
|
30246
|
-
setIsLoading(true);
|
|
30247
|
-
setError(null);
|
|
30248
|
-
const assetRoot = detectAssetRoot(url);
|
|
30249
|
-
const loader = new GLTFLoader.GLTFLoader();
|
|
30250
|
-
loader.setResourcePath(assetRoot);
|
|
30251
|
-
loader.load(
|
|
30252
|
-
url,
|
|
30253
|
-
(gltf) => {
|
|
30254
|
-
setModel(gltf.scene);
|
|
30255
|
-
setIsLoading(false);
|
|
30256
|
-
},
|
|
30257
|
-
void 0,
|
|
30258
|
-
(err) => {
|
|
30259
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
30260
|
-
setIsLoading(false);
|
|
30261
|
-
}
|
|
30262
|
-
);
|
|
30263
|
-
}, [url]);
|
|
30264
|
-
return { model, isLoading, error };
|
|
30265
|
-
}
|
|
30266
|
-
function FeatureModel({
|
|
30267
|
-
feature,
|
|
30268
|
-
position,
|
|
30269
|
-
isSelected,
|
|
30270
|
-
onClick,
|
|
30271
|
-
onHover
|
|
30272
|
-
}) {
|
|
30273
|
-
const groupRef = React87.useRef(null);
|
|
30274
|
-
const { model: loadedModel, isLoading } = useGLTFModel(feature.assetUrl);
|
|
30275
|
-
const model = React87.useMemo(() => {
|
|
30276
|
-
if (!loadedModel) return null;
|
|
30277
|
-
const cloned = loadedModel.clone();
|
|
30278
|
-
cloned.scale.setScalar(0.3);
|
|
30279
|
-
cloned.traverse((child) => {
|
|
30280
|
-
if (child instanceof THREE__namespace.Mesh) {
|
|
30281
|
-
child.castShadow = true;
|
|
30282
|
-
child.receiveShadow = true;
|
|
30283
|
-
}
|
|
30284
|
-
});
|
|
30285
|
-
return cloned;
|
|
30286
|
-
}, [loadedModel]);
|
|
30287
|
-
fiber.useFrame((state) => {
|
|
30288
|
-
if (groupRef.current) {
|
|
30289
|
-
const featureRotation = feature.rotation;
|
|
30290
|
-
const baseRotation = featureRotation !== void 0 ? featureRotation * Math.PI / 180 - Math.PI / 4 : -Math.PI / 4;
|
|
30291
|
-
const wobble = isSelected ? Math.sin(state.clock.elapsedTime * 2) * 0.1 : 0;
|
|
30292
|
-
groupRef.current.rotation.y = baseRotation + wobble;
|
|
30293
|
-
}
|
|
30294
|
-
});
|
|
30295
|
-
if (isLoading) {
|
|
30296
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
30297
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
30298
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
30299
|
-
] }) });
|
|
30300
|
-
}
|
|
30301
|
-
if (!model && !feature.assetUrl) {
|
|
30302
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30303
|
-
"group",
|
|
30304
|
-
{
|
|
30305
|
-
position,
|
|
30306
|
-
onClick,
|
|
30307
|
-
onPointerEnter: () => onHover(true),
|
|
30308
|
-
onPointerLeave: () => onHover(false),
|
|
30309
|
-
userData: { type: "feature", featureId: feature.id, featureType: feature.type },
|
|
30310
|
-
children: [
|
|
30311
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30312
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
30313
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
30314
|
-
] }),
|
|
30315
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.5, 0], children: [
|
|
30316
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.4, 0.4, 0.4] }),
|
|
30317
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8947848 })
|
|
30318
|
-
] })
|
|
30319
|
-
]
|
|
30320
|
-
}
|
|
30321
|
-
);
|
|
30322
|
-
}
|
|
30323
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
30324
|
-
"group",
|
|
30325
|
-
{
|
|
30326
|
-
ref: groupRef,
|
|
30327
|
-
position,
|
|
30328
|
-
onClick,
|
|
30329
|
-
onPointerEnter: () => onHover(true),
|
|
30330
|
-
onPointerLeave: () => onHover(false),
|
|
30331
|
-
userData: { type: "feature", featureId: feature.id, featureType: feature.type },
|
|
30332
|
-
children: [
|
|
30333
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30334
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
30335
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
30336
|
-
] }),
|
|
30337
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.01, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30338
|
-
/* @__PURE__ */ jsxRuntime.jsx("circleGeometry", { args: [0.35, 16] }),
|
|
30339
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.2 })
|
|
30340
|
-
] }),
|
|
30341
|
-
model && /* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model })
|
|
30342
|
-
]
|
|
30343
|
-
}
|
|
30344
|
-
);
|
|
30345
|
-
}
|
|
30346
|
-
function FeatureRenderer3D({
|
|
30347
|
-
features,
|
|
30348
|
-
cellSize = 1,
|
|
30349
|
-
offsetX = 0,
|
|
30350
|
-
offsetZ = 0,
|
|
30351
|
-
onFeatureClick,
|
|
30352
|
-
onFeatureHover,
|
|
30353
|
-
selectedFeatureIds = []
|
|
30354
|
-
}) {
|
|
30355
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { children: features.map((feature) => {
|
|
30356
|
-
const x = (feature.x - offsetX) * cellSize;
|
|
30357
|
-
const z = ((feature.z ?? feature.y ?? 0) - offsetZ) * cellSize;
|
|
30358
|
-
const y = (feature.elevation ?? 0) * 0.1;
|
|
30359
|
-
const isSelected = feature.id ? selectedFeatureIds.includes(feature.id) : false;
|
|
30360
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
30361
|
-
FeatureModel,
|
|
30362
|
-
{
|
|
30363
|
-
feature,
|
|
30364
|
-
position: [x, y, z],
|
|
30365
|
-
isSelected,
|
|
30366
|
-
onClick: () => onFeatureClick?.(feature),
|
|
30367
|
-
onHover: (hovered) => onFeatureHover?.(hovered ? feature : null)
|
|
30368
|
-
},
|
|
30369
|
-
feature.id ?? `feature-${feature.x}-${feature.y}`
|
|
30370
|
-
);
|
|
30371
|
-
}) });
|
|
30372
|
-
}
|
|
30373
|
-
function detectAssetRoot2(modelUrl) {
|
|
30374
|
-
const idx = modelUrl.indexOf("/3d/");
|
|
30375
|
-
if (idx !== -1) {
|
|
30376
|
-
return modelUrl.substring(0, idx + 4);
|
|
30377
|
-
}
|
|
30378
|
-
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
30379
|
-
}
|
|
30380
|
-
function createGLTFLoaderForUrl(url) {
|
|
30381
|
-
const loader = new GLTFLoader_js.GLTFLoader();
|
|
30382
|
-
loader.setResourcePath(detectAssetRoot2(url));
|
|
30383
|
-
return loader;
|
|
30384
|
-
}
|
|
30385
|
-
var AssetLoader = class {
|
|
30386
|
-
constructor() {
|
|
30387
|
-
__publicField(this, "objLoader");
|
|
30388
|
-
__publicField(this, "textureLoader");
|
|
30389
|
-
__publicField(this, "modelCache");
|
|
30390
|
-
__publicField(this, "textureCache");
|
|
30391
|
-
__publicField(this, "loadingPromises");
|
|
30392
|
-
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
30393
|
-
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
30394
|
-
this.modelCache = /* @__PURE__ */ new Map();
|
|
30395
|
-
this.textureCache = /* @__PURE__ */ new Map();
|
|
30396
|
-
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
30397
|
-
}
|
|
30398
|
-
/**
|
|
30399
|
-
* Load a GLB/GLTF model
|
|
30400
|
-
* @param url - URL to the .glb or .gltf file
|
|
30401
|
-
* @returns Promise with loaded model scene and animations
|
|
30402
|
-
*/
|
|
30403
|
-
async loadModel(url) {
|
|
30404
|
-
if (this.modelCache.has(url)) {
|
|
30405
|
-
return this.modelCache.get(url);
|
|
30406
|
-
}
|
|
30407
|
-
if (this.loadingPromises.has(url)) {
|
|
30408
|
-
return this.loadingPromises.get(url);
|
|
30409
|
-
}
|
|
30410
|
-
const loader = createGLTFLoaderForUrl(url);
|
|
30411
|
-
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
30412
|
-
const result = {
|
|
30413
|
-
scene: gltf.scene,
|
|
30414
|
-
animations: gltf.animations || []
|
|
30415
|
-
};
|
|
30416
|
-
this.modelCache.set(url, result);
|
|
30417
|
-
this.loadingPromises.delete(url);
|
|
30418
|
-
return result;
|
|
30419
|
-
}).catch((error) => {
|
|
30420
|
-
this.loadingPromises.delete(url);
|
|
30421
|
-
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
30422
|
-
});
|
|
30423
|
-
this.loadingPromises.set(url, loadPromise);
|
|
30424
|
-
return loadPromise;
|
|
30425
|
-
}
|
|
30426
|
-
/**
|
|
30427
|
-
* Load an OBJ model (fallback for non-GLB assets)
|
|
30428
|
-
* @param url - URL to the .obj file
|
|
30429
|
-
* @returns Promise with loaded object group
|
|
30430
|
-
*/
|
|
30431
|
-
async loadOBJ(url) {
|
|
30432
|
-
if (this.modelCache.has(url)) {
|
|
30433
|
-
return this.modelCache.get(url).scene;
|
|
30434
|
-
}
|
|
30435
|
-
if (this.loadingPromises.has(url)) {
|
|
30436
|
-
const result = await this.loadingPromises.get(url);
|
|
30437
|
-
return result.scene;
|
|
30438
|
-
}
|
|
30439
|
-
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
30440
|
-
const result = {
|
|
30441
|
-
scene: group,
|
|
30442
|
-
animations: []
|
|
30443
|
-
};
|
|
30444
|
-
this.modelCache.set(url, result);
|
|
30445
|
-
this.loadingPromises.delete(url);
|
|
30446
|
-
return result;
|
|
30447
|
-
}).catch((error) => {
|
|
30448
|
-
this.loadingPromises.delete(url);
|
|
30449
|
-
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
30450
|
-
});
|
|
30451
|
-
this.loadingPromises.set(url, loadPromise);
|
|
30452
|
-
return (await loadPromise).scene;
|
|
30453
|
-
}
|
|
30454
|
-
/**
|
|
30455
|
-
* Load a texture
|
|
30456
|
-
* @param url - URL to the texture image
|
|
30457
|
-
* @returns Promise with loaded texture
|
|
30458
|
-
*/
|
|
30459
|
-
async loadTexture(url) {
|
|
30460
|
-
if (this.textureCache.has(url)) {
|
|
30461
|
-
return this.textureCache.get(url);
|
|
30462
|
-
}
|
|
30463
|
-
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
30464
|
-
return this.loadingPromises.get(`texture:${url}`);
|
|
30465
|
-
}
|
|
30466
|
-
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
30467
|
-
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
30468
|
-
this.textureCache.set(url, texture);
|
|
30469
|
-
this.loadingPromises.delete(`texture:${url}`);
|
|
30470
|
-
return texture;
|
|
30471
|
-
}).catch((error) => {
|
|
30472
|
-
this.loadingPromises.delete(`texture:${url}`);
|
|
30473
|
-
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
30474
|
-
});
|
|
30475
|
-
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
30476
|
-
return loadPromise;
|
|
30477
|
-
}
|
|
30478
|
-
/**
|
|
30479
|
-
* Preload multiple assets
|
|
30480
|
-
* @param urls - Array of asset URLs to preload
|
|
30481
|
-
* @returns Promise that resolves when all assets are loaded
|
|
30482
|
-
*/
|
|
30483
|
-
async preload(urls) {
|
|
30484
|
-
const promises = urls.map((url) => {
|
|
30485
|
-
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
30486
|
-
return this.loadModel(url).catch(() => null);
|
|
30487
|
-
} else if (url.endsWith(".obj")) {
|
|
30488
|
-
return this.loadOBJ(url).catch(() => null);
|
|
30489
|
-
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
30490
|
-
return this.loadTexture(url).catch(() => null);
|
|
30491
|
-
}
|
|
30492
|
-
return Promise.resolve(null);
|
|
30493
|
-
});
|
|
30494
|
-
await Promise.all(promises);
|
|
30495
|
-
}
|
|
30496
|
-
/**
|
|
30497
|
-
* Check if a model is cached
|
|
30498
|
-
* @param url - Model URL
|
|
30499
|
-
*/
|
|
30500
|
-
hasModel(url) {
|
|
30501
|
-
return this.modelCache.has(url);
|
|
30502
|
-
}
|
|
30503
|
-
/**
|
|
30504
|
-
* Check if a texture is cached
|
|
30505
|
-
* @param url - Texture URL
|
|
30506
|
-
*/
|
|
30507
|
-
hasTexture(url) {
|
|
30508
|
-
return this.textureCache.has(url);
|
|
30509
|
-
}
|
|
30510
|
-
/**
|
|
30511
|
-
* Get cached model (throws if not cached)
|
|
30512
|
-
* @param url - Model URL
|
|
30513
|
-
*/
|
|
30514
|
-
getModel(url) {
|
|
30515
|
-
const model = this.modelCache.get(url);
|
|
30516
|
-
if (!model) {
|
|
30517
|
-
throw new Error(`Model ${url} not in cache`);
|
|
30518
|
-
}
|
|
30519
|
-
return model;
|
|
30520
|
-
}
|
|
30521
|
-
/**
|
|
30522
|
-
* Get cached texture (throws if not cached)
|
|
30523
|
-
* @param url - Texture URL
|
|
30524
|
-
*/
|
|
30525
|
-
getTexture(url) {
|
|
30526
|
-
const texture = this.textureCache.get(url);
|
|
30527
|
-
if (!texture) {
|
|
30528
|
-
throw new Error(`Texture ${url} not in cache`);
|
|
30529
|
-
}
|
|
30530
|
-
return texture;
|
|
30531
|
-
}
|
|
30532
|
-
/**
|
|
30533
|
-
* Clear all caches
|
|
30534
|
-
*/
|
|
30535
|
-
clearCache() {
|
|
30536
|
-
this.textureCache.forEach((texture) => {
|
|
30537
|
-
texture.dispose();
|
|
30538
|
-
});
|
|
30539
|
-
this.modelCache.forEach((model) => {
|
|
30540
|
-
model.scene.traverse((child) => {
|
|
30541
|
-
if (child instanceof THREE__namespace.Mesh) {
|
|
30542
|
-
child.geometry.dispose();
|
|
30543
|
-
if (Array.isArray(child.material)) {
|
|
30544
|
-
child.material.forEach((m) => m.dispose());
|
|
30545
|
-
} else {
|
|
30546
|
-
child.material.dispose();
|
|
30547
|
-
}
|
|
30548
|
-
}
|
|
30549
|
-
});
|
|
30550
|
-
});
|
|
30551
|
-
this.modelCache.clear();
|
|
30552
|
-
this.textureCache.clear();
|
|
30553
|
-
this.loadingPromises.clear();
|
|
30554
|
-
}
|
|
30555
|
-
/**
|
|
30556
|
-
* Get cache statistics
|
|
30557
|
-
*/
|
|
30558
|
-
getStats() {
|
|
30559
|
-
return {
|
|
30560
|
-
models: this.modelCache.size,
|
|
30561
|
-
textures: this.textureCache.size,
|
|
30562
|
-
loading: this.loadingPromises.size
|
|
30563
|
-
};
|
|
30564
|
-
}
|
|
30565
|
-
};
|
|
30566
|
-
new AssetLoader();
|
|
30567
|
-
function useAssetLoader(options = {}) {
|
|
30568
|
-
const { preloadUrls = [], loader: customLoader } = options;
|
|
30569
|
-
const loaderRef = React87.useRef(customLoader || new AssetLoader());
|
|
30570
|
-
const [state, setState] = React87.useState({
|
|
30571
|
-
isLoading: false,
|
|
30572
|
-
progress: 0,
|
|
30573
|
-
loaded: 0,
|
|
30574
|
-
total: 0,
|
|
30575
|
-
errors: []
|
|
30576
|
-
});
|
|
30577
|
-
React87.useEffect(() => {
|
|
30578
|
-
if (preloadUrls.length > 0) {
|
|
30579
|
-
preload(preloadUrls);
|
|
30580
|
-
}
|
|
30581
|
-
}, []);
|
|
30582
|
-
const updateProgress = React87.useCallback((loaded, total) => {
|
|
30583
|
-
setState((prev) => ({
|
|
30584
|
-
...prev,
|
|
30585
|
-
loaded,
|
|
30586
|
-
total,
|
|
30587
|
-
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
30588
|
-
}));
|
|
30589
|
-
}, []);
|
|
30590
|
-
const loadModel = React87.useCallback(
|
|
30591
|
-
async (url) => {
|
|
30592
|
-
setState((prev) => ({ ...prev, isLoading: true }));
|
|
30593
|
-
try {
|
|
30594
|
-
const model = await loaderRef.current.loadModel(url);
|
|
30595
|
-
setState((prev) => ({
|
|
30596
|
-
...prev,
|
|
30597
|
-
isLoading: false,
|
|
30598
|
-
loaded: prev.loaded + 1
|
|
30599
|
-
}));
|
|
30600
|
-
return model;
|
|
30601
|
-
} catch (error) {
|
|
30602
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
30603
|
-
setState((prev) => ({
|
|
30604
|
-
...prev,
|
|
30605
|
-
isLoading: false,
|
|
30606
|
-
errors: [...prev.errors, errorMsg]
|
|
30607
|
-
}));
|
|
30608
|
-
throw error;
|
|
30609
|
-
}
|
|
30610
|
-
},
|
|
30611
|
-
[]
|
|
30612
|
-
);
|
|
30613
|
-
const loadOBJ = React87.useCallback(
|
|
30614
|
-
async (url) => {
|
|
30615
|
-
setState((prev) => ({ ...prev, isLoading: true }));
|
|
30616
|
-
try {
|
|
30617
|
-
const model = await loaderRef.current.loadOBJ(url);
|
|
30618
|
-
setState((prev) => ({
|
|
30619
|
-
...prev,
|
|
30620
|
-
isLoading: false,
|
|
30621
|
-
loaded: prev.loaded + 1
|
|
30622
|
-
}));
|
|
30623
|
-
return model;
|
|
30624
|
-
} catch (error) {
|
|
30625
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
30626
|
-
setState((prev) => ({
|
|
30627
|
-
...prev,
|
|
30628
|
-
isLoading: false,
|
|
30629
|
-
errors: [...prev.errors, errorMsg]
|
|
30630
|
-
}));
|
|
30631
|
-
throw error;
|
|
30632
|
-
}
|
|
30633
|
-
},
|
|
30634
|
-
[]
|
|
30635
|
-
);
|
|
30636
|
-
const loadTexture = React87.useCallback(
|
|
30637
|
-
async (url) => {
|
|
30638
|
-
setState((prev) => ({ ...prev, isLoading: true }));
|
|
30639
|
-
try {
|
|
30640
|
-
const texture = await loaderRef.current.loadTexture(url);
|
|
30641
|
-
setState((prev) => ({
|
|
30642
|
-
...prev,
|
|
30643
|
-
isLoading: false,
|
|
30644
|
-
loaded: prev.loaded + 1
|
|
30645
|
-
}));
|
|
30646
|
-
return texture;
|
|
30647
|
-
} catch (error) {
|
|
30648
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
30649
|
-
setState((prev) => ({
|
|
30650
|
-
...prev,
|
|
30651
|
-
isLoading: false,
|
|
30652
|
-
errors: [...prev.errors, errorMsg]
|
|
30653
|
-
}));
|
|
30654
|
-
throw error;
|
|
30655
|
-
}
|
|
30656
|
-
},
|
|
30657
|
-
[]
|
|
30658
|
-
);
|
|
30659
|
-
const preload = React87.useCallback(
|
|
30660
|
-
async (urls) => {
|
|
30661
|
-
setState((prev) => ({
|
|
30662
|
-
...prev,
|
|
30663
|
-
isLoading: true,
|
|
30664
|
-
total: urls.length,
|
|
30665
|
-
loaded: 0,
|
|
30666
|
-
errors: []
|
|
30667
|
-
}));
|
|
30668
|
-
let completed = 0;
|
|
30669
|
-
const errors = [];
|
|
30670
|
-
await Promise.all(
|
|
30671
|
-
urls.map(async (url) => {
|
|
30672
|
-
try {
|
|
30673
|
-
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
30674
|
-
await loaderRef.current.loadModel(url);
|
|
30675
|
-
} else if (url.endsWith(".obj")) {
|
|
30676
|
-
await loaderRef.current.loadOBJ(url);
|
|
30677
|
-
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
30678
|
-
await loaderRef.current.loadTexture(url);
|
|
30679
|
-
}
|
|
30680
|
-
completed++;
|
|
30681
|
-
updateProgress(completed, urls.length);
|
|
30682
|
-
} catch (error) {
|
|
30683
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
30684
|
-
errors.push(`${url}: ${errorMsg}`);
|
|
30685
|
-
completed++;
|
|
30686
|
-
updateProgress(completed, urls.length);
|
|
30687
|
-
}
|
|
30688
|
-
})
|
|
30689
|
-
);
|
|
30690
|
-
setState((prev) => ({
|
|
30691
|
-
...prev,
|
|
30692
|
-
isLoading: false,
|
|
30693
|
-
errors
|
|
30694
|
-
}));
|
|
30695
|
-
},
|
|
30696
|
-
[updateProgress]
|
|
30697
|
-
);
|
|
30698
|
-
const hasModel = React87.useCallback((url) => {
|
|
30699
|
-
return loaderRef.current.hasModel(url);
|
|
30700
|
-
}, []);
|
|
30701
|
-
const hasTexture = React87.useCallback((url) => {
|
|
30702
|
-
return loaderRef.current.hasTexture(url);
|
|
30703
|
-
}, []);
|
|
30704
|
-
const getModel = React87.useCallback((url) => {
|
|
30705
|
-
try {
|
|
30706
|
-
return loaderRef.current.getModel(url);
|
|
30707
|
-
} catch {
|
|
30708
|
-
return void 0;
|
|
30709
|
-
}
|
|
30710
|
-
}, []);
|
|
30711
|
-
const getTexture = React87.useCallback((url) => {
|
|
30712
|
-
try {
|
|
30713
|
-
return loaderRef.current.getTexture(url);
|
|
30714
|
-
} catch {
|
|
30715
|
-
return void 0;
|
|
30716
|
-
}
|
|
30717
|
-
}, []);
|
|
30718
|
-
const clearCache = React87.useCallback(() => {
|
|
30719
|
-
loaderRef.current.clearCache();
|
|
30720
|
-
setState({
|
|
30721
|
-
isLoading: false,
|
|
30722
|
-
progress: 0,
|
|
30723
|
-
loaded: 0,
|
|
30724
|
-
total: 0,
|
|
30725
|
-
errors: []
|
|
30726
|
-
});
|
|
30727
|
-
}, []);
|
|
30728
|
-
return {
|
|
30729
|
-
...state,
|
|
30730
|
-
loadModel,
|
|
30731
|
-
loadOBJ,
|
|
30732
|
-
loadTexture,
|
|
30733
|
-
preload,
|
|
30734
|
-
hasModel,
|
|
30735
|
-
hasTexture,
|
|
30736
|
-
getModel,
|
|
30737
|
-
getTexture,
|
|
30738
|
-
clearCache
|
|
30739
|
-
};
|
|
30740
|
-
}
|
|
30741
|
-
function useGameCanvas3DEvents(options) {
|
|
30742
|
-
const {
|
|
30743
|
-
tileClickEvent,
|
|
30744
|
-
unitClickEvent,
|
|
30745
|
-
featureClickEvent,
|
|
30746
|
-
canvasClickEvent,
|
|
30747
|
-
tileHoverEvent,
|
|
30748
|
-
tileLeaveEvent,
|
|
30749
|
-
unitAnimationEvent,
|
|
30750
|
-
cameraChangeEvent,
|
|
30751
|
-
onTileClick,
|
|
30752
|
-
onUnitClick,
|
|
30753
|
-
onFeatureClick,
|
|
30754
|
-
onCanvasClick,
|
|
30755
|
-
onTileHover,
|
|
30756
|
-
onUnitAnimation
|
|
30757
|
-
} = options;
|
|
30758
|
-
const emit = useEmitEvent();
|
|
30759
|
-
const optionsRef = React87.useRef(options);
|
|
30760
|
-
optionsRef.current = options;
|
|
30761
|
-
const handleTileClick = React87.useCallback(
|
|
30762
|
-
(tile, event) => {
|
|
30763
|
-
if (tileClickEvent) {
|
|
30764
|
-
emit(tileClickEvent, {
|
|
30765
|
-
tileId: tile.id,
|
|
30766
|
-
x: tile.x,
|
|
30767
|
-
z: tile.z ?? tile.y ?? 0,
|
|
30768
|
-
type: tile.type,
|
|
30769
|
-
terrain: tile.terrain,
|
|
30770
|
-
elevation: tile.elevation
|
|
30771
|
-
});
|
|
30772
|
-
}
|
|
30773
|
-
optionsRef.current.onTileClick?.(tile, event);
|
|
30774
|
-
},
|
|
30775
|
-
[tileClickEvent, emit]
|
|
30776
|
-
);
|
|
30777
|
-
const handleUnitClick = React87.useCallback(
|
|
30778
|
-
(unit, event) => {
|
|
30779
|
-
if (unitClickEvent) {
|
|
30780
|
-
emit(unitClickEvent, {
|
|
30781
|
-
unitId: unit.id,
|
|
30782
|
-
x: unit.x,
|
|
30783
|
-
z: unit.z ?? unit.y ?? 0,
|
|
30784
|
-
unitType: unit.unitType,
|
|
30785
|
-
name: unit.name,
|
|
30786
|
-
team: unit.team,
|
|
30787
|
-
faction: unit.faction,
|
|
30788
|
-
health: unit.health,
|
|
30789
|
-
maxHealth: unit.maxHealth
|
|
30790
|
-
});
|
|
30791
|
-
}
|
|
30792
|
-
optionsRef.current.onUnitClick?.(unit, event);
|
|
30793
|
-
},
|
|
30794
|
-
[unitClickEvent, emit]
|
|
30795
|
-
);
|
|
30796
|
-
const handleFeatureClick = React87.useCallback(
|
|
30797
|
-
(feature, event) => {
|
|
30798
|
-
if (featureClickEvent) {
|
|
30799
|
-
emit(featureClickEvent, {
|
|
30800
|
-
featureId: feature.id,
|
|
30801
|
-
x: feature.x,
|
|
30802
|
-
z: feature.z ?? feature.y ?? 0,
|
|
30803
|
-
type: feature.type,
|
|
30804
|
-
elevation: feature.elevation
|
|
30805
|
-
});
|
|
30806
|
-
}
|
|
30807
|
-
optionsRef.current.onFeatureClick?.(feature, event);
|
|
30808
|
-
},
|
|
30809
|
-
[featureClickEvent, emit]
|
|
30810
|
-
);
|
|
30811
|
-
const handleCanvasClick = React87.useCallback(
|
|
30812
|
-
(event) => {
|
|
30813
|
-
if (canvasClickEvent) {
|
|
30814
|
-
emit(canvasClickEvent, {
|
|
30815
|
-
clientX: event.clientX,
|
|
30816
|
-
clientY: event.clientY,
|
|
30817
|
-
button: event.button
|
|
30818
|
-
});
|
|
30819
|
-
}
|
|
30820
|
-
optionsRef.current.onCanvasClick?.(event);
|
|
30821
|
-
},
|
|
30822
|
-
[canvasClickEvent, emit]
|
|
30823
|
-
);
|
|
30824
|
-
const handleTileHover = React87.useCallback(
|
|
30825
|
-
(tile, event) => {
|
|
30826
|
-
if (tile) {
|
|
30827
|
-
if (tileHoverEvent) {
|
|
30828
|
-
emit(tileHoverEvent, {
|
|
30829
|
-
tileId: tile.id,
|
|
30830
|
-
x: tile.x,
|
|
30831
|
-
z: tile.z ?? tile.y ?? 0,
|
|
30832
|
-
type: tile.type
|
|
30833
|
-
});
|
|
30834
|
-
}
|
|
30835
|
-
} else {
|
|
30836
|
-
if (tileLeaveEvent) {
|
|
30837
|
-
emit(tileLeaveEvent, {});
|
|
30838
|
-
}
|
|
30839
|
-
}
|
|
30840
|
-
optionsRef.current.onTileHover?.(tile, event);
|
|
30841
|
-
},
|
|
30842
|
-
[tileHoverEvent, tileLeaveEvent, emit]
|
|
30843
|
-
);
|
|
30844
|
-
const handleUnitAnimation = React87.useCallback(
|
|
30845
|
-
(unitId, state) => {
|
|
30846
|
-
if (unitAnimationEvent) {
|
|
30847
|
-
emit(unitAnimationEvent, {
|
|
30848
|
-
unitId,
|
|
30849
|
-
state,
|
|
30850
|
-
timestamp: Date.now()
|
|
30851
|
-
});
|
|
30852
|
-
}
|
|
30853
|
-
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
30854
|
-
},
|
|
30855
|
-
[unitAnimationEvent, emit]
|
|
30856
|
-
);
|
|
30857
|
-
const handleCameraChange = React87.useCallback(
|
|
30858
|
-
(position) => {
|
|
30859
|
-
if (cameraChangeEvent) {
|
|
30860
|
-
emit(cameraChangeEvent, {
|
|
30861
|
-
position,
|
|
30862
|
-
timestamp: Date.now()
|
|
30863
|
-
});
|
|
30864
|
-
}
|
|
30865
|
-
},
|
|
30866
|
-
[cameraChangeEvent, emit]
|
|
30867
|
-
);
|
|
30868
|
-
return {
|
|
30869
|
-
handleTileClick,
|
|
30870
|
-
handleUnitClick,
|
|
30871
|
-
handleFeatureClick,
|
|
30872
|
-
handleCanvasClick,
|
|
30873
|
-
handleTileHover,
|
|
30874
|
-
handleUnitAnimation,
|
|
30875
|
-
handleCameraChange
|
|
30876
|
-
};
|
|
30877
|
-
}
|
|
30878
|
-
function detectAssetRoot3(modelUrl) {
|
|
30879
|
-
const idx = modelUrl.indexOf("/3d/");
|
|
30880
|
-
if (idx !== -1) {
|
|
30881
|
-
return modelUrl.substring(0, idx + 4);
|
|
30882
|
-
}
|
|
30883
|
-
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
30884
|
-
}
|
|
30885
|
-
function useGLTFModel2(url, resourceBasePath) {
|
|
30886
|
-
const [state, setState] = React87.useState({
|
|
30887
|
-
model: null,
|
|
30888
|
-
isLoading: false,
|
|
30889
|
-
error: null
|
|
30890
|
-
});
|
|
30891
|
-
React87.useEffect(() => {
|
|
30892
|
-
if (!url) {
|
|
30893
|
-
setState({ model: null, isLoading: false, error: null });
|
|
30894
|
-
return;
|
|
30895
|
-
}
|
|
30896
|
-
console.log("[ModelLoader] Loading:", url);
|
|
30897
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
30898
|
-
const assetRoot = resourceBasePath || detectAssetRoot3(url);
|
|
30899
|
-
const loader = new GLTFLoader.GLTFLoader();
|
|
30900
|
-
loader.setResourcePath(assetRoot);
|
|
30901
|
-
loader.load(
|
|
30902
|
-
url,
|
|
30903
|
-
(gltf) => {
|
|
30904
|
-
console.log("[ModelLoader] Loaded:", url);
|
|
30905
|
-
setState({
|
|
30906
|
-
model: gltf.scene,
|
|
30907
|
-
isLoading: false,
|
|
30908
|
-
error: null
|
|
30909
|
-
});
|
|
30910
|
-
},
|
|
30911
|
-
void 0,
|
|
30912
|
-
(err) => {
|
|
30913
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
30914
|
-
console.warn("[ModelLoader] Failed:", url, errorMsg);
|
|
30915
|
-
setState({
|
|
30916
|
-
model: null,
|
|
30917
|
-
isLoading: false,
|
|
30918
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
30919
|
-
});
|
|
30920
|
-
}
|
|
30921
|
-
);
|
|
30922
|
-
}, [url, resourceBasePath]);
|
|
30923
|
-
return state;
|
|
30924
|
-
}
|
|
30925
|
-
function ModelLoader({
|
|
30926
|
-
url,
|
|
30927
|
-
position = [0, 0, 0],
|
|
30928
|
-
scale = 1,
|
|
30929
|
-
rotation = [0, 0, 0],
|
|
30930
|
-
isSelected = false,
|
|
30931
|
-
isHovered = false,
|
|
30932
|
-
onClick,
|
|
30933
|
-
onHover,
|
|
30934
|
-
fallbackGeometry = "box",
|
|
30935
|
-
castShadow = true,
|
|
30936
|
-
receiveShadow = true,
|
|
30937
|
-
resourceBasePath
|
|
30938
|
-
}) {
|
|
30939
|
-
const { model: loadedModel, isLoading, error } = useGLTFModel2(url, resourceBasePath);
|
|
30940
|
-
const model = React87.useMemo(() => {
|
|
30941
|
-
if (!loadedModel) return null;
|
|
30942
|
-
const cloned = loadedModel.clone();
|
|
30943
|
-
cloned.traverse((child) => {
|
|
30944
|
-
if (child instanceof THREE__namespace.Mesh) {
|
|
30945
|
-
child.castShadow = castShadow;
|
|
30946
|
-
child.receiveShadow = receiveShadow;
|
|
30947
|
-
}
|
|
30948
|
-
});
|
|
30949
|
-
return cloned;
|
|
30950
|
-
}, [loadedModel, castShadow, receiveShadow]);
|
|
30951
|
-
const scaleArray = React87.useMemo(() => {
|
|
30952
|
-
if (typeof scale === "number") {
|
|
30953
|
-
return [scale, scale, scale];
|
|
30954
|
-
}
|
|
30955
|
-
return scale;
|
|
30956
|
-
}, [scale]);
|
|
30957
|
-
const rotationRad = React87.useMemo(() => {
|
|
30958
|
-
return [
|
|
30959
|
-
rotation[0] * Math.PI / 180,
|
|
30960
|
-
rotation[1] * Math.PI / 180,
|
|
30961
|
-
rotation[2] * Math.PI / 180
|
|
30962
|
-
];
|
|
30963
|
-
}, [rotation]);
|
|
30964
|
-
if (isLoading) {
|
|
30965
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
30966
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
30967
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
30968
|
-
] }) });
|
|
30969
|
-
}
|
|
30970
|
-
if (error || !model) {
|
|
30971
|
-
if (fallbackGeometry === "none") {
|
|
30972
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
30973
|
-
}
|
|
30974
|
-
const fallbackProps = {
|
|
30975
|
-
onClick,
|
|
30976
|
-
onPointerOver: () => onHover?.(true),
|
|
30977
|
-
onPointerOut: () => onHover?.(false)
|
|
30978
|
-
};
|
|
30979
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
30980
|
-
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
30981
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
30982
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30983
|
-
"meshBasicMaterial",
|
|
30984
|
-
{
|
|
30985
|
-
color: isSelected ? 16755200 : 16777215,
|
|
30986
|
-
transparent: true,
|
|
30987
|
-
opacity: 0.5
|
|
30988
|
-
}
|
|
30989
|
-
)
|
|
30990
|
-
] }),
|
|
30991
|
-
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
30992
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
30993
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
30994
|
-
] }),
|
|
30995
|
-
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
30996
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
30997
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
30998
|
-
] }),
|
|
30999
|
-
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
31000
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
31001
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
31002
|
-
] })
|
|
31003
|
-
] });
|
|
31004
|
-
}
|
|
31005
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31006
|
-
"group",
|
|
31007
|
-
{
|
|
31008
|
-
position,
|
|
31009
|
-
rotation: rotationRad,
|
|
31010
|
-
onClick,
|
|
31011
|
-
onPointerOver: () => onHover?.(true),
|
|
31012
|
-
onPointerOut: () => onHover?.(false),
|
|
31013
|
-
children: [
|
|
31014
|
-
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
31015
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
31016
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31017
|
-
"meshBasicMaterial",
|
|
31018
|
-
{
|
|
31019
|
-
color: isSelected ? 16755200 : 16777215,
|
|
31020
|
-
transparent: true,
|
|
31021
|
-
opacity: 0.5
|
|
31022
|
-
}
|
|
31023
|
-
)
|
|
31024
|
-
] }),
|
|
31025
|
-
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
31026
|
-
]
|
|
31027
|
-
}
|
|
31028
|
-
);
|
|
31029
|
-
}
|
|
31030
|
-
var DEFAULT_GRID_CONFIG = {
|
|
31031
|
-
cellSize: 1,
|
|
31032
|
-
offsetX: 0,
|
|
31033
|
-
offsetZ: 0
|
|
31034
|
-
};
|
|
31035
|
-
function CameraController({
|
|
31036
|
-
onCameraChange
|
|
31037
|
-
}) {
|
|
31038
|
-
const { camera } = fiber.useThree();
|
|
31039
|
-
React87.useEffect(() => {
|
|
31040
|
-
if (onCameraChange) {
|
|
31041
|
-
onCameraChange({
|
|
31042
|
-
x: camera.position.x,
|
|
31043
|
-
y: camera.position.y,
|
|
31044
|
-
z: camera.position.z
|
|
31045
|
-
});
|
|
31046
|
-
}
|
|
31047
|
-
}, [camera.position, onCameraChange]);
|
|
31048
|
-
return null;
|
|
31049
|
-
}
|
|
31050
|
-
var GameCanvas3D = React87.forwardRef(
|
|
31051
|
-
({
|
|
31052
|
-
tiles = [],
|
|
31053
|
-
units = [],
|
|
31054
|
-
features = [],
|
|
31055
|
-
events: events2 = [],
|
|
31056
|
-
orientation = "standard",
|
|
31057
|
-
cameraMode = "isometric",
|
|
31058
|
-
showGrid = true,
|
|
31059
|
-
showCoordinates = false,
|
|
31060
|
-
showTileInfo = false,
|
|
31061
|
-
overlay = "default",
|
|
31062
|
-
shadows = true,
|
|
31063
|
-
backgroundColor = "#1a1a2e",
|
|
31064
|
-
onTileClick,
|
|
31065
|
-
onUnitClick,
|
|
31066
|
-
onFeatureClick,
|
|
31067
|
-
onCanvasClick,
|
|
31068
|
-
onTileHover,
|
|
31069
|
-
onUnitAnimation,
|
|
31070
|
-
assetLoader: customAssetLoader,
|
|
31071
|
-
tileRenderer: CustomTileRenderer,
|
|
31072
|
-
unitRenderer: CustomUnitRenderer,
|
|
31073
|
-
featureRenderer: CustomFeatureRenderer,
|
|
31074
|
-
className,
|
|
31075
|
-
isLoading: externalLoading,
|
|
31076
|
-
error: externalError,
|
|
31077
|
-
entity,
|
|
31078
|
-
preloadAssets = [],
|
|
31079
|
-
tileClickEvent,
|
|
31080
|
-
unitClickEvent,
|
|
31081
|
-
featureClickEvent,
|
|
31082
|
-
canvasClickEvent,
|
|
31083
|
-
tileHoverEvent,
|
|
31084
|
-
tileLeaveEvent,
|
|
31085
|
-
unitAnimationEvent,
|
|
31086
|
-
cameraChangeEvent,
|
|
31087
|
-
loadingMessage = "Loading 3D Scene...",
|
|
31088
|
-
useInstancing = true,
|
|
31089
|
-
validMoves = [],
|
|
31090
|
-
attackTargets = [],
|
|
31091
|
-
selectedTileIds = [],
|
|
31092
|
-
selectedUnitId = null,
|
|
31093
|
-
children
|
|
31094
|
-
}, ref) => {
|
|
31095
|
-
const containerRef = React87.useRef(null);
|
|
31096
|
-
const controlsRef = React87.useRef(null);
|
|
31097
|
-
const [hoveredTile, setHoveredTile] = React87.useState(null);
|
|
31098
|
-
const [internalError, setInternalError] = React87.useState(null);
|
|
31099
|
-
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
31100
|
-
preloadUrls: preloadAssets,
|
|
31101
|
-
loader: customAssetLoader
|
|
31102
|
-
});
|
|
31103
|
-
const eventHandlers = useGameCanvas3DEvents({
|
|
31104
|
-
tileClickEvent,
|
|
31105
|
-
unitClickEvent,
|
|
31106
|
-
featureClickEvent,
|
|
31107
|
-
canvasClickEvent,
|
|
31108
|
-
tileHoverEvent,
|
|
31109
|
-
tileLeaveEvent,
|
|
31110
|
-
unitAnimationEvent,
|
|
31111
|
-
cameraChangeEvent,
|
|
31112
|
-
onTileClick,
|
|
31113
|
-
onUnitClick,
|
|
31114
|
-
onFeatureClick,
|
|
31115
|
-
onCanvasClick,
|
|
31116
|
-
onTileHover,
|
|
31117
|
-
onUnitAnimation
|
|
31118
|
-
});
|
|
31119
|
-
const gridBounds = React87.useMemo(() => {
|
|
31120
|
-
if (tiles.length === 0) {
|
|
31121
|
-
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
31122
|
-
}
|
|
31123
|
-
const xs = tiles.map((t) => t.x);
|
|
31124
|
-
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
31125
|
-
return {
|
|
31126
|
-
minX: Math.min(...xs),
|
|
31127
|
-
maxX: Math.max(...xs),
|
|
31128
|
-
minZ: Math.min(...zs),
|
|
31129
|
-
maxZ: Math.max(...zs)
|
|
31130
|
-
};
|
|
31131
|
-
}, [tiles]);
|
|
31132
|
-
const cameraTarget = React87.useMemo(() => {
|
|
31133
|
-
return [
|
|
31134
|
-
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
31135
|
-
0,
|
|
31136
|
-
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
31137
|
-
];
|
|
31138
|
-
}, [gridBounds]);
|
|
31139
|
-
const gridConfig = React87.useMemo(
|
|
31140
|
-
() => ({
|
|
31141
|
-
...DEFAULT_GRID_CONFIG,
|
|
31142
|
-
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
31143
|
-
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
31144
|
-
}),
|
|
31145
|
-
[gridBounds]
|
|
31146
|
-
);
|
|
31147
|
-
const gridToWorld = React87.useCallback(
|
|
31148
|
-
(x, z, y = 0) => {
|
|
31149
|
-
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
31150
|
-
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
31151
|
-
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
31152
|
-
},
|
|
31153
|
-
[gridBounds, gridConfig]
|
|
31154
|
-
);
|
|
31155
|
-
React87.useImperativeHandle(ref, () => ({
|
|
31156
|
-
getCameraPosition: () => {
|
|
31157
|
-
if (controlsRef.current) {
|
|
31158
|
-
const pos = controlsRef.current.object.position;
|
|
31159
|
-
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
31160
|
-
}
|
|
31161
|
-
return null;
|
|
31162
|
-
},
|
|
31163
|
-
setCameraPosition: (x, y, z) => {
|
|
31164
|
-
if (controlsRef.current) {
|
|
31165
|
-
controlsRef.current.object.position.set(x, y, z);
|
|
31166
|
-
controlsRef.current.update();
|
|
31167
|
-
}
|
|
31168
|
-
},
|
|
31169
|
-
lookAt: (x, y, z) => {
|
|
31170
|
-
if (controlsRef.current) {
|
|
31171
|
-
controlsRef.current.target.set(x, y, z);
|
|
31172
|
-
controlsRef.current.update();
|
|
31173
|
-
}
|
|
31174
|
-
},
|
|
31175
|
-
resetCamera: () => {
|
|
31176
|
-
if (controlsRef.current) {
|
|
31177
|
-
controlsRef.current.reset();
|
|
31178
|
-
}
|
|
31179
|
-
},
|
|
31180
|
-
screenshot: () => {
|
|
31181
|
-
const canvas = containerRef.current?.querySelector("canvas");
|
|
31182
|
-
if (canvas) {
|
|
31183
|
-
return canvas.toDataURL("image/png");
|
|
31184
|
-
}
|
|
31185
|
-
return null;
|
|
31186
|
-
},
|
|
31187
|
-
export: () => ({
|
|
31188
|
-
tiles,
|
|
31189
|
-
units,
|
|
31190
|
-
features
|
|
31191
|
-
})
|
|
31192
|
-
}));
|
|
31193
|
-
const handleTileClick = React87.useCallback(
|
|
31194
|
-
(tile, event) => {
|
|
31195
|
-
eventHandlers.handleTileClick(tile, event);
|
|
31196
|
-
},
|
|
31197
|
-
[eventHandlers]
|
|
31198
|
-
);
|
|
31199
|
-
const handleUnitClick = React87.useCallback(
|
|
31200
|
-
(unit, event) => {
|
|
31201
|
-
eventHandlers.handleUnitClick(unit, event);
|
|
31202
|
-
},
|
|
31203
|
-
[eventHandlers]
|
|
31204
|
-
);
|
|
31205
|
-
const handleFeatureClick = React87.useCallback(
|
|
31206
|
-
(feature, event) => {
|
|
31207
|
-
if (event) {
|
|
31208
|
-
eventHandlers.handleFeatureClick(feature, event);
|
|
31209
|
-
}
|
|
31210
|
-
},
|
|
31211
|
-
[eventHandlers]
|
|
31212
|
-
);
|
|
31213
|
-
const handleTileHover = React87.useCallback(
|
|
31214
|
-
(tile, event) => {
|
|
31215
|
-
setHoveredTile(tile);
|
|
31216
|
-
if (event) {
|
|
31217
|
-
eventHandlers.handleTileHover(tile, event);
|
|
31218
|
-
}
|
|
31219
|
-
},
|
|
31220
|
-
[eventHandlers]
|
|
31221
|
-
);
|
|
31222
|
-
const cameraConfig = React87.useMemo(() => {
|
|
31223
|
-
const size = Math.max(
|
|
31224
|
-
gridBounds.maxX - gridBounds.minX,
|
|
31225
|
-
gridBounds.maxZ - gridBounds.minZ
|
|
31226
|
-
);
|
|
31227
|
-
const distance = size * 1.5;
|
|
31228
|
-
switch (cameraMode) {
|
|
31229
|
-
case "isometric":
|
|
31230
|
-
return {
|
|
31231
|
-
position: [distance, distance * 0.8, distance],
|
|
31232
|
-
fov: 45
|
|
31233
|
-
};
|
|
31234
|
-
case "top-down":
|
|
31235
|
-
return {
|
|
31236
|
-
position: [0, distance * 2, 0],
|
|
31237
|
-
fov: 45
|
|
31238
|
-
};
|
|
31239
|
-
case "perspective":
|
|
31240
|
-
default:
|
|
31241
|
-
return {
|
|
31242
|
-
position: [distance, distance, distance],
|
|
31243
|
-
fov: 45
|
|
31244
|
-
};
|
|
31245
|
-
}
|
|
31246
|
-
}, [cameraMode, gridBounds]);
|
|
31247
|
-
const DefaultTileRenderer = React87.useCallback(
|
|
31248
|
-
({ tile, position }) => {
|
|
31249
|
-
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
31250
|
-
const isHovered = hoveredTile?.id === tile.id;
|
|
31251
|
-
const isValidMove = validMoves.some(
|
|
31252
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
31253
|
-
);
|
|
31254
|
-
const isAttackTarget = attackTargets.some(
|
|
31255
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
31256
|
-
);
|
|
31257
|
-
let color = 8421504;
|
|
31258
|
-
if (tile.type === "water") color = 4491468;
|
|
31259
|
-
else if (tile.type === "grass") color = 4500036;
|
|
31260
|
-
else if (tile.type === "sand") color = 14535816;
|
|
31261
|
-
else if (tile.type === "rock") color = 8947848;
|
|
31262
|
-
else if (tile.type === "snow") color = 15658734;
|
|
31263
|
-
let emissive = 0;
|
|
31264
|
-
if (isSelected) emissive = 4473924;
|
|
31265
|
-
else if (isAttackTarget) emissive = 4456448;
|
|
31266
|
-
else if (isValidMove) emissive = 17408;
|
|
31267
|
-
else if (isHovered) emissive = 2236962;
|
|
31268
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31269
|
-
"mesh",
|
|
31270
|
-
{
|
|
31271
|
-
position,
|
|
31272
|
-
onClick: (e) => handleTileClick(tile, e),
|
|
31273
|
-
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
31274
|
-
onPointerLeave: (e) => handleTileHover(null, e),
|
|
31275
|
-
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
31276
|
-
children: [
|
|
31277
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
31278
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
31279
|
-
]
|
|
31280
|
-
}
|
|
31281
|
-
);
|
|
31282
|
-
},
|
|
31283
|
-
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
31284
|
-
);
|
|
31285
|
-
const DefaultUnitRenderer = React87.useCallback(
|
|
31286
|
-
({ unit, position }) => {
|
|
31287
|
-
const isSelected = selectedUnitId === unit.id;
|
|
31288
|
-
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
31289
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31290
|
-
"group",
|
|
31291
|
-
{
|
|
31292
|
-
position,
|
|
31293
|
-
onClick: (e) => handleUnitClick(unit, e),
|
|
31294
|
-
userData: { type: "unit", unitId: unit.id },
|
|
31295
|
-
children: [
|
|
31296
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
31297
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
31298
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
31299
|
-
] }),
|
|
31300
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
31301
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
31302
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
31303
|
-
] }),
|
|
31304
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
31305
|
-
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
31306
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
31307
|
-
] }),
|
|
31308
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
31309
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
31310
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
31311
|
-
] }),
|
|
31312
|
-
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
31313
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
31314
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
31315
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
31316
|
-
] }),
|
|
31317
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
31318
|
-
"mesh",
|
|
31319
|
-
{
|
|
31320
|
-
position: [
|
|
31321
|
-
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
31322
|
-
0,
|
|
31323
|
-
0.01
|
|
31324
|
-
],
|
|
31325
|
-
children: [
|
|
31326
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
31327
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31328
|
-
"meshBasicMaterial",
|
|
31329
|
-
{
|
|
31330
|
-
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
31331
|
-
}
|
|
31332
|
-
)
|
|
31333
|
-
]
|
|
31334
|
-
}
|
|
31335
|
-
)
|
|
31336
|
-
] })
|
|
31337
|
-
]
|
|
31338
|
-
}
|
|
31339
|
-
);
|
|
31340
|
-
},
|
|
31341
|
-
[selectedUnitId, handleUnitClick]
|
|
31342
|
-
);
|
|
31343
|
-
const DefaultFeatureRenderer = React87.useCallback(
|
|
31344
|
-
({
|
|
31345
|
-
feature,
|
|
31346
|
-
position
|
|
31347
|
-
}) => {
|
|
31348
|
-
if (feature.assetUrl) {
|
|
31349
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31350
|
-
ModelLoader,
|
|
31351
|
-
{
|
|
31352
|
-
url: feature.assetUrl,
|
|
31353
|
-
position,
|
|
31354
|
-
scale: 0.5,
|
|
31355
|
-
rotation: [0, feature.rotation ?? 0, 0],
|
|
31356
|
-
onClick: () => handleFeatureClick(feature, null),
|
|
31357
|
-
fallbackGeometry: "box"
|
|
31358
|
-
},
|
|
31359
|
-
feature.id
|
|
31360
|
-
);
|
|
31361
|
-
}
|
|
31362
|
-
if (feature.type === "tree") {
|
|
31363
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31364
|
-
"group",
|
|
31365
|
-
{
|
|
31366
|
-
position,
|
|
31367
|
-
onClick: (e) => handleFeatureClick(feature, e),
|
|
31368
|
-
userData: { type: "feature", featureId: feature.id },
|
|
31369
|
-
children: [
|
|
31370
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
31371
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
31372
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
31373
|
-
] }),
|
|
31374
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
31375
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
31376
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
31377
|
-
] })
|
|
31378
|
-
]
|
|
31379
|
-
}
|
|
31380
|
-
);
|
|
31381
|
-
}
|
|
31382
|
-
if (feature.type === "rock") {
|
|
31383
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31384
|
-
"mesh",
|
|
31385
|
-
{
|
|
31386
|
-
position: [position[0], position[1] + 0.3, position[2]],
|
|
31387
|
-
onClick: (e) => handleFeatureClick(feature, e),
|
|
31388
|
-
userData: { type: "feature", featureId: feature.id },
|
|
31389
|
-
children: [
|
|
31390
|
-
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
31391
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
31392
|
-
]
|
|
31393
|
-
}
|
|
31394
|
-
);
|
|
31395
|
-
}
|
|
31396
|
-
return null;
|
|
31397
|
-
},
|
|
31398
|
-
[handleFeatureClick]
|
|
31399
|
-
);
|
|
31400
|
-
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
31401
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31402
|
-
Canvas3DLoadingState,
|
|
31403
|
-
{
|
|
31404
|
-
progress,
|
|
31405
|
-
loaded,
|
|
31406
|
-
total,
|
|
31407
|
-
message: loadingMessage,
|
|
31408
|
-
className
|
|
31409
|
-
}
|
|
31410
|
-
);
|
|
31411
|
-
}
|
|
31412
|
-
const displayError = externalError || internalError;
|
|
31413
|
-
if (displayError) {
|
|
31414
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
31415
|
-
"Error: ",
|
|
31416
|
-
displayError
|
|
31417
|
-
] }) }) });
|
|
31418
|
-
}
|
|
31419
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31420
|
-
Canvas3DErrorBoundary,
|
|
31421
|
-
{
|
|
31422
|
-
onError: (err) => setInternalError(err.message),
|
|
31423
|
-
onReset: () => setInternalError(null),
|
|
31424
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31425
|
-
"div",
|
|
31426
|
-
{
|
|
31427
|
-
ref: containerRef,
|
|
31428
|
-
className: `game-canvas-3d ${className || ""}`,
|
|
31429
|
-
"data-orientation": orientation,
|
|
31430
|
-
"data-camera-mode": cameraMode,
|
|
31431
|
-
"data-overlay": overlay,
|
|
31432
|
-
children: [
|
|
31433
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
31434
|
-
fiber.Canvas,
|
|
31435
|
-
{
|
|
31436
|
-
shadows,
|
|
31437
|
-
camera: {
|
|
31438
|
-
position: cameraConfig.position,
|
|
31439
|
-
fov: cameraConfig.fov,
|
|
31440
|
-
near: 0.1,
|
|
31441
|
-
far: 1e3
|
|
31442
|
-
},
|
|
31443
|
-
style: { background: backgroundColor },
|
|
31444
|
-
onClick: (e) => {
|
|
31445
|
-
if (e.target === e.currentTarget) {
|
|
31446
|
-
eventHandlers.handleCanvasClick(e);
|
|
31447
|
-
}
|
|
31448
|
-
},
|
|
31449
|
-
children: [
|
|
31450
|
-
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
31451
|
-
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
31452
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31453
|
-
"directionalLight",
|
|
31454
|
-
{
|
|
31455
|
-
position: [10, 20, 10],
|
|
31456
|
-
intensity: 0.8,
|
|
31457
|
-
castShadow: shadows,
|
|
31458
|
-
"shadow-mapSize": [2048, 2048]
|
|
31459
|
-
}
|
|
31460
|
-
),
|
|
31461
|
-
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
31462
|
-
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
31463
|
-
drei.Grid,
|
|
31464
|
-
{
|
|
31465
|
-
args: [
|
|
31466
|
-
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
31467
|
-
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
31468
|
-
],
|
|
31469
|
-
position: [
|
|
31470
|
-
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
31471
|
-
0,
|
|
31472
|
-
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
31473
|
-
],
|
|
31474
|
-
cellSize: 1,
|
|
31475
|
-
cellThickness: 1,
|
|
31476
|
-
cellColor: "#444444",
|
|
31477
|
-
sectionSize: 5,
|
|
31478
|
-
sectionThickness: 1.5,
|
|
31479
|
-
sectionColor: "#666666",
|
|
31480
|
-
fadeDistance: 50,
|
|
31481
|
-
fadeStrength: 1
|
|
31482
|
-
}
|
|
31483
|
-
),
|
|
31484
|
-
tiles.map((tile, index) => {
|
|
31485
|
-
const position = gridToWorld(
|
|
31486
|
-
tile.x,
|
|
31487
|
-
tile.z ?? tile.y ?? 0,
|
|
31488
|
-
tile.elevation ?? 0
|
|
31489
|
-
);
|
|
31490
|
-
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
31491
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
31492
|
-
}),
|
|
31493
|
-
features.map((feature, index) => {
|
|
31494
|
-
const position = gridToWorld(
|
|
31495
|
-
feature.x,
|
|
31496
|
-
feature.z ?? feature.y ?? 0,
|
|
31497
|
-
(feature.elevation ?? 0) + 0.5
|
|
31498
|
-
);
|
|
31499
|
-
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
31500
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
31501
|
-
}),
|
|
31502
|
-
units.map((unit) => {
|
|
31503
|
-
const position = gridToWorld(
|
|
31504
|
-
unit.x ?? 0,
|
|
31505
|
-
unit.z ?? unit.y ?? 0,
|
|
31506
|
-
(unit.elevation ?? 0) + 0.5
|
|
31507
|
-
);
|
|
31508
|
-
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
31509
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
31510
|
-
}),
|
|
31511
|
-
children,
|
|
31512
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31513
|
-
drei.OrbitControls,
|
|
31514
|
-
{
|
|
31515
|
-
ref: controlsRef,
|
|
31516
|
-
target: cameraTarget,
|
|
31517
|
-
enableDamping: true,
|
|
31518
|
-
dampingFactor: 0.05,
|
|
31519
|
-
minDistance: 2,
|
|
31520
|
-
maxDistance: 100,
|
|
31521
|
-
maxPolarAngle: Math.PI / 2 - 0.1
|
|
31522
|
-
}
|
|
31523
|
-
)
|
|
31524
|
-
]
|
|
31525
|
-
}
|
|
31526
|
-
),
|
|
31527
|
-
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
31528
|
-
"X: ",
|
|
31529
|
-
hoveredTile.x,
|
|
31530
|
-
", Z: ",
|
|
31531
|
-
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
31532
|
-
] }),
|
|
31533
|
-
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
31534
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
31535
|
-
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
31536
|
-
] })
|
|
31537
|
-
]
|
|
31538
|
-
}
|
|
31539
|
-
)
|
|
29725
|
+
Box,
|
|
29726
|
+
{
|
|
29727
|
+
className: "w-full overflow-auto p-4 font-mono text-sm",
|
|
29728
|
+
style: { height, fontSize: `${zoom}%` },
|
|
29729
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "whitespace-pre-wrap break-words", children: activeContent })
|
|
31540
29730
|
}
|
|
31541
29731
|
);
|
|
31542
|
-
}
|
|
31543
|
-
)
|
|
31544
|
-
|
|
31545
|
-
|
|
31546
|
-
entity,
|
|
31547
|
-
cameraMode = "perspective",
|
|
31548
|
-
showGrid = true,
|
|
31549
|
-
shadows = true,
|
|
31550
|
-
backgroundColor = "#2a1a1a",
|
|
31551
|
-
tileClickEvent,
|
|
31552
|
-
unitClickEvent,
|
|
31553
|
-
unitAttackEvent,
|
|
31554
|
-
unitMoveEvent,
|
|
31555
|
-
endTurnEvent,
|
|
31556
|
-
exitEvent,
|
|
31557
|
-
selectedUnitId,
|
|
31558
|
-
validMoves,
|
|
31559
|
-
attackTargets,
|
|
31560
|
-
className
|
|
31561
|
-
}) {
|
|
31562
|
-
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
31563
|
-
if (!resolved) return null;
|
|
31564
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("game-canvas-3d-battle-template", className), children: [
|
|
31565
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31566
|
-
GameCanvas3D,
|
|
29732
|
+
};
|
|
29733
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", children: [
|
|
29734
|
+
tabItems && tabItems.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
29735
|
+
Tabs,
|
|
31567
29736
|
{
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
|
|
31572
|
-
|
|
31573
|
-
|
|
31574
|
-
showTileInfo: false,
|
|
31575
|
-
shadows,
|
|
31576
|
-
backgroundColor,
|
|
31577
|
-
tileClickEvent,
|
|
31578
|
-
unitClickEvent,
|
|
31579
|
-
selectedUnitId,
|
|
31580
|
-
validMoves,
|
|
31581
|
-
attackTargets,
|
|
31582
|
-
className: "game-canvas-3d-battle-template__canvas"
|
|
29737
|
+
tabs: tabItems,
|
|
29738
|
+
activeTab: `doc-${activeDocIndex}`,
|
|
29739
|
+
onTabChange: (id) => {
|
|
29740
|
+
const idx = parseInt(id.replace("doc-", ""), 10);
|
|
29741
|
+
setActiveDocIndex(idx);
|
|
29742
|
+
}
|
|
31583
29743
|
}
|
|
31584
|
-
),
|
|
31585
|
-
|
|
29744
|
+
) }),
|
|
29745
|
+
showToolbar && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
31586
29746
|
HStack,
|
|
31587
29747
|
{
|
|
31588
29748
|
gap: "sm",
|
|
31589
29749
|
align: "center",
|
|
31590
|
-
|
|
29750
|
+
justify: "between",
|
|
29751
|
+
className: "px-4 py-2 border-b border-[var(--color-border)] bg-[var(--color-muted)]/30",
|
|
31591
29752
|
children: [
|
|
31592
|
-
/* @__PURE__ */ jsxRuntime.
|
|
31593
|
-
|
|
31594
|
-
"
|
|
31595
|
-
|
|
29753
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", children: [
|
|
29754
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons.FileText, size: "sm", className: "text-[var(--color-muted-foreground)]" }),
|
|
29755
|
+
title && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "truncate max-w-[200px]", children: title })
|
|
29756
|
+
] }),
|
|
29757
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
29758
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ZoomOut, onClick: handleZoomOut }),
|
|
29759
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", className: "tabular-nums w-10 text-center", children: [
|
|
29760
|
+
zoom,
|
|
29761
|
+
"%"
|
|
29762
|
+
] }),
|
|
29763
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ZoomIn, onClick: handleZoomIn }),
|
|
29764
|
+
totalPages && totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
29765
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-px h-4 bg-[var(--color-border)] mx-1" }),
|
|
29766
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ChevronLeft, onClick: handlePagePrev, disabled: currentPage <= 1 }),
|
|
29767
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: [
|
|
29768
|
+
currentPage,
|
|
29769
|
+
" / ",
|
|
29770
|
+
totalPages
|
|
29771
|
+
] }),
|
|
29772
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.ChevronRight, onClick: handlePageNext, disabled: currentPage >= totalPages })
|
|
29773
|
+
] }),
|
|
29774
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-px h-4 bg-[var(--color-border)] mx-1" }),
|
|
29775
|
+
showDownload && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.Download, onClick: handleDownload }),
|
|
29776
|
+
showPrint && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", icon: LucideIcons.Printer, onClick: handlePrint }),
|
|
29777
|
+
actions?.map((action, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
29778
|
+
Badge,
|
|
29779
|
+
{
|
|
29780
|
+
variant: "default",
|
|
29781
|
+
className: "cursor-pointer hover:opacity-80 transition-opacity",
|
|
29782
|
+
onClick: () => handleAction(action),
|
|
29783
|
+
children: action.label
|
|
29784
|
+
},
|
|
29785
|
+
idx
|
|
29786
|
+
))
|
|
31596
29787
|
] })
|
|
31597
29788
|
]
|
|
31598
29789
|
}
|
|
31599
|
-
)
|
|
31600
|
-
] });
|
|
31601
|
-
}
|
|
31602
|
-
GameCanvas3DBattleTemplate.displayName = "GameCanvas3DBattleTemplate";
|
|
31603
|
-
function GameCanvas3DCastleTemplate({
|
|
31604
|
-
entity,
|
|
31605
|
-
cameraMode = "isometric",
|
|
31606
|
-
showGrid = true,
|
|
31607
|
-
shadows = true,
|
|
31608
|
-
backgroundColor = "#1e1e2e",
|
|
31609
|
-
buildingClickEvent,
|
|
31610
|
-
unitClickEvent,
|
|
31611
|
-
buildEvent,
|
|
31612
|
-
recruitEvent,
|
|
31613
|
-
exitEvent,
|
|
31614
|
-
selectedBuildingId,
|
|
31615
|
-
selectedTileIds = [],
|
|
31616
|
-
availableBuildSites,
|
|
31617
|
-
showHeader = true,
|
|
31618
|
-
className
|
|
31619
|
-
}) {
|
|
31620
|
-
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
31621
|
-
if (!resolved) return null;
|
|
31622
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-canvas-3d-castle-template", className), children: [
|
|
31623
|
-
showHeader && resolved.name && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", align: "center", className: "castle-template__header", children: [
|
|
31624
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", className: "header__name", children: resolved.name }),
|
|
31625
|
-
resolved.level && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "header__level", children: [
|
|
31626
|
-
"Level ",
|
|
31627
|
-
resolved.level
|
|
31628
|
-
] }),
|
|
31629
|
-
resolved.owner && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "header__owner", children: resolved.owner })
|
|
31630
|
-
] }),
|
|
31631
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
31632
|
-
GameCanvas3D,
|
|
31633
|
-
{
|
|
31634
|
-
tiles: resolved.tiles,
|
|
31635
|
-
units: resolved.units,
|
|
31636
|
-
features: resolved.features,
|
|
31637
|
-
cameraMode,
|
|
31638
|
-
showGrid,
|
|
31639
|
-
showCoordinates: false,
|
|
31640
|
-
showTileInfo: false,
|
|
31641
|
-
shadows,
|
|
31642
|
-
backgroundColor,
|
|
31643
|
-
featureClickEvent: buildingClickEvent,
|
|
31644
|
-
unitClickEvent,
|
|
31645
|
-
selectedTileIds,
|
|
31646
|
-
validMoves: availableBuildSites,
|
|
31647
|
-
className: "game-canvas-3d-castle-template__canvas"
|
|
31648
|
-
}
|
|
31649
29790
|
),
|
|
31650
|
-
|
|
31651
|
-
|
|
31652
|
-
|
|
31653
|
-
|
|
31654
|
-
|
|
31655
|
-
|
|
31656
|
-
|
|
31657
|
-
|
|
29791
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-full overflow-hidden bg-[var(--color-muted)]/20", children: renderContent() })
|
|
29792
|
+
] }) });
|
|
29793
|
+
};
|
|
29794
|
+
DocumentViewer.displayName = "DocumentViewer";
|
|
29795
|
+
function extractTitle(children) {
|
|
29796
|
+
if (!React87__namespace.default.isValidElement(children)) return void 0;
|
|
29797
|
+
const props = children.props;
|
|
29798
|
+
if (typeof props.title === "string") {
|
|
29799
|
+
return props.title;
|
|
29800
|
+
}
|
|
29801
|
+
return void 0;
|
|
31658
29802
|
}
|
|
31659
|
-
|
|
31660
|
-
|
|
31661
|
-
|
|
31662
|
-
|
|
31663
|
-
|
|
31664
|
-
showCoordinates = true,
|
|
31665
|
-
showTileInfo = true,
|
|
31666
|
-
shadows = true,
|
|
31667
|
-
backgroundColor = "#1a1a2e",
|
|
31668
|
-
tileClickEvent,
|
|
31669
|
-
unitClickEvent,
|
|
31670
|
-
featureClickEvent,
|
|
31671
|
-
tileHoverEvent,
|
|
31672
|
-
tileLeaveEvent,
|
|
31673
|
-
cameraChangeEvent,
|
|
31674
|
-
selectedUnitId,
|
|
31675
|
-
validMoves,
|
|
31676
|
-
attackTargets,
|
|
29803
|
+
var DrawerSlot = ({
|
|
29804
|
+
children,
|
|
29805
|
+
title: overrideTitle,
|
|
29806
|
+
position = "right",
|
|
29807
|
+
size = "md",
|
|
31677
29808
|
className
|
|
31678
|
-
}) {
|
|
31679
|
-
const
|
|
31680
|
-
|
|
29809
|
+
}) => {
|
|
29810
|
+
const eventBus = useEventBus();
|
|
29811
|
+
const isOpen = Boolean(children);
|
|
29812
|
+
const title = overrideTitle || extractTitle(children);
|
|
29813
|
+
const handleClose = () => {
|
|
29814
|
+
eventBus.emit("UI:CLOSE");
|
|
29815
|
+
eventBus.emit("UI:CANCEL");
|
|
29816
|
+
};
|
|
29817
|
+
if (!isOpen) return null;
|
|
31681
29818
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31682
|
-
|
|
29819
|
+
Drawer,
|
|
31683
29820
|
{
|
|
31684
|
-
|
|
31685
|
-
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
|
|
31689
|
-
|
|
31690
|
-
|
|
31691
|
-
shadows,
|
|
31692
|
-
backgroundColor,
|
|
31693
|
-
tileClickEvent,
|
|
31694
|
-
unitClickEvent,
|
|
31695
|
-
featureClickEvent,
|
|
31696
|
-
tileHoverEvent,
|
|
31697
|
-
tileLeaveEvent,
|
|
31698
|
-
cameraChangeEvent,
|
|
31699
|
-
selectedUnitId,
|
|
31700
|
-
validMoves,
|
|
31701
|
-
attackTargets,
|
|
31702
|
-
className
|
|
29821
|
+
isOpen,
|
|
29822
|
+
onClose: handleClose,
|
|
29823
|
+
title,
|
|
29824
|
+
position,
|
|
29825
|
+
width: size,
|
|
29826
|
+
className,
|
|
29827
|
+
children
|
|
31703
29828
|
}
|
|
31704
29829
|
);
|
|
31705
|
-
}
|
|
31706
|
-
|
|
29830
|
+
};
|
|
29831
|
+
DrawerSlot.displayName = "DrawerSlot";
|
|
31707
29832
|
var GameShell = ({
|
|
31708
29833
|
appName = "Game",
|
|
31709
29834
|
hud,
|
|
@@ -32220,55 +30345,6 @@ var GraphCanvas = ({
|
|
|
32220
30345
|
] }) });
|
|
32221
30346
|
};
|
|
32222
30347
|
GraphCanvas.displayName = "GraphCanvas";
|
|
32223
|
-
function Lighting3D({
|
|
32224
|
-
ambientIntensity = 0.6,
|
|
32225
|
-
ambientColor = "#ffffff",
|
|
32226
|
-
directionalIntensity = 0.8,
|
|
32227
|
-
directionalColor = "#ffffff",
|
|
32228
|
-
directionalPosition = [10, 20, 10],
|
|
32229
|
-
shadows = true,
|
|
32230
|
-
shadowMapSize = 2048,
|
|
32231
|
-
shadowCameraSize = 20,
|
|
32232
|
-
showHelpers = false
|
|
32233
|
-
}) {
|
|
32234
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
32235
|
-
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: ambientIntensity, color: ambientColor }),
|
|
32236
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32237
|
-
"directionalLight",
|
|
32238
|
-
{
|
|
32239
|
-
position: directionalPosition,
|
|
32240
|
-
intensity: directionalIntensity,
|
|
32241
|
-
color: directionalColor,
|
|
32242
|
-
castShadow: shadows,
|
|
32243
|
-
"shadow-mapSize": [shadowMapSize, shadowMapSize],
|
|
32244
|
-
"shadow-camera-left": -shadowCameraSize,
|
|
32245
|
-
"shadow-camera-right": shadowCameraSize,
|
|
32246
|
-
"shadow-camera-top": shadowCameraSize,
|
|
32247
|
-
"shadow-camera-bottom": -shadowCameraSize,
|
|
32248
|
-
"shadow-camera-near": 0.1,
|
|
32249
|
-
"shadow-camera-far": 100,
|
|
32250
|
-
"shadow-bias": -1e-3
|
|
32251
|
-
}
|
|
32252
|
-
),
|
|
32253
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32254
|
-
"hemisphereLight",
|
|
32255
|
-
{
|
|
32256
|
-
intensity: 0.3,
|
|
32257
|
-
color: "#87ceeb",
|
|
32258
|
-
groundColor: "#362d1d"
|
|
32259
|
-
}
|
|
32260
|
-
),
|
|
32261
|
-
showHelpers && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
32262
|
-
"directionalLightHelper",
|
|
32263
|
-
{
|
|
32264
|
-
args: [
|
|
32265
|
-
new THREE__namespace.DirectionalLight(directionalColor, directionalIntensity),
|
|
32266
|
-
5
|
|
32267
|
-
]
|
|
32268
|
-
}
|
|
32269
|
-
) })
|
|
32270
|
-
] });
|
|
32271
|
-
}
|
|
32272
30348
|
var COLUMN_CLASSES = {
|
|
32273
30349
|
2: "grid-cols-2",
|
|
32274
30350
|
3: "grid-cols-2 sm:grid-cols-3",
|
|
@@ -32511,92 +30587,6 @@ var ModalSlot = ({
|
|
|
32511
30587
|
);
|
|
32512
30588
|
};
|
|
32513
30589
|
ModalSlot.displayName = "ModalSlot";
|
|
32514
|
-
function PhysicsObject3D({
|
|
32515
|
-
entityId,
|
|
32516
|
-
modelUrl,
|
|
32517
|
-
initialPosition = [0, 0, 0],
|
|
32518
|
-
initialVelocity = [0, 0, 0],
|
|
32519
|
-
mass = 1,
|
|
32520
|
-
gravity = 9.8,
|
|
32521
|
-
groundY = 0,
|
|
32522
|
-
scale = 1,
|
|
32523
|
-
onPhysicsUpdate,
|
|
32524
|
-
onGroundHit,
|
|
32525
|
-
onCollision
|
|
32526
|
-
}) {
|
|
32527
|
-
const groupRef = React87.useRef(null);
|
|
32528
|
-
const physicsStateRef = React87.useRef({
|
|
32529
|
-
id: entityId,
|
|
32530
|
-
x: initialPosition[0],
|
|
32531
|
-
y: initialPosition[1],
|
|
32532
|
-
z: initialPosition[2],
|
|
32533
|
-
vx: initialVelocity[0],
|
|
32534
|
-
vy: initialVelocity[1],
|
|
32535
|
-
vz: initialVelocity[2],
|
|
32536
|
-
rx: 0,
|
|
32537
|
-
ry: 0,
|
|
32538
|
-
rz: 0,
|
|
32539
|
-
isGrounded: false,
|
|
32540
|
-
gravity,
|
|
32541
|
-
friction: 0.8,
|
|
32542
|
-
mass,
|
|
32543
|
-
state: "Active"
|
|
32544
|
-
});
|
|
32545
|
-
const groundHitRef = React87.useRef(false);
|
|
32546
|
-
React87.useEffect(() => {
|
|
32547
|
-
if (groupRef.current) {
|
|
32548
|
-
groupRef.current.position.set(
|
|
32549
|
-
initialPosition[0],
|
|
32550
|
-
initialPosition[1],
|
|
32551
|
-
initialPosition[2]
|
|
32552
|
-
);
|
|
32553
|
-
}
|
|
32554
|
-
}, []);
|
|
32555
|
-
fiber.useFrame((state, delta) => {
|
|
32556
|
-
const physics = physicsStateRef.current;
|
|
32557
|
-
if (physics.state !== "Active") return;
|
|
32558
|
-
const dt = Math.min(delta, 0.1);
|
|
32559
|
-
if (!physics.isGrounded) {
|
|
32560
|
-
physics.vy -= physics.gravity * dt;
|
|
32561
|
-
}
|
|
32562
|
-
physics.x += physics.vx * dt;
|
|
32563
|
-
physics.y += physics.vy * dt;
|
|
32564
|
-
physics.z += physics.vz * dt;
|
|
32565
|
-
const airResistance = Math.pow(0.99, dt * 60);
|
|
32566
|
-
physics.vx *= airResistance;
|
|
32567
|
-
physics.vz *= airResistance;
|
|
32568
|
-
if (physics.y <= groundY) {
|
|
32569
|
-
physics.y = groundY;
|
|
32570
|
-
if (!physics.isGrounded) {
|
|
32571
|
-
physics.isGrounded = true;
|
|
32572
|
-
groundHitRef.current = true;
|
|
32573
|
-
physics.vx *= physics.friction;
|
|
32574
|
-
physics.vz *= physics.friction;
|
|
32575
|
-
onGroundHit?.();
|
|
32576
|
-
}
|
|
32577
|
-
physics.vy = 0;
|
|
32578
|
-
} else {
|
|
32579
|
-
physics.isGrounded = false;
|
|
32580
|
-
}
|
|
32581
|
-
if (groupRef.current) {
|
|
32582
|
-
groupRef.current.position.set(physics.x, physics.y, physics.z);
|
|
32583
|
-
if (!physics.isGrounded) {
|
|
32584
|
-
physics.rx += physics.vz * dt * 0.5;
|
|
32585
|
-
physics.rz -= physics.vx * dt * 0.5;
|
|
32586
|
-
groupRef.current.rotation.set(physics.rx, physics.ry, physics.rz);
|
|
32587
|
-
}
|
|
32588
|
-
}
|
|
32589
|
-
onPhysicsUpdate?.({ ...physics });
|
|
32590
|
-
});
|
|
32591
|
-
const scaleArray = typeof scale === "number" ? [scale, scale, scale] : scale;
|
|
32592
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { ref: groupRef, scale: scaleArray, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
32593
|
-
ModelLoader,
|
|
32594
|
-
{
|
|
32595
|
-
url: modelUrl,
|
|
32596
|
-
fallbackGeometry: "box"
|
|
32597
|
-
}
|
|
32598
|
-
) });
|
|
32599
|
-
}
|
|
32600
30590
|
|
|
32601
30591
|
// lib/traitRegistry.ts
|
|
32602
30592
|
var traits = /* @__PURE__ */ new Map();
|
|
@@ -33744,30 +31734,6 @@ function RuntimeDebugger({
|
|
|
33744
31734
|
);
|
|
33745
31735
|
}
|
|
33746
31736
|
RuntimeDebugger.displayName = "RuntimeDebugger";
|
|
33747
|
-
function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
33748
|
-
const { scene } = fiber.useThree();
|
|
33749
|
-
const initializedRef = React87.useRef(false);
|
|
33750
|
-
React87.useEffect(() => {
|
|
33751
|
-
if (initializedRef.current) return;
|
|
33752
|
-
initializedRef.current = true;
|
|
33753
|
-
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
33754
|
-
scene.background = new THREE__namespace.Color(background);
|
|
33755
|
-
} else {
|
|
33756
|
-
const loader = new THREE__namespace.TextureLoader();
|
|
33757
|
-
loader.load(background, (texture) => {
|
|
33758
|
-
scene.background = texture;
|
|
33759
|
-
});
|
|
33760
|
-
}
|
|
33761
|
-
if (fog) {
|
|
33762
|
-
scene.fog = new THREE__namespace.Fog(fog.color, fog.near, fog.far);
|
|
33763
|
-
}
|
|
33764
|
-
return () => {
|
|
33765
|
-
scene.background = null;
|
|
33766
|
-
scene.fog = null;
|
|
33767
|
-
};
|
|
33768
|
-
}, [scene, background, fog]);
|
|
33769
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
33770
|
-
}
|
|
33771
31737
|
var SignaturePad = ({
|
|
33772
31738
|
label = "Signature",
|
|
33773
31739
|
helperText = "Draw your signature above",
|
|
@@ -33944,174 +31910,6 @@ var SignaturePad = ({
|
|
|
33944
31910
|
] }) });
|
|
33945
31911
|
};
|
|
33946
31912
|
SignaturePad.displayName = "SignaturePad";
|
|
33947
|
-
var DEFAULT_TERRAIN_COLORS = {
|
|
33948
|
-
grass: "#44aa44",
|
|
33949
|
-
dirt: "#8b7355",
|
|
33950
|
-
sand: "#ddcc88",
|
|
33951
|
-
water: "#4488cc",
|
|
33952
|
-
rock: "#888888",
|
|
33953
|
-
snow: "#eeeeee",
|
|
33954
|
-
forest: "#228b22",
|
|
33955
|
-
desert: "#d4a574",
|
|
33956
|
-
mountain: "#696969",
|
|
33957
|
-
swamp: "#556b2f"
|
|
33958
|
-
};
|
|
33959
|
-
function TileRenderer({
|
|
33960
|
-
tiles,
|
|
33961
|
-
cellSize = 1,
|
|
33962
|
-
offsetX = 0,
|
|
33963
|
-
offsetZ = 0,
|
|
33964
|
-
useInstancing = true,
|
|
33965
|
-
terrainColors = DEFAULT_TERRAIN_COLORS,
|
|
33966
|
-
onTileClick,
|
|
33967
|
-
onTileHover,
|
|
33968
|
-
selectedTileIds = [],
|
|
33969
|
-
validMoves = [],
|
|
33970
|
-
attackTargets = []
|
|
33971
|
-
}) {
|
|
33972
|
-
const meshRef = React87.useRef(null);
|
|
33973
|
-
const geometry = React87.useMemo(() => {
|
|
33974
|
-
return new THREE__namespace.BoxGeometry(cellSize * 0.95, 0.2, cellSize * 0.95);
|
|
33975
|
-
}, [cellSize]);
|
|
33976
|
-
const material = React87.useMemo(() => {
|
|
33977
|
-
return new THREE__namespace.MeshStandardMaterial({
|
|
33978
|
-
roughness: 0.8,
|
|
33979
|
-
metalness: 0.1
|
|
33980
|
-
});
|
|
33981
|
-
}, []);
|
|
33982
|
-
const { positions, colors, tileMap } = React87.useMemo(() => {
|
|
33983
|
-
const pos = [];
|
|
33984
|
-
const cols = [];
|
|
33985
|
-
const map = /* @__PURE__ */ new Map();
|
|
33986
|
-
tiles.forEach((tile) => {
|
|
33987
|
-
const x = (tile.x - offsetX) * cellSize;
|
|
33988
|
-
const z = ((tile.z ?? tile.y ?? 0) - offsetZ) * cellSize;
|
|
33989
|
-
const y = (tile.elevation ?? 0) * 0.1;
|
|
33990
|
-
pos.push(new THREE__namespace.Vector3(x, y, z));
|
|
33991
|
-
const colorHex = terrainColors[tile.type || ""] || terrainColors[tile.terrain || ""] || "#808080";
|
|
33992
|
-
const color = new THREE__namespace.Color(colorHex);
|
|
33993
|
-
const isValidMove = validMoves.some(
|
|
33994
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
33995
|
-
);
|
|
33996
|
-
const isAttackTarget = attackTargets.some(
|
|
33997
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
33998
|
-
);
|
|
33999
|
-
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
34000
|
-
if (isSelected) {
|
|
34001
|
-
color.addScalar(0.3);
|
|
34002
|
-
} else if (isAttackTarget) {
|
|
34003
|
-
color.setHex(16729156);
|
|
34004
|
-
} else if (isValidMove) {
|
|
34005
|
-
color.setHex(4521796);
|
|
34006
|
-
}
|
|
34007
|
-
cols.push(color);
|
|
34008
|
-
map.set(`${tile.x},${tile.z ?? tile.y ?? 0}`, tile);
|
|
34009
|
-
});
|
|
34010
|
-
return { positions: pos, colors: cols, tileMap: map };
|
|
34011
|
-
}, [tiles, cellSize, offsetX, offsetZ, terrainColors, selectedTileIds, validMoves, attackTargets]);
|
|
34012
|
-
React87.useEffect(() => {
|
|
34013
|
-
if (!meshRef.current || !useInstancing) return;
|
|
34014
|
-
const mesh = meshRef.current;
|
|
34015
|
-
mesh.count = positions.length;
|
|
34016
|
-
const dummy = new THREE__namespace.Object3D();
|
|
34017
|
-
positions.forEach((pos, i) => {
|
|
34018
|
-
dummy.position.copy(pos);
|
|
34019
|
-
dummy.updateMatrix();
|
|
34020
|
-
mesh.setMatrixAt(i, dummy.matrix);
|
|
34021
|
-
if (mesh.setColorAt) {
|
|
34022
|
-
mesh.setColorAt(i, colors[i]);
|
|
34023
|
-
}
|
|
34024
|
-
});
|
|
34025
|
-
mesh.instanceMatrix.needsUpdate = true;
|
|
34026
|
-
if (mesh.instanceColor) {
|
|
34027
|
-
mesh.instanceColor.needsUpdate = true;
|
|
34028
|
-
}
|
|
34029
|
-
}, [positions, colors, useInstancing]);
|
|
34030
|
-
const handlePointerMove = (e) => {
|
|
34031
|
-
if (!onTileHover) return;
|
|
34032
|
-
const instanceId = e.instanceId;
|
|
34033
|
-
if (instanceId !== void 0) {
|
|
34034
|
-
const pos = positions[instanceId];
|
|
34035
|
-
if (pos) {
|
|
34036
|
-
const gridX = Math.round(pos.x / cellSize + offsetX);
|
|
34037
|
-
const gridZ = Math.round(pos.z / cellSize + offsetZ);
|
|
34038
|
-
const tile = tileMap.get(`${gridX},${gridZ}`);
|
|
34039
|
-
if (tile) {
|
|
34040
|
-
onTileHover(tile);
|
|
34041
|
-
}
|
|
34042
|
-
}
|
|
34043
|
-
}
|
|
34044
|
-
};
|
|
34045
|
-
const handleClick = (e) => {
|
|
34046
|
-
if (!onTileClick) return;
|
|
34047
|
-
const instanceId = e.instanceId;
|
|
34048
|
-
if (instanceId !== void 0) {
|
|
34049
|
-
const pos = positions[instanceId];
|
|
34050
|
-
if (pos) {
|
|
34051
|
-
const gridX = Math.round(pos.x / cellSize + offsetX);
|
|
34052
|
-
const gridZ = Math.round(pos.z / cellSize + offsetZ);
|
|
34053
|
-
const tile = tileMap.get(`${gridX},${gridZ}`);
|
|
34054
|
-
if (tile) {
|
|
34055
|
-
onTileClick(tile);
|
|
34056
|
-
}
|
|
34057
|
-
}
|
|
34058
|
-
}
|
|
34059
|
-
};
|
|
34060
|
-
const renderIndividualTiles = () => {
|
|
34061
|
-
return tiles.map((tile) => {
|
|
34062
|
-
const x = (tile.x - offsetX) * cellSize;
|
|
34063
|
-
const z = ((tile.z ?? tile.y ?? 0) - offsetZ) * cellSize;
|
|
34064
|
-
const y = (tile.elevation ?? 0) * 0.1;
|
|
34065
|
-
const colorHex = terrainColors[tile.type || ""] || terrainColors[tile.terrain || ""] || "#808080";
|
|
34066
|
-
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
34067
|
-
const isValidMove = validMoves.some(
|
|
34068
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
34069
|
-
);
|
|
34070
|
-
const isAttackTarget = attackTargets.some(
|
|
34071
|
-
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
34072
|
-
);
|
|
34073
|
-
let emissive = "#000000";
|
|
34074
|
-
if (isSelected) emissive = "#444444";
|
|
34075
|
-
else if (isAttackTarget) emissive = "#440000";
|
|
34076
|
-
else if (isValidMove) emissive = "#004400";
|
|
34077
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
34078
|
-
"mesh",
|
|
34079
|
-
{
|
|
34080
|
-
position: [x, y, z],
|
|
34081
|
-
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
34082
|
-
onClick: () => onTileClick?.(tile),
|
|
34083
|
-
onPointerEnter: () => onTileHover?.(tile),
|
|
34084
|
-
onPointerLeave: () => onTileHover?.(null),
|
|
34085
|
-
children: [
|
|
34086
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [cellSize * 0.95, 0.2, cellSize * 0.95] }),
|
|
34087
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
34088
|
-
"meshStandardMaterial",
|
|
34089
|
-
{
|
|
34090
|
-
color: colorHex,
|
|
34091
|
-
emissive,
|
|
34092
|
-
roughness: 0.8,
|
|
34093
|
-
metalness: 0.1
|
|
34094
|
-
}
|
|
34095
|
-
)
|
|
34096
|
-
]
|
|
34097
|
-
},
|
|
34098
|
-
tile.id ?? `tile-${tile.x}-${tile.y}`
|
|
34099
|
-
);
|
|
34100
|
-
});
|
|
34101
|
-
};
|
|
34102
|
-
if (useInstancing && tiles.length > 0) {
|
|
34103
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
34104
|
-
"instancedMesh",
|
|
34105
|
-
{
|
|
34106
|
-
ref: meshRef,
|
|
34107
|
-
args: [geometry, material, tiles.length],
|
|
34108
|
-
onPointerMove: handlePointerMove,
|
|
34109
|
-
onClick: handleClick
|
|
34110
|
-
}
|
|
34111
|
-
);
|
|
34112
|
-
}
|
|
34113
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { children: renderIndividualTiles() });
|
|
34114
|
-
}
|
|
34115
31913
|
var STATUS_STYLES3 = {
|
|
34116
31914
|
complete: {
|
|
34117
31915
|
dotColor: "text-[var(--color-success)]",
|
|
@@ -34293,111 +32091,6 @@ var ToastSlot = ({
|
|
|
34293
32091
|
) });
|
|
34294
32092
|
};
|
|
34295
32093
|
ToastSlot.displayName = "ToastSlot";
|
|
34296
|
-
function UnitVisual({ unit, position, isSelected, onClick }) {
|
|
34297
|
-
const groupRef = React87.useRef(null);
|
|
34298
|
-
const [animationState, setAnimationState] = React87.useState("idle");
|
|
34299
|
-
const [isHovered, setIsHovered] = React87.useState(false);
|
|
34300
|
-
const teamColor = React87.useMemo(() => {
|
|
34301
|
-
if (unit.faction === "player" || unit.team === "player") return 4491519;
|
|
34302
|
-
if (unit.faction === "enemy" || unit.team === "enemy") return 16729156;
|
|
34303
|
-
if (unit.faction === "neutral" || unit.team === "neutral") return 16777028;
|
|
34304
|
-
return 8947848;
|
|
34305
|
-
}, [unit.faction, unit.team]);
|
|
34306
|
-
fiber.useFrame((state) => {
|
|
34307
|
-
if (groupRef.current && animationState === "idle") {
|
|
34308
|
-
const y = position[1] + Math.sin(state.clock.elapsedTime * 2 + position[0]) * 0.05;
|
|
34309
|
-
groupRef.current.position.y = y;
|
|
34310
|
-
}
|
|
34311
|
-
});
|
|
34312
|
-
const healthPercent = React87.useMemo(() => {
|
|
34313
|
-
if (unit.health === void 0 || unit.maxHealth === void 0) return 1;
|
|
34314
|
-
return Math.max(0, Math.min(1, unit.health / unit.maxHealth));
|
|
34315
|
-
}, [unit.health, unit.maxHealth]);
|
|
34316
|
-
const healthColor = React87.useMemo(() => {
|
|
34317
|
-
if (healthPercent > 0.5) return "#44aa44";
|
|
34318
|
-
if (healthPercent > 0.25) return "#aaaa44";
|
|
34319
|
-
return "#ff4444";
|
|
34320
|
-
}, [healthPercent]);
|
|
34321
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
34322
|
-
"group",
|
|
34323
|
-
{
|
|
34324
|
-
ref: groupRef,
|
|
34325
|
-
position,
|
|
34326
|
-
onClick,
|
|
34327
|
-
onPointerEnter: () => setIsHovered(true),
|
|
34328
|
-
onPointerLeave: () => setIsHovered(false),
|
|
34329
|
-
userData: { type: "unit", unitId: unit.id },
|
|
34330
|
-
children: [
|
|
34331
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
34332
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
34333
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
34334
|
-
] }),
|
|
34335
|
-
isHovered && !isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
34336
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
34337
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffffff", transparent: true, opacity: 0.5 })
|
|
34338
|
-
] }),
|
|
34339
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.1, 0], children: [
|
|
34340
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.25, 0.25, 0.1, 8] }),
|
|
34341
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: teamColor })
|
|
34342
|
-
] }),
|
|
34343
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.5, 0], children: [
|
|
34344
|
-
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.15, 0.5, 4, 8] }),
|
|
34345
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: teamColor })
|
|
34346
|
-
] }),
|
|
34347
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
34348
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
34349
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: teamColor })
|
|
34350
|
-
] }),
|
|
34351
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 1.3, 0], children: [
|
|
34352
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.06] }),
|
|
34353
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#333333" })
|
|
34354
|
-
] }),
|
|
34355
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25 + 0.25 * healthPercent, 1.3, 0.01], children: [
|
|
34356
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * healthPercent, 0.04] }),
|
|
34357
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: healthColor })
|
|
34358
|
-
] }),
|
|
34359
|
-
unit.name && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 1.5, 0], children: [
|
|
34360
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.4, 0.1] }),
|
|
34361
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#000000", transparent: true, opacity: 0.5 })
|
|
34362
|
-
] })
|
|
34363
|
-
]
|
|
34364
|
-
}
|
|
34365
|
-
);
|
|
34366
|
-
}
|
|
34367
|
-
function UnitRenderer({
|
|
34368
|
-
units,
|
|
34369
|
-
cellSize = 1,
|
|
34370
|
-
offsetX = 0,
|
|
34371
|
-
offsetZ = 0,
|
|
34372
|
-
selectedUnitId,
|
|
34373
|
-
onUnitClick,
|
|
34374
|
-
onAnimationStateChange,
|
|
34375
|
-
animationSpeed = 1
|
|
34376
|
-
}) {
|
|
34377
|
-
const handleUnitClick = React87__namespace.default.useCallback(
|
|
34378
|
-
(unit) => {
|
|
34379
|
-
onUnitClick?.(unit);
|
|
34380
|
-
},
|
|
34381
|
-
[onUnitClick]
|
|
34382
|
-
);
|
|
34383
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { children: units.map((unit) => {
|
|
34384
|
-
const unitX = unit.x ?? unit.position?.x ?? 0;
|
|
34385
|
-
const unitY = unit.z ?? unit.y ?? unit.position?.y ?? 0;
|
|
34386
|
-
const x = (unitX - offsetX) * cellSize;
|
|
34387
|
-
const z = (unitY - offsetZ) * cellSize;
|
|
34388
|
-
const y = (unit.elevation ?? 0) * 0.1 + 0.5;
|
|
34389
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
34390
|
-
UnitVisual,
|
|
34391
|
-
{
|
|
34392
|
-
unit,
|
|
34393
|
-
position: [x, y, z],
|
|
34394
|
-
isSelected: selectedUnitId === unit.id,
|
|
34395
|
-
onClick: () => handleUnitClick(unit)
|
|
34396
|
-
},
|
|
34397
|
-
unit.id
|
|
34398
|
-
);
|
|
34399
|
-
}) });
|
|
34400
|
-
}
|
|
34401
32094
|
function WorldMapTemplate({
|
|
34402
32095
|
entity,
|
|
34403
32096
|
scale = 0.4,
|
|
@@ -34425,6 +32118,33 @@ function WorldMapTemplate({
|
|
|
34425
32118
|
WorldMapTemplate.displayName = "WorldMapTemplate";
|
|
34426
32119
|
|
|
34427
32120
|
// components/organisms/component-registry.generated.ts
|
|
32121
|
+
function lazyThree(name, loader) {
|
|
32122
|
+
const Lazy = React87__namespace.default.lazy(() => loader().then((m) => ({ default: m[name] })));
|
|
32123
|
+
function ThreeWrapper(props) {
|
|
32124
|
+
return React87__namespace.default.createElement(
|
|
32125
|
+
React87__namespace.default.Suspense,
|
|
32126
|
+
{ fallback: null },
|
|
32127
|
+
React87__namespace.default.createElement(Lazy, props)
|
|
32128
|
+
);
|
|
32129
|
+
}
|
|
32130
|
+
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
32131
|
+
return ThreeWrapper;
|
|
32132
|
+
}
|
|
32133
|
+
var Camera3D = lazyThree("Camera3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32134
|
+
var Canvas3DErrorBoundary = lazyThree("Canvas3DErrorBoundary", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32135
|
+
var Canvas3DLoadingState = lazyThree("Canvas3DLoadingState", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32136
|
+
var FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32137
|
+
var FeatureRenderer3D = lazyThree("FeatureRenderer3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32138
|
+
var GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32139
|
+
var GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32140
|
+
var GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32141
|
+
var GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32142
|
+
var Lighting3D = lazyThree("Lighting3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32143
|
+
var ModelLoader = lazyThree("ModelLoader", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32144
|
+
var PhysicsObject3D = lazyThree("PhysicsObject3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32145
|
+
var Scene3D = lazyThree("Scene3D", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32146
|
+
var TileRenderer = lazyThree("TileRenderer", () => import('@almadar/ui/components/organisms/game/three'));
|
|
32147
|
+
var UnitRenderer = lazyThree("UnitRenderer", () => import('@almadar/ui/components/organisms/game/three'));
|
|
34428
32148
|
var COMPONENT_REGISTRY = {
|
|
34429
32149
|
"Accordion": AccordionPattern,
|
|
34430
32150
|
"AccordionPattern": AccordionPattern,
|