cdn and packages building

This commit is contained in:
wass08
2026-02-04 09:53:44 +09:00
parent 4036f370e7
commit e776ce9849
14 changed files with 205 additions and 28 deletions
+47 -10
View File
@@ -1,33 +1,70 @@
{
"name": "@pascal-app/core",
"version": "0.0.1",
"private": true,
"version": "0.1.5",
"description": "Core library for Pascal 3D building editor",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts",
"default": "./src/index.ts"
}
},
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "tsc --declaration --emitDeclarationOnly && tsc",
"prepublishOnly": "npm run build"
},
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
},
"peerDependencies": {
"@react-three/drei": "^10",
"@react-three/fiber": "^9",
"react": "^18 || ^19",
"three": "^0.182"
},
"dependencies": {
"dedent": "^1.7.1",
"idb-keyval": "^6.2.2",
"mitt": "^3.0.1",
"nanoid": "^5.1.6",
"three-bvh-csg": "^0.0.17",
"zod": "^4.3.5",
"zundo": "^2.3.0",
"zustand": "^5"
},
"devDependencies": {
"@repo/typescript-config": "*",
"@types/react": "^19.2.2",
"typescript": "5.9.2",
"@types/three": "^0.182.0"
},
"dependencies": {
"dedent": "^1.7.1",
"idb-keyval": "^6.2.2",
"mitt": "^3.0.1",
"nanoid": "^5.1.6",
"zod": "^4.3.5",
"zundo": "^2.3.0"
}
"keywords": [
"3d",
"building",
"editor",
"architecture",
"webgpu",
"three.js"
],
"repository": {
"type": "git",
"url": "https://github.com/your-username/pascal-editor.git",
"directory": "packages/core"
},
"license": "MIT"
}
+1 -1
View File
@@ -4,6 +4,6 @@
"outDir": "dist",
"rootDir": "src"
},
"include": ["src", "../viewer/src/systems/level"],
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
+44 -4
View File
@@ -1,26 +1,66 @@
{
"name": "@pascal-app/viewer",
"version": "0.0.1",
"private": true,
"version": "0.1.5",
"description": "3D viewer component for Pascal building editor",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./src/index.ts",
"default": "./src/index.ts"
}
},
"files": [
"dist",
"README.md"
],
"scripts": {
"build": "tsc --declaration --emitDeclarationOnly && tsc",
"prepublishOnly": "npm run build"
},
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
},
"peerDependencies": {
"@pascal-app/core": "*",
"@pascal-app/core": "^0.1.4",
"@react-three/drei": "^10",
"@react-three/fiber": "^9",
"react": "^18 || ^19",
"three": "^0.182"
},
"dependencies": {
"zustand": "^5"
},
"devDependencies": {
"@repo/typescript-config": "*",
"@types/react": "^19.2.2",
"typescript": "5.9.2",
"@types/three": "^0.182.0"
}
},
"keywords": [
"3d",
"building",
"editor",
"viewer",
"architecture",
"webgpu",
"three.js",
"react-three-fiber"
],
"repository": {
"type": "git",
"url": "https://github.com/your-username/pascal-editor.git",
"directory": "packages/viewer"
},
"license": "MIT"
}
@@ -4,6 +4,7 @@ import { useGLTF } from '@react-three/drei/core/Gltf'
import { Suspense, useEffect, useMemo, useRef } from 'react'
import type { Group, Material, Mesh } from 'three'
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu'
import { resolveCdnUrl } from '../../../lib/asset-url'
import { useNodeEvents } from '../../../hooks/use-node-events'
// Shared materials to avoid creating new instances for every mesh
@@ -46,7 +47,7 @@ export const ItemRenderer = ({ node }: { node: ItemNode }) => {
}
const ModelRenderer = ({ node }: { node: ItemNode }) => {
const { scene, nodes } = useGLTF(node.asset.src)
const { scene, nodes } = useGLTF(resolveCdnUrl(node.asset.src) || '')
if (nodes.cutout) {
nodes.cutout.visible = false
@@ -64,19 +65,19 @@ const ModelRenderer = ({ node }: { node: ItemNode }) => {
if ((child as Mesh).isMesh) {
const mesh = child as Mesh
if (mesh.name === 'cutout') {
child.visible = false;
child.visible = false
return
}
let hasGlass = false;
let hasGlass = false
// Handle both single material and material array cases
if (Array.isArray(mesh.material)) {
mesh.material = mesh.material.map((mat) => getMaterialForOriginal(mat))
hasGlass = mesh.material.some(mat => mat.name === 'glass');
hasGlass = mesh.material.some((mat) => mat.name === 'glass')
} else {
mesh.material = getMaterialForOriginal(mesh.material)
hasGlass = mesh.material.name === 'glass';
hasGlass = mesh.material.name === 'glass'
}
mesh.castShadow = !hasGlass
mesh.receiveShadow = !hasGlass
@@ -27,7 +27,8 @@ export const ScanRenderer = ({ node }: { node: ScanNode }) => {
}
const ScanModel = ({ url, opacity }: { url: string; opacity: number }) => {
const { scene } = useGLTFKTX2(url)
const gltf = useGLTFKTX2(url) as any
const scene = gltf.scene
useMemo(() => {
const normalizedOpacity = opacity / 100
@@ -44,7 +45,7 @@ const ScanModel = ({ url, opacity }: { url: string; opacity: number }) => {
material.needsUpdate = true
}
scene.traverse((child) => {
scene.traverse((child: any) => {
if ((child as Mesh).isMesh) {
const mesh = child as Mesh
+1 -1
View File
@@ -6,7 +6,7 @@ import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.j
const ktx2LoaderInstance = new KTX2Loader()
ktx2LoaderInstance.setTranscoderPath('https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/')
const useGLTFKTX2 = (path: string) => {
const useGLTFKTX2 = (path: string): ReturnType<typeof useGLTF> => {
const gl = useThree((state) => state.gl)
return useGLTF(path, true, true, (loader) => {
+1
View File
@@ -1,3 +1,4 @@
export { default as Viewer } from './components/viewer'
export { useGridEvents } from './hooks/use-grid-events'
export { default as useViewer } from './store/use-viewer'
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url'
+51
View File
@@ -0,0 +1,51 @@
import { loadAssetUrl } from '@pascal-app/core'
export const ASSETS_CDN_URL = 'https://pascal-cdn.wawasensei.dev'
/**
* Resolves an asset URL to the appropriate format:
* - If URL starts with http:// or https://, return as-is (external URL)
* - If URL starts with asset://, resolve from IndexedDB storage
* - If URL starts with /, prepend CDN URL (absolute path)
* - Otherwise, prepend CDN URL (relative path)
*/
export async function resolveAssetUrl(url: string | undefined | null): Promise<string | null> {
if (!url) return null
// External URL - use as-is
if (url.startsWith('http://') || url.startsWith('https://')) {
return url
}
// IndexedDB asset - resolve from storage
if (url.startsWith('asset://')) {
return loadAssetUrl(url)
}
// Absolute or relative path - prepend CDN URL
const normalizedPath = url.startsWith('/') ? url : `/${url}`
return `${ASSETS_CDN_URL}${normalizedPath}`
}
/**
* Synchronous version for URLs that don't need IndexedDB resolution
* Only use this if you're sure the URL is not an asset:// URL
*/
export function resolveCdnUrl(url: string | undefined | null): string | null {
if (!url) return null
// External URL - use as-is
if (url.startsWith('http://') || url.startsWith('https://')) {
return url
}
// Don't use this for asset:// URLs - use resolveAssetUrl instead
if (url.startsWith('asset://')) {
console.warn('Use resolveAssetUrl() for asset:// URLs, not resolveCdnUrl()')
return null
}
// Absolute or relative path - prepend CDN URL
const normalizedPath = url.startsWith('/') ? url : `/${url}`
return `${ASSETS_CDN_URL}${normalizedPath}`
}
+35
View File
@@ -0,0 +1,35 @@
import type { AnyNode, BaseNode, BuildingNode, LevelNode, ZoneNode } from "@pascal-app/core";
import type { Object3D } from "three";
type SelectionPath = {
buildingId: BuildingNode["id"] | null;
levelId: LevelNode["id"] | null;
zoneId: ZoneNode["id"] | null;
selectedIds: BaseNode["id"][];
};
type Outliner = {
selectedObjects: Object3D[];
hoveredObjects: Object3D[];
};
type ViewerState = {
selection: SelectionPath;
hoveredId: AnyNode['id'] | ZoneNode['id'] | null;
setHoveredId: (id: AnyNode['id'] | ZoneNode['id'] | null) => void;
cameraMode: 'perspective' | 'orthographic';
setCameraMode: (mode: 'perspective' | 'orthographic') => void;
levelMode: 'stacked' | 'exploded' | 'solo' | 'manual';
setLevelMode: (mode: 'stacked' | 'exploded' | 'solo' | 'manual') => void;
wallMode: 'up' | 'cutaway' | 'down';
setWallMode: (mode: 'up' | 'cutaway' | 'down') => void;
showScans: boolean;
setShowScans: (show: boolean) => void;
showGuides: boolean;
setShowGuides: (show: boolean) => void;
setSelection: (updates: Partial<SelectionPath>) => void;
resetSelection: () => void;
outliner: Outliner;
exportScene: (() => Promise<void>) | null;
setExportScene: (fn: (() => Promise<void>) | null) => void;
};
declare const useViewer: import("zustand").UseBoundStore<import("zustand").StoreApi<ViewerState>>;
export default useViewer;
//# sourceMappingURL=use-viewer.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["use-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,QAAQ,EACT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAItC,KAAK,aAAa,GAAG;IACnB,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;CAC/B,CAAC;AAEF,KAAK,QAAQ,GAAG;IACd,eAAe,EAAE,QAAQ,EAAE,CAAC;IAC5B,cAAc,EAAE,QAAQ,EAAE,CAAC;CAC5B,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,aAAa,CAAA;IACxB,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,YAAY,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAEjE,UAAU,EAAE,aAAa,GAAG,cAAc,CAAA;IAC1C,aAAa,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,KAAK,IAAI,CAAA;IAE7D,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAA;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,KAAK,IAAI,CAAA;IAExE,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,CAAA;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;IAEtD,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAErC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAGtC,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAA;IACvD,cAAc,EAAE,MAAM,IAAI,CAAA;IAE1B,QAAQ,EAAE,QAAQ,CAAA;IAGlB,WAAW,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAA;IACzC,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;CAC3D,CAAA;AAED,QAAA,MAAM,SAAS,0EAqDZ,CAAC;AAEJ,eAAe,SAAS,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
export declare const LevelSystem: () => null;
//# sourceMappingURL=level-system.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"level-system.d.ts","sourceRoot":"","sources":["level-system.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,YAkBvB,CAAA"}