@pascal-app/viewer 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/error-boundary.d.ts +18 -0
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/components/error-boundary.js +11 -0
- package/dist/components/renderers/item/item-renderer.d.ts.map +1 -1
- package/dist/components/renderers/item/item-renderer.js +7 -1
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
- package/dist/components/renderers/wall/wall-renderer.js +6 -2
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +23 -3
- package/dist/hooks/use-grid-events.d.ts +12 -0
- package/dist/hooks/use-grid-events.d.ts.map +1 -0
- package/dist/hooks/use-grid-events.js +33 -0
- package/dist/lib/asset-url.d.ts +1 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +0 -1
- package/dist/store/use-viewer.d.ts +4 -0
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +3 -0
- package/package.json +4 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ErrorInfo, ReactNode } from 'react';
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
export declare class ErrorBoundary extends Component<{
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
fallback: ReactNode;
|
|
6
|
+
}, {
|
|
7
|
+
hasError: boolean;
|
|
8
|
+
}> {
|
|
9
|
+
state: {
|
|
10
|
+
hasError: boolean;
|
|
11
|
+
};
|
|
12
|
+
static getDerivedStateFromError(): {
|
|
13
|
+
hasError: boolean;
|
|
14
|
+
};
|
|
15
|
+
componentDidCatch(_e: Error, _i: ErrorInfo): void;
|
|
16
|
+
render(): ReactNode;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=error-boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/components/error-boundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,qBAAa,aAAc,SAAQ,SAAS,CAC1C;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,EAC5C;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CACtB;IACC,KAAK;;MAAsB;IAC3B,MAAM,CAAC,wBAAwB;;;IAG/B,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS;IAC1C,MAAM;CAGP"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
export class ErrorBoundary extends Component {
|
|
3
|
+
state = { hasError: false };
|
|
4
|
+
static getDerivedStateFromError() {
|
|
5
|
+
return { hasError: true };
|
|
6
|
+
}
|
|
7
|
+
componentDidCatch(_e, _i) { }
|
|
8
|
+
render() {
|
|
9
|
+
return this.state.hasError ? this.props.fallback : this.props.children;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/item/item-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"item-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/item/item-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;AAoDzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAiBxD,CAAA"}
|
|
@@ -11,6 +11,7 @@ import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
|
11
11
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
12
12
|
import { resolveCdnUrl } from '../../../lib/asset-url';
|
|
13
13
|
import { useItemLightPool } from '../../../store/use-item-light-pool';
|
|
14
|
+
import { ErrorBoundary } from '../../error-boundary';
|
|
14
15
|
import { NodeRenderer } from '../node-renderer';
|
|
15
16
|
// Shared materials to avoid creating new instances for every mesh
|
|
16
17
|
const defaultMaterial = new MeshStandardNodeMaterial({
|
|
@@ -34,10 +35,15 @@ const getMaterialForOriginal = (original) => {
|
|
|
34
35
|
}
|
|
35
36
|
return defaultMaterial;
|
|
36
37
|
};
|
|
38
|
+
const BrokenItemFallback = ({ node }) => {
|
|
39
|
+
const handlers = useNodeEvents(node, 'item');
|
|
40
|
+
const [w, h, d] = node.asset.dimensions;
|
|
41
|
+
return (_jsxs("mesh", { "position-y": h / 2, ...handlers, children: [_jsx("boxGeometry", { args: [w, h, d] }), _jsx("meshStandardMaterial", { color: "#ef4444", opacity: 0.6, transparent: true, wireframe: true })] }));
|
|
42
|
+
};
|
|
37
43
|
export const ItemRenderer = ({ node }) => {
|
|
38
44
|
const ref = useRef(null);
|
|
39
45
|
useRegistry(node.id, node.type, ref);
|
|
40
|
-
return (_jsxs("group", { position: node.position, ref: ref, rotation: node.rotation, visible: node.visible, children: [_jsx(Suspense, { fallback: _jsx(PreviewModel, { node: node }), children: _jsx(ModelRenderer, { node: node }) }), node.children?.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
46
|
+
return (_jsxs("group", { position: node.position, ref: ref, rotation: node.rotation, visible: node.visible, children: [_jsx(ErrorBoundary, { fallback: _jsx(BrokenItemFallback, { node: node }), children: _jsx(Suspense, { fallback: _jsx(PreviewModel, { node: node }), children: _jsx(ModelRenderer, { node: node }) }) }), node.children?.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
41
47
|
};
|
|
42
48
|
const previewMaterial = new MeshStandardNodeMaterial({
|
|
43
49
|
color: '#cccccc',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAMvE,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CA0BxD,CAAA"}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
2
|
+
import { useRegistry, useScene } from '@pascal-app/core';
|
|
3
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
5
|
import { NodeRenderer } from '../node-renderer';
|
|
6
6
|
export const WallRenderer = ({ node }) => {
|
|
7
7
|
const ref = useRef(null);
|
|
8
8
|
useRegistry(node.id, 'wall', ref);
|
|
9
|
+
// Mark dirty on mount so WallSystem rebuilds geometry when wall (re)appears
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
useScene.getState().markDirty(node.id);
|
|
12
|
+
}, [node.id]);
|
|
9
13
|
const handlers = useNodeEvents(node, 'wall');
|
|
10
14
|
return (_jsxs("mesh", { castShadow: true, receiveShadow: true, ref: ref, visible: node.visible, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { name: "collision-mesh", visible: false, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
11
15
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ground-occluder.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/ground-occluder.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+
|
|
1
|
+
{"version":3,"file":"ground-occluder.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/ground-occluder.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+CA+F1B,CAAA"}
|
|
@@ -17,12 +17,32 @@ export const GroundOccluder = () => {
|
|
|
17
17
|
s.lineTo(size, size);
|
|
18
18
|
s.lineTo(-size, size);
|
|
19
19
|
s.closePath();
|
|
20
|
-
|
|
20
|
+
const levelIndexById = new Map();
|
|
21
|
+
let lowestLevelIndex = Number.POSITIVE_INFINITY;
|
|
22
|
+
Object.values(nodes).forEach((node) => {
|
|
23
|
+
if (node.type !== 'level') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
levelIndexById.set(node.id, node.level);
|
|
27
|
+
lowestLevelIndex = Math.min(lowestLevelIndex, node.level);
|
|
28
|
+
});
|
|
29
|
+
// Only the lowest level should punch through the ground plane.
|
|
30
|
+
// Upper-level slabs should still cast shadows, but they should not
|
|
31
|
+
// reveal their footprint on the level-zero ground material.
|
|
21
32
|
const polygons = [];
|
|
22
33
|
Object.values(nodes).forEach((node) => {
|
|
23
|
-
if (node.type === 'slab' && node.
|
|
24
|
-
|
|
34
|
+
if (!(node.type === 'slab' && node.visible && node.polygon.length >= 3)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (Number.isFinite(lowestLevelIndex)) {
|
|
38
|
+
const parentLevelIndex = node.parentId
|
|
39
|
+
? levelIndexById.get(node.parentId)
|
|
40
|
+
: undefined;
|
|
41
|
+
if (parentLevelIndex !== lowestLevelIndex) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
25
44
|
}
|
|
45
|
+
polygons.push(node.polygon);
|
|
26
46
|
});
|
|
27
47
|
if (polygons.length > 0) {
|
|
28
48
|
// Format for polygon-clipping: [[[x, y], [x, y], ...]]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ThreeEvent } from "@react-three/fiber";
|
|
2
|
+
export declare function useGridEvents(): {
|
|
3
|
+
onPointerDown: (e: ThreeEvent<PointerEvent>) => void;
|
|
4
|
+
onPointerUp: (e: ThreeEvent<PointerEvent>) => void;
|
|
5
|
+
onClick: (e: ThreeEvent<PointerEvent>) => void;
|
|
6
|
+
onPointerEnter: (e: ThreeEvent<PointerEvent>) => void;
|
|
7
|
+
onPointerLeave: (e: ThreeEvent<PointerEvent>) => void;
|
|
8
|
+
onPointerMove: (e: ThreeEvent<PointerEvent>) => void;
|
|
9
|
+
onDoubleClick: (e: ThreeEvent<PointerEvent>) => void;
|
|
10
|
+
onContextMenu: (e: ThreeEvent<PointerEvent>) => void;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=use-grid-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-grid-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-grid-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,wBAAgB,aAAa;uBAYN,UAAU,CAAC,YAAY,CAAC;qBAI1B,UAAU,CAAC,YAAY,CAAC;iBAI5B,UAAU,CAAC,YAAY,CAAC;wBAIjB,UAAU,CAAC,YAAY,CAAC;wBACxB,UAAU,CAAC,YAAY,CAAC;uBACzB,UAAU,CAAC,YAAY,CAAC;uBACxB,UAAU,CAAC,YAAY,CAAC;uBACxB,UAAU,CAAC,YAAY,CAAC;EAE9C"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { emitter } from "@pascal-app/core";
|
|
2
|
+
export function useGridEvents() {
|
|
3
|
+
const emit = (suffix, e) => {
|
|
4
|
+
const eventKey = `grid:${suffix}`;
|
|
5
|
+
const payload = {
|
|
6
|
+
position: [e.point.x, e.point.y, e.point.z],
|
|
7
|
+
nativeEvent: e,
|
|
8
|
+
};
|
|
9
|
+
emitter.emit(eventKey, payload);
|
|
10
|
+
};
|
|
11
|
+
return {
|
|
12
|
+
onPointerDown: (e) => {
|
|
13
|
+
if (e.button !== 0)
|
|
14
|
+
return;
|
|
15
|
+
emit("pointerdown", e);
|
|
16
|
+
},
|
|
17
|
+
onPointerUp: (e) => {
|
|
18
|
+
if (e.button !== 0)
|
|
19
|
+
return;
|
|
20
|
+
emit("pointerup", e);
|
|
21
|
+
},
|
|
22
|
+
onClick: (e) => {
|
|
23
|
+
if (e.button !== 0)
|
|
24
|
+
return;
|
|
25
|
+
emit("click", e);
|
|
26
|
+
},
|
|
27
|
+
onPointerEnter: (e) => emit("enter", e),
|
|
28
|
+
onPointerLeave: (e) => emit("leave", e),
|
|
29
|
+
onPointerMove: (e) => emit("move", e),
|
|
30
|
+
onDoubleClick: (e) => emit("double-click", e),
|
|
31
|
+
onContextMenu: (e) => emit("context-menu", e),
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/lib/asset-url.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/lib/asset-url.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"asset-url.d.ts","sourceRoot":"","sources":["../../src/lib/asset-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,QAAwE,CAAA;AAEnG;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgB5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAiB3E"}
|
package/dist/lib/asset-url.js
CHANGED
|
@@ -18,6 +18,8 @@ type ViewerState = {
|
|
|
18
18
|
setCameraMode: (mode: 'perspective' | 'orthographic') => void;
|
|
19
19
|
theme: 'light' | 'dark';
|
|
20
20
|
setTheme: (theme: 'light' | 'dark') => void;
|
|
21
|
+
unit: 'metric' | 'imperial';
|
|
22
|
+
setUnit: (unit: 'metric' | 'imperial') => void;
|
|
21
23
|
levelMode: 'stacked' | 'exploded' | 'solo' | 'manual';
|
|
22
24
|
setLevelMode: (mode: 'stacked' | 'exploded' | 'solo' | 'manual') => void;
|
|
23
25
|
wallMode: 'up' | 'cutaway' | 'down';
|
|
@@ -52,6 +54,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
52
54
|
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
53
55
|
cameraMode: "perspective" | "orthographic";
|
|
54
56
|
theme: "light" | "dark";
|
|
57
|
+
unit: "metric" | "imperial";
|
|
55
58
|
levelMode: "stacked" | "exploded" | "solo" | "manual";
|
|
56
59
|
wallMode: "up" | "cutaway" | "down";
|
|
57
60
|
projectPreferences: Record<string, {
|
|
@@ -68,6 +71,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
68
71
|
getOptions: () => Partial<import("zustand/middleware").PersistOptions<ViewerState, {
|
|
69
72
|
cameraMode: "perspective" | "orthographic";
|
|
70
73
|
theme: "light" | "dark";
|
|
74
|
+
unit: "metric" | "imperial";
|
|
71
75
|
levelMode: "stacked" | "exploded" | "solo" | "manual";
|
|
72
76
|
wallMode: "up" | "cutaway" | "down";
|
|
73
77
|
projectPreferences: Record<string, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
|
package/dist/store/use-viewer.js
CHANGED
|
@@ -9,6 +9,8 @@ const useViewer = create()(persist((set) => ({
|
|
|
9
9
|
setCameraMode: (mode) => set({ cameraMode: mode }),
|
|
10
10
|
theme: 'light',
|
|
11
11
|
setTheme: (theme) => set({ theme }),
|
|
12
|
+
unit: 'metric',
|
|
13
|
+
setUnit: (unit) => set({ unit }),
|
|
12
14
|
levelMode: 'stacked',
|
|
13
15
|
setLevelMode: (mode) => set({ levelMode: mode }),
|
|
14
16
|
wallMode: 'up',
|
|
@@ -102,6 +104,7 @@ const useViewer = create()(persist((set) => ({
|
|
|
102
104
|
partialize: (state) => ({
|
|
103
105
|
cameraMode: state.cameraMode,
|
|
104
106
|
theme: state.theme,
|
|
107
|
+
unit: state.unit,
|
|
105
108
|
levelMode: state.levelMode,
|
|
106
109
|
wallMode: state.wallMode,
|
|
107
110
|
projectPreferences: state.projectPreferences,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@pascal/typescript-config": "*",
|
|
37
|
+
"@types/node": "^25.5.0",
|
|
37
38
|
"@types/react": "^19.2.2",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
39
|
+
"@types/three": "^0.183.0",
|
|
40
|
+
"typescript": "5.9.3"
|
|
40
41
|
},
|
|
41
42
|
"keywords": [
|
|
42
43
|
"3d",
|