11<script setup lang="ts">
2+ import { Node } from ' three/webgpu'
23import { computed , ref } from ' vue'
34import type { InspectorNode } from ' ~/client/types'
45
@@ -46,6 +47,37 @@ const getLabel = (value: unknown): string => {
4647}
4748
4849// Copy functionality
50+
51+ const copyPath = async (path : string ): Promise <void > => {
52+ try {
53+ await navigator .clipboard .writeText (path )
54+ }
55+ catch (error ) {
56+ console .error (' Failed to copy path:' , error )
57+ }
58+ }
59+
60+ const copyProp = async (node : InspectorNode ): Promise <void > => {
61+ try {
62+ const propString = ` :${node .path .replace (/ \. / g , ' -' )}="${typeof node .value === ' string' ? node .value : JSON .stringify (node .value )}" `
63+ await navigator .clipboard .writeText (propString )
64+ }
65+ catch (error ) {
66+ console .error (' Failed to copy prop:' , error )
67+ }
68+ }
69+
70+ const copyPropAsArray = async (node : InspectorNode ): Promise <void > => {
71+ try {
72+ const arrayValue = node .children ?.map (child => child .value ) || []
73+ const propString = ` :${node .path .replace (/ \. / g , ' -' )}='[${arrayValue .map (v => (typeof v === ' string' ? ` "${v }" ` : v )).join (' , ' )}]' `
74+ await navigator .clipboard .writeText (propString )
75+ }
76+ catch (error ) {
77+ console .error (' Failed to copy prop as array:' , error )
78+ }
79+ }
80+
4981const copyValue = async (value : unknown ): Promise <void > => {
5082 try {
5183 const stringValue = typeof value === ' string' ? value : JSON .stringify (value )
@@ -56,15 +88,41 @@ const copyValue = async (value: unknown): Promise<void> => {
5688 }
5789}
5890
59- const copyPath = async (path : string ): Promise <void > => {
91+ const copyValueAsVector3 = async (node : InspectorNode ): Promise <void > => {
6092 try {
61- await navigator .clipboard .writeText (path )
93+ await navigator .clipboard .writeText (` new Vector3(${ node . children [ 0 ]. value }, ${ node . children [ 1 ]. value }, ${ node . children [ 2 ]. value }) ` )
6294 }
6395 catch (error ) {
64- console .error (' Failed to copy path:' , error )
96+ console .error (' Failed to copy prop as Vector3:' , error )
97+ }
98+ }
99+
100+ const copyValueAsArray = async (node : InspectorNode ): Promise <void > => {
101+ try {
102+ const arrayValue = node .children ?.map (child => child .value ) || []
103+ const propString = ` [${arrayValue .map (v => (typeof v === ' string' ? ` "${v }" ` : v )).join (' , ' )}] `
104+ await navigator .clipboard .writeText (propString )
105+ }
106+ catch (error ) {
107+ console .error (' Failed to copy prop as array:' , error )
65108 }
66109}
67110
111+ const copyValueAsJSON = async (node : InspectorNode ): Promise <void > => {
112+ try {
113+ let object = {}
114+ if (node .children && node .children .length > 0 ) {
115+ object = node .children .reduce ((acc , child ) => {
116+ acc [child .label ] = child .value
117+ return acc
118+ }, {} as Record <string , unknown >)
119+ }
120+ await navigator .clipboard .writeText (JSON .stringify (object , null , 2 ))
121+ }
122+ catch (error ) {
123+ console .error (' Failed to copy value as JSON:' , error )
124+ }
125+ }
68126// Value modification
69127const incrementValue = (): void => {
70128 if (typeof props .node .value === ' number' ) {
@@ -171,6 +229,29 @@ const indentStyle = computed(() => ({ paddingLeft: `${props.level * 16}px` }))
171229 >
172230 {{ node.label }} : <span class =" text-gray-600 font-semibold" >{{ node.value }}</span >
173231 </span >
232+
233+ <UDropdownMenu
234+ v-if =" node.type !== 'array'"
235+ size =" xs"
236+ :items =" [
237+ { label: 'Copy Path', icon: 'i-lucide:link', onSelect: () => copyPath(node.path) },
238+ { label: 'Copy value as Array', icon: 'i-material-symbols:data-array', onSelect: () => copyValueAsArray(node) },
239+ node.value === '_Vector3' ? { label: 'Copy value as Vector3', icon: 'i-lucide:pen-line', onSelect: () => copyValueAsVector3(node) } : null,
240+ { label: 'Copy value as JSON', icon: 'i-material-symbols:data-object', onSelect: () => copyValueAsJSON(node) },
241+ node.value === '_Vector3' || node.value === '_Euler' ? { label: 'Copy as Prop', icon: 'i-lucide:code', onSelect: () => copyPropAsArray(node) } : null,
242+ ].filter(Boolean)"
243+ :ui =" {
244+ content: 'w-48',
245+ }"
246+ >
247+ <UButton
248+ size =" xs"
249+ variant =" ghost"
250+ color =" gray"
251+ icon =" i-lucide-ellipsis-vertical"
252+ title =" Copy value"
253+ />
254+ </UDropdownMenu >
174255 </template >
175256
176257 <!-- Primitive value display -->
@@ -233,26 +314,6 @@ const indentStyle = computed(() => ({ paddingLeft: `${props.level * 16}px` }))
233314 class =" flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity ml-auto"
234315 @click.stop
235316 >
236- <!-- Copy value button -->
237- <UButton
238- size =" xs"
239- variant =" ghost"
240- color =" gray"
241- icon =" i-tabler:copy"
242- title =" Copy value"
243- @click.stop =" copyValue(node.value)"
244- />
245-
246- <!-- Copy path button -->
247- <UButton
248- size =" xs"
249- variant =" ghost"
250- color =" gray"
251- icon =" i-tabler:link"
252- title =" Copy path"
253- @click.stop =" copyPath(node.path)"
254- />
255-
256317 <!-- Number controls -->
257318 <template v-if =" typeof node .value === ' number' " >
258319 <UButton
@@ -272,6 +333,25 @@ const indentStyle = computed(() => ({ paddingLeft: `${props.level * 16}px` }))
272333 @click.stop =" incrementValue"
273334 />
274335 </template >
336+ <UDropdownMenu
337+ size =" xs"
338+ :items =" [
339+ { label: 'Copy Value', icon: 'i-lucide:copy', onSelect: () => copyValue(node.value) },
340+ { label: 'Copy Path', icon: 'i-lucide:link', onSelect: () => copyPath(node.path) },
341+ { label: 'Copy as prop', icon: 'i-lucide:pen-line', onSelect: () => copyProp(node) },
342+ ]"
343+ :ui =" {
344+ content: 'w-48',
345+ }"
346+ >
347+ <UButton
348+ size =" xs"
349+ variant =" ghost"
350+ color =" gray"
351+ icon =" i-lucide-ellipsis-vertical"
352+ title =" Copy value"
353+ />
354+ </UDropdownMenu >
275355 </div >
276356 </div >
277357 </div >
0 commit comments