Skip to content
This repository was archived by the owner on Feb 1, 2026. It is now read-only.

Commit adc6c34

Browse files
committed
feat(components): add EditableColor component for color editing and enhance Props component to utilize it
1 parent 4201c7c commit adc6c34

5 files changed

Lines changed: 127 additions & 13 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
4+
interface Props {
5+
modelValue: number
6+
}
7+
8+
interface Emits {
9+
(e: 'update:modelValue', value: number): void
10+
}
11+
12+
const props = defineProps<Props>()
13+
const emit = defineEmits<Emits>()
14+
15+
const editValue = ref(props.modelValue)
16+
17+
const finishEditing = () => {
18+
emit('update:modelValue', editValue.value)
19+
}
20+
</script>
21+
22+
<template>
23+
<UPopover @blur="finishEditing">
24+
<UButton
25+
:label="editValue"
26+
color="neutral"
27+
size="xs"
28+
variant="soft"
29+
>
30+
<template #leading>
31+
<span
32+
:style="{
33+
backgroundColor: editValue,
34+
}"
35+
class="size-3 rounded-full"
36+
/>
37+
</template>
38+
</UButton>
39+
40+
<template #content>
41+
<UColorPicker
42+
v-model="editValue"
43+
class="p-2"
44+
@update:model-value="finishEditing"
45+
/>
46+
</template>
47+
</UPopover>
48+
</template>

