diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 36a40f21..d7d1b868 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -9,6 +9,7 @@ on: env: PNPM_CACHE_FOLDER: .pnpm-store HUSKY: 0 # Bypass husky commit hook for CI + NUXT_UI_PRO_LICENSE: ${{ secrets.NUXT_UI_PRO_LICENSE }} permissions: {} @@ -34,5 +35,5 @@ jobs: - name: Install dependencies run: pnpm install - name: Build - run: pnpm prepack + run: pnpm build - run: pnpx pkg-pr-new publish --compact --pnpm diff --git a/.gitignore b/.gitignore index e6d4c909..d7295688 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ dist .env .netlify +**/.env + # Env .env diff --git a/.release-it.json b/.release-it.json index b72b1978..dccafd5d 100644 --- a/.release-it.json +++ b/.release-it.json @@ -14,7 +14,7 @@ }, "hooks": { "before:init": ["pnpm run lint"], - "after:bump": "pnpm run prepack", + "after:bump": "pnpm run build", "after:git:release": "echo After git push, before github release", "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}." } diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..633fc3f8 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "client: chrome", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + }, + { + "type": "node", + "request": "launch", + "name": "server: nuxt", + "outputCapture": "std", + "program": "${workspaceFolder}/node_modules/nuxt/bin/nuxt.mjs", + "args": [ + "dev playground" + ], + } + ], + "compounds": [ + { + "name": "fullstack: nuxt", + "configurations": [ + "server: nuxt", + "client: chrome" + ] + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d355eb37..98b4a553 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## [5.0.0-rc.1](https://github.com/Tresjs/nuxt/compare/5.0.0-rc.0...5.0.0-rc.1) (2025-09-20) + +### Bug Fixes + +* integrate useAttrs composable in TresCanvas component ([16f12e7](https://github.com/Tresjs/nuxt/commit/16f12e79334c6dcbd3d78412838b41a00a47185d)) + +## [5.0.0-rc.0](https://github.com/Tresjs/nuxt/compare/4.1.0-next.1...5.0.0-rc.0) (2025-09-20) + +### ⚠ BREAKING CHANGES + +* upgrade Nuxt 4 (#171) + +### Features + +* upgrade Nuxt 4 ([#171](https://github.com/Tresjs/nuxt/issues/171)) ([2cb9e6b](https://github.com/Tresjs/nuxt/commit/2cb9e6b624cf1714edaa0b05be768ca99a4d32dc)) +* v5-tres-devtools ([#172](https://github.com/Tresjs/nuxt/issues/172)) ([b86f224](https://github.com/Tresjs/nuxt/commit/b86f224e189f83600646099bef3348f7c7a8a893)) + +## [4.1.0-next.1](https://github.com/Tresjs/nuxt/compare/4.0.0...4.1.0-next.1) (2025-06-26) + +### Features + +* add pointer events to AnimatedDonnut and update core dependency ([8b6693f](https://github.com/Tresjs/nuxt/commit/8b6693fc55adc215c3381086e819b9f05489751b)) +## [4.0.0-next.0](https://github.com/Tresjs/nuxt/compare/4.0.0...4.1.0-next.1) (2025-02-22) + ## [4.0.0](https://github.com/Tresjs/nuxt/compare/3.0.8...4.0.0) (2025-05-25) ### ⚠ BREAKING CHANGES @@ -14,6 +38,12 @@ * update TypeScript error comments in ProgramsModule.vue for clarity ([3e58130](https://github.com/Tresjs/nuxt/commit/3e58130dfb94b44a1f12323bf432c7cf93bc6220)) +## [4.0.0-next.0](https://github.com/tresjs/nuxt/compare/3.0.7...4.0.0-next.0) (2025-02-22) + +### Bug Fixes + +* **deps:** update dependency @tresjs/nuxt to v3.0.7 ([#106](https://github.com/tresjs/nuxt/issues/106)) ([8a73538](https://github.com/tresjs/nuxt/commit/8a735385edffdeda2fb12fa2cbe51376008ae39f)) +* **deps:** update dependency typescript to v5.6.2 ([#136](https://github.com/tresjs/nuxt/issues/136)) ([a32ec04](https://github.com/tresjs/nuxt/commit/a32ec043fe65e1fb396a42a206eb32b3cb6df6a3)) ## [3.0.8](https://github.com/Tresjs/nuxt/compare/3.0.7...3.0.8) (2025-02-25) ### Bug Fixes diff --git a/client/.npmrc b/client/.npmrc new file mode 100644 index 00000000..c483022c --- /dev/null +++ b/client/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=true \ No newline at end of file diff --git a/client/app.config.ts b/client/app.config.ts new file mode 100644 index 00000000..1bebf0a3 --- /dev/null +++ b/client/app.config.ts @@ -0,0 +1,15 @@ +export default defineAppConfig({ + ui: { + colors: { + primary: 'teal', + accent: 'yellow', + neutral: 'zinc', + }, + card: { + slots: { + root: 'hover:bg-linear-[115deg,#272727 .06%,#171717]', + body: 'p-2 sm:p-3', + }, + }, + }, +}) diff --git a/client/app.vue b/client/app.vue index f8eacfa7..8dd09d3f 100644 --- a/client/app.vue +++ b/client/app.vue @@ -1,5 +1,5 @@ diff --git a/client/assets/css/main.css b/client/assets/css/main.css new file mode 100644 index 00000000..be75c7cf --- /dev/null +++ b/client/assets/css/main.css @@ -0,0 +1,61 @@ +@import "tailwindcss"; +@import "@nuxt/ui-pro"; + +@theme static { + --font-display: 'Manrope', sans-serif; + --font-sans: 'Inter', sans-serif; + --font-mono: 'Fira Code', monospace; + --breakpoint-3xl: 1920px; + --ui-pattern-fg: color-mix(in oklab,var(--ui-text)5%,transparent); + --ui-pattern-bg: repeating-linear-gradient(315deg,var(--ui-pattern-fg)0,var(--ui-pattern-fg)1px,transparent 0,transparent 50%); + + /* Technical blueprint styles */ + --ui-line-gap: 5px; + --ui-line-width: 1px; + --ui-line-offset: 172px; + --ui-line-color: var(--color-gray-200); + + /* Teal Palette */ + --color-teal-50: #f2fbf8; + --color-teal-100: #d3f4ea; + --color-teal-200: #a6e9d6; + --color-teal-300: #82dbc5; + --color-teal-400: #44bda2; + --color-teal-500: #2ba189; + --color-teal-600: #20816f; + --color-teal-700: #1d685b; + --color-teal-800: #1c534b; + --color-teal-900: #1b463f; + --color-teal-950: #0a2925; + + /* Yellow/Brown Palette */ + --color-yellow-50: #fff8eb; + --color-yellow-100: #feeac7; + --color-yellow-200: #fdd48a; + --color-yellow-300: #fbb03b; + --color-yellow-400: #fa9e25; + --color-yellow-500: #f47a0c; + --color-yellow-600: #b85607; + --color-yellow-700: #b3390a; + --color-yellow-800: #922b0e; + --color-yellow-900: #78250f; + --color-yellow-950: #451003; +} + +:root { + --ui-header-height: 49px; +} + +.dark { + --ui-line-color: var(--color-gray-800); +} + +.pattern-bg { + background-image: var(--ui-pattern-bg); + background-size: 10px 10px; + background-attachment: fixed; +} + +.noise-bg { + background-image: url("data:image/svg+xml,%3C!-- svg: first layer --%3E%3Csvg viewBox='0 0 250 250' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='4' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); +} diff --git a/client/components/AssetsMonitor.vue b/client/components/AssetsMonitor.vue new file mode 100644 index 00000000..f8365b3e --- /dev/null +++ b/client/components/AssetsMonitor.vue @@ -0,0 +1,246 @@ + + + diff --git a/client/components/DevtoolsGraph.vue b/client/components/DevtoolsGraph.vue index 175bde6c..6cba08d9 100644 --- a/client/components/DevtoolsGraph.vue +++ b/client/components/DevtoolsGraph.vue @@ -6,17 +6,13 @@ const props = withDefaults(defineProps<{ value?: number unit?: string label?: string - color?: 'green' | 'yellow' + color?: 'primary' | 'warning' | 'error' }>(), { points: () => [], value: 0, unit: '', label: '', - color: 'green', -}) - -const textColor = computed(() => { // '' - return props.color === 'yellow' ? 'text-[#827717] dark:text-[#EAB306]' : 'text-[#15803D] dark:text-[#34E676]' + color: 'primary', }) const height = 40 @@ -50,10 +46,11 @@ const pointsF = computed(() => props.points.map( border-none font-sans " + :class="`graph-${color}`" >
{{ Math.round(value) }} {{ unit }}
@@ -75,21 +72,24 @@ const pointsF = computed(() => props.points.map( diff --git a/client/components/EditableColor.vue b/client/components/EditableColor.vue new file mode 100644 index 00000000..d16dda14 --- /dev/null +++ b/client/components/EditableColor.vue @@ -0,0 +1,48 @@ + + + diff --git a/client/components/EditableNumber.vue b/client/components/EditableNumber.vue new file mode 100644 index 00000000..f8be83c4 --- /dev/null +++ b/client/components/EditableNumber.vue @@ -0,0 +1,65 @@ + + + diff --git a/client/components/ModelAssetCard.vue b/client/components/ModelAssetCard.vue new file mode 100644 index 00000000..940e5ab9 --- /dev/null +++ b/client/components/ModelAssetCard.vue @@ -0,0 +1,70 @@ + + + diff --git a/client/components/PerformanceMonitor.vue b/client/components/PerformanceMonitor.vue index fd593ae0..85d69bcc 100644 --- a/client/components/PerformanceMonitor.vue +++ b/client/components/PerformanceMonitor.vue @@ -1,105 +1,259 @@ diff --git a/client/components/SceneGraphItem.vue b/client/components/SceneGraphItem.vue deleted file mode 100644 index d468a3d0..00000000 --- a/client/components/SceneGraphItem.vue +++ /dev/null @@ -1,312 +0,0 @@ - - - diff --git a/client/components/TextureAssetCard.vue b/client/components/TextureAssetCard.vue new file mode 100644 index 00000000..c63c92f2 --- /dev/null +++ b/client/components/TextureAssetCard.vue @@ -0,0 +1,82 @@ + + + diff --git a/client/components/TheHeader.vue b/client/components/TheHeader.vue new file mode 100644 index 00000000..d40d99c6 --- /dev/null +++ b/client/components/TheHeader.vue @@ -0,0 +1,40 @@ + + + diff --git a/client/components/TreeGraph.vue b/client/components/TreeGraph.vue new file mode 100644 index 00000000..1b00ae72 --- /dev/null +++ b/client/components/TreeGraph.vue @@ -0,0 +1,107 @@ + + + diff --git a/client/components/inspector/GeometryBadge.vue b/client/components/inspector/GeometryBadge.vue new file mode 100644 index 00000000..85035c43 --- /dev/null +++ b/client/components/inspector/GeometryBadge.vue @@ -0,0 +1,79 @@ + + + diff --git a/client/components/inspector/MaterialBadge.vue b/client/components/inspector/MaterialBadge.vue new file mode 100644 index 00000000..3eb17572 --- /dev/null +++ b/client/components/inspector/MaterialBadge.vue @@ -0,0 +1,85 @@ + + + diff --git a/client/components/inspector/Props.vue b/client/components/inspector/Props.vue new file mode 100644 index 00000000..e7eee86e --- /dev/null +++ b/client/components/inspector/Props.vue @@ -0,0 +1,339 @@ + + + diff --git a/client/components/inspector/Tree.vue b/client/components/inspector/Tree.vue new file mode 100644 index 00000000..1a135636 --- /dev/null +++ b/client/components/inspector/Tree.vue @@ -0,0 +1,296 @@ + + + diff --git a/client/components/scene-graph/index.vue b/client/components/scene-graph/index.vue new file mode 100644 index 00000000..a1547639 --- /dev/null +++ b/client/components/scene-graph/index.vue @@ -0,0 +1,151 @@ + + + diff --git a/client/composables/useDevtoolsHook.ts b/client/composables/useDevtoolsHook.ts index a344653f..81fb9d92 100644 --- a/client/composables/useDevtoolsHook.ts +++ b/client/composables/useDevtoolsHook.ts @@ -1,8 +1,43 @@ -import type { TresContext, TresObject } from '@tresjs/core' +import type { TresObject, DevtoolsMessage } from '@tresjs/core' import type { Scene } from 'three' -import type { SceneGraphObject, ProgramObject } from '../types' -import { reactive } from '#imports' -import type { UnwrapNestedRefs } from '#imports' +import type { SceneGraphObject } from '../types' +import { reactive, shallowReactive } from '#imports' +import { createSharedComposable } from '@vueuse/core' +import type { UnwrapNestedRefs } from 'vue' +import { getSceneGraph } from '../utils/graph' + +// Define message data types for different message types +export interface ContextMessageData { + scene: { + value: Scene + } + renderer: { + instance: { + info: { + render: { + frame: number + calls: number + triangles: number + points: number + lines: number + } + memory: { + geometries: number + textures: number + } + programs: WebGLProgram[] + } + } + } +} + +export interface PerformanceMessageData { + fps: FPSState + memory: MemoryState +} + +// Union type for all possible message types +export type DevtoolsMessageData = ContextMessageData | PerformanceMessageData export interface FPSState { value: number @@ -47,123 +82,16 @@ export interface RendererState { export interface DevtoolsHookReturn { scene: { objects: number - graph: Record + graph: SceneGraphObject | null value: Scene | undefined + selected: TresObject | undefined + /* assets: AssetInfo[] */ } fps: FPSState memory: MemoryState renderer: RendererState } -const scene = reactive<{ - objects: number - graph: Record - value: Scene | undefined -}>({ - objects: 0, - graph: {}, - value: undefined, -}) - -const gl = { - fps: reactive({ - value: 0, - accumulator: [], - lastLoggedTime: Date.now(), - logInterval: 1000, - }), - memory: reactive({ - currentMem: 0, - averageMem: 0, - maxMemory: 0, - allocatedMem: 0, - accumulator: [], - lastLoggedTime: Date.now(), - logInterval: 1000, - }), - renderer: reactive({ - info: { - render: { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0, - }, - memory: { - geometries: 0, - textures: 0, - }, - programs: [], - }, - }), -} satisfies RendererType - -const icons: Record = { - scene: 'i-carbon-web-services-container', - perspectivecamera: 'i-carbon-video', - mesh: 'i-carbon-cube', - group: 'i-carbon-group-objects', - ambientlight: 'i-carbon-light', - directionallight: 'i-carbon-light', - spotlight: 'i-iconoir-project-curve-3d', - position: 'i-iconoir-axes', - rotation: 'i-carbon-rotate-clockwise', - scale: 'i-iconoir-ellipse-3d-three-points', - bone: 'i-ph-bone', - skinnedmesh: 'carbon:3d-print-mesh', -} - -function createNode(object: TresObject) { - const node: SceneGraphObject = { - name: object.name, - type: object.type, - icon: icons[object.type.toLowerCase()] || 'i-carbon-cube', - position: { - x: object.position.x, - y: object.position.y, - z: object.position.z, - }, - rotation: { - x: object.rotation.x, - y: object.rotation.y, - z: object.rotation.z, - }, - children: [], - } - - if (object.type === 'Mesh') { - node.material = object.material - node.geometry = object.geometry - node.scale = { - x: object.scale.x, - y: object.scale.y, - z: object.scale.z, - } - } - - if (object.type.includes('Light')) { - node.color = object.color.getHexString() - node.intensity = object.intensity - } - return node -} - -function getSceneGraph(scene: TresObject) { - function buildGraph(object: TresObject, node: SceneGraphObject) { - object.children.forEach((child: TresObject) => { - const childNode = createNode(child) - node.children.push(childNode) - buildGraph(child, childNode) - }) - } - - const root = createNode(scene) - buildGraph(scene, root) - - return root -} - function countObjectsInScene(scene: Scene) { let count = 0 @@ -177,31 +105,110 @@ function countObjectsInScene(scene: Scene) { return count } -export function useDevtoolsHook(): DevtoolsHookReturn { - // Connect with Core - const tresGlobalHook = { - cb(context: TresContext) { - scene.value = context.scene.value - scene.objects = countObjectsInScene(context.scene.value) - Object.assign(gl.renderer.info.render, context.renderer.value.info.render) - Object.assign(gl.renderer.info.memory, context.renderer.value.info.memory) - gl.renderer.info.programs = [...(context.renderer.value.info.programs || []) as unknown as ProgramObject[]] - Object.assign(gl.fps, context.perf.fps) - gl.fps.accumulator = [...context.perf.fps.accumulator] - Object.assign(gl.memory, context.perf.memory) - gl.memory.accumulator = [...context.perf.memory.accumulator] - scene.graph = getSceneGraph(context.scene.value as unknown as TresObject) - /* - console.log('Devtools hook updated', context.renderer.value.info.render.triangles) */ - }, +export interface DevtoolsState { + scene: { + objects: number + graph: SceneGraphObject | null + value: Scene | undefined + selected: TresObject | undefined + /* assets: AssetInfo[] */ } + fps: FPSState + memory: MemoryState + renderer: RendererState +} - window.parent.parent.__TRES__DEVTOOLS__ = tresGlobalHook +function _useDevtoolsHook(): DevtoolsHookReturn { + const state: DevtoolsState = { + scene: shallowReactive({ + objects: 0, + graph: null, + value: undefined, + selected: undefined, + assets: [], + }), + fps: shallowReactive({ + value: 0, + accumulator: [], + lastLoggedTime: Date.now(), + logInterval: 1000, + }), + memory: shallowReactive({ + currentMem: 0, + averageMem: 0, + maxMemory: 0, + allocatedMem: 0, + accumulator: [], + lastLoggedTime: Date.now(), + logInterval: 1000, + }), + renderer: reactive({ + info: { + render: { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0, + }, + memory: { + geometries: 0, + textures: 0, + }, + programs: [], + }, + }), + } - return { - scene, - fps: gl.fps, - memory: gl.memory, - renderer: gl.renderer, + let lastSceneUuid: string | null = null + + // Initialize DevtoolsMessenger if not exists + if (typeof window !== 'undefined' && window.parent.parent.__TRES__DEVTOOLS__) { + // Subscribe to different message types + window.parent.parent.__TRES__DEVTOOLS__.subscribe((message: DevtoolsMessage) => { + /* if (message.type === 'asset-load') { + const messageData = message.data as AssetLoadData + const formattedAsset = formatAsset(messageData) + state.scene.assets.push(formattedAsset) + } */ + + if (message.type === 'context') { + const context = message.data as ContextMessageData + if (context.scene.value.children.length > 0) { + // Use scene UUID for lightweight change detection + const currentSceneUuid = context.scene.value.uuid + if (currentSceneUuid !== lastSceneUuid) { + state.scene.value = context.scene.value + state.scene.objects = countObjectsInScene(context.scene.value) + state.scene.graph = getSceneGraph(context.scene.value as unknown as TresObject) + lastSceneUuid = currentSceneUuid + } + } + else { + state.scene.value = undefined + state.scene.graph = null + /* state.scene.assets = [] */ + lastSceneUuid = null + } + + // Update renderer info from context + if (context.renderer?.instance?.info) { + Object.assign(state.renderer.info.render, context.renderer.instance.info.render) + Object.assign(state.renderer.info.memory, context.renderer.instance.info.memory) + state.renderer.info.programs = [...(context.renderer.instance.info.programs || [])] + } + } + else if (message.type === 'performance') { + const performance = message.data as PerformanceMessageData + Object.assign(state.fps, performance.fps) + state.fps.accumulator = [...performance.fps.accumulator] + Object.assign(state.memory, performance.memory) + state.memory.accumulator = [...performance.memory.accumulator] + } + }) } + + return state } + +export const useDevtoolsHook = createSharedComposable(_useDevtoolsHook) diff --git a/client/env.d.ts b/client/env.d.ts new file mode 100644 index 00000000..68ef0859 --- /dev/null +++ b/client/env.d.ts @@ -0,0 +1,3 @@ +interface Window { + __TRES__DEVTOOLS__?: DevtoolsMessenger +} diff --git a/client/global.d.ts b/client/global.d.ts deleted file mode 100644 index 3281029e..00000000 --- a/client/global.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -interface Window { - __TRES__DEVTOOLS__?: { - cb: () => void - // You can add other properties of __TRES__DEVTOOLS__ here if needed - } -} diff --git a/client/nuxt.config.ts b/client/nuxt.config.ts index 9f45840e..c9d61ca9 100644 --- a/client/nuxt.config.ts +++ b/client/nuxt.config.ts @@ -3,9 +3,9 @@ import { resolve } from 'pathe' export default defineNuxtConfig({ modules: [ - '@nuxt/devtools-ui-kit', - '@unocss/nuxt', - '@nuxt/ui', + '@nuxt/ui-pro', + '@vueuse/nuxt', + /* '@nuxt/devtools-ui-kit', */ '@nuxt/icon', ], ssr: false, @@ -13,28 +13,28 @@ export default defineNuxtConfig({ app: { baseURL: '/__tres_nuxt_devtools', }, - - compatibilityDate: '2024-12-19', + css: ['~/assets/css/main.css'], nitro: { output: { publicDir: resolve(__dirname, '../dist/client'), }, }, - vite: { optimizeDeps: { include: [ '@nuxt/devtools-kit/iframe-client', ], }, + server: { + hmr: { + // Instead of go through proxy, we directly connect real port of the client app + clientPort: +(process.env.PORT || 3300), + }, + }, }, - icon: { - size: '24px', // default size applied - class: 'icon', // default class applied - aliases: { - mesh: 'carbon:cube', - }, + uiPro: { + license: process.env.NUXT_UI_PRO_LICENSE, }, }) diff --git a/client/package.json b/client/package.json index d102ad22..642b115c 100644 --- a/client/package.json +++ b/client/package.json @@ -1,14 +1,4 @@ { - "name": "tres-nuxt-module", - "type": "module", - "private": true, - "devDependencies": { - "@nuxt/devtools-ui-kit": "^2.4.1", - "@nuxt/icon": "^1.10.2", - "@nuxt/ui": "^2.20.0", - "@unocss/nuxt": "^66.1.2" - }, - "dependencies": { - "vue": "^3.5.14" - } + "name": "tres-nuxt-devtools", + "private": true } diff --git a/client/pages/index.vue b/client/pages/index.vue index b6a20a29..071ea83a 100644 --- a/client/pages/index.vue +++ b/client/pages/index.vue @@ -1,93 +1,61 @@