client/components/EditableNumber.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { computed, ref, watch } from 'vue'
2+
import { computed, ref } from 'vue'
33
44
interface Props {
55
modelValue: number

client/components/inspector/Props.vue

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { TresObject } from '@tresjs/core'
3-
import { computed } from 'vue'
3+
import { computed, watch } from 'vue'
44
import { copyProp, copyValue, copyValueAsVector3, copyValueAsEuler, copyValueAsQuaternion } from '~/utils/clipboard'
55
66
import { iconsMap } from '../../utils/graph'
@@ -31,7 +31,7 @@ const importantProperties: Record<string, string[]> = {
3131
OrthographicCamera: ['position', 'rotation', 'left', 'right', 'top', 'bottom', 'near', 'far'],
3232
3333
// Light properties
34-
DirectionalLight: ['color', 'intensity', 'position', 'target'],
34+
DirectionalLight: ['color', 'intensity', 'position'],
3535
PointLight: ['color', 'intensity', 'position', 'distance', 'decay'],
3636
SpotLight: ['color', 'intensity', 'position', 'target', 'angle', 'penumbra'],
3737
AmbientLight: ['color', 'intensity'],
@@ -48,6 +48,14 @@ const importantProperties: Record<string, string[]> = {
4848
Scene: ['background', 'environment', 'fog'],
4949
}
5050
51+
function formatValue(key: string, value: unknown): unknown {
52+
// Handle Color objects
53+
if (value && typeof value === 'object' && 'r' in value && 'g' in value && 'b' in value && 'getHexString' in value) {
54+
return `#${(value as { getHexString: () => string }).getHexString()}`
55+
}
56+
return value
57+
}
58+
5159
/**
5260
* Get the important properties for the current object type
5361
*/
@@ -59,12 +67,17 @@ const keyProperties = computed(() => {
5967
const value = getNestedValue(props.object, prop)
6068
return {
6169
key: prop,
62-
value,
63-
displayValue: formatPropertyValue(prop, value),
70+
value: formatValue(prop, value),
71+
displayValue: formatPropertyDisplayValue(prop, value),
6472
}
6573
}).filter(prop => prop.value !== undefined)
6674
})
6775
76+
watch(keyProperties, (newProps) => {
77+
// Ensure Vector3 properties are reactive
78+
console.log('Key properties updated:', newProps)
79+
}, { immediate: true })
80+
6881
/**
6982
* Get nested property value (e.g., position.x, rotation.y)
7083
*/
@@ -79,7 +92,7 @@ function getNestedValue(obj: unknown, path: string): unknown {
7992
/**
8093
* Format property values for display (compact inspector style)
8194
*/
82-
function formatPropertyValue(key: string, value: unknown): string {
95+
function formatPropertyDisplayValue(key: string, value: unknown): string {
8396
if (value === null || value === undefined) return 'undefined'
8497
8598
// Handle Vector3-like objects - will be handled separately in template
@@ -237,12 +250,11 @@ function getValueClass(value: unknown): string {
237250
/>
238251

239252
<!-- Color value with preview dot -->
240-
<template v-else-if="prop.key === 'color' && prop.value && typeof prop.value === 'object' && 'getHexString' in prop.value">
241-
<div
242-
class="w-3 h-3 rounded-full border border-gray-300 dark:border-gray-600 ml-1 mr-1"
243-
:style="{ backgroundColor: '#' + (prop.value as { getHexString: () => string }).getHexString() }"
253+
<template v-else-if="prop.key === 'color'">
254+
<EditableColor
255+
v-model="prop.value"
256+
@update:model-value="(val) => emit('update-value', prop.key, val)"
244257
/>
245-
<span :class="getValueClass(prop.value)">{{ prop.displayValue }}</span>
246258
</template>
247259

248260
<EditableNumber

client/components/scene-graph/index.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import type { TresObject } from '@tresjs/core'
33
import type { Mesh } from 'three'
4+
import { Color } from 'three'
45
import { createHighlightMesh, getInspectorGraph } from '~/utils/graph'
56
import { useDevtoolsHook } from '~/composables/useDevtoolsHook'
67
import { computed, ref, shallowRef, toValue } from 'vue'
@@ -49,7 +50,10 @@ const handleValueUpdate = (path: string, value: unknown): void => {
4950
if (selectedObject.value) {
5051
setValueByPath(selectedObject.value, path, value)
5152
// Trigger reactive update
52-
refreshTrigger.value++
53+
if (!path.includes('color')) {
54+
// Avoid excessive updates for color changes
55+
refreshTrigger.value++
56+
}
5357
}
5458
}
5559
@@ -78,7 +82,12 @@ const setValueByPath = (obj: unknown, path: string, value: unknown): void => {
7882
7983
// Set the value on the final property
8084
const finalKey = keys[keys.length - 1]!
81-
current[finalKey] = value
85+
86+
// Check if value is a hex string and should be converted to Color
87+
const isHexString = (val: unknown): val is string =>
88+
typeof val === 'string' && /^#[0-9a-f]{6}$/i.test(val)
89+
90+
current[finalKey] = isHexString(value) ? new Color(value) : value
8291
8392
// Check if this is a camera and update projection matrix if needed
8493
const rootObj = obj as any
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script setup lang="ts">
2+
const { clearColor } = useControls({
3+
clearColor: '#000',
4+
}, {
5+
uuid: 'lights',
6+
})
7+
</script>
8+
9+
<template>
10+
<div class="w-full h-screen relative">
11+
<ClientOnly>
12+
<TresLeches uuid="lights" />
13+
</ClientOnly>
14+
<TresCanvas
15+
:clear-color="clearColor"
16+
shadows
17+
>
18+
<TresPerspectiveCamera />
19+
<TresMesh
20+
name="Im a pretty Box"
21+
:position="[0, 1, 0]"
22+
cast-shadow
23+
>
24+
<TresBoxGeometry />
25+
<TresMeshToonMaterial color="teal" />
26+
</TresMesh>
27+
<TresMesh
28+
ref="planeRef"
29+
v-log:material
30+
:rotation="[-Math.PI / 2, 0, 0]"
31+
receive-shadow
32+
>
33+
<TresPlaneGeometry :args="[10, 10, 10, 10]" />
34+
<TresMeshToonMaterial />
35+
</TresMesh>
36+
<TresDirectionalLight
37+
color="white"
38+
:intensity="1"
39+
:position="[5, 5, -5]"
40+
cast-shadow
41+
/>
42+
<OrbitControls />
43+
</TresCanvas>
44+
</div>
45+
</template>

0 commit comments

Comments
 (0)