Skip to content

Commit 1ca377f

Browse files
committed
fix UI
1 parent 88073d7 commit 1ca377f

File tree

5 files changed

+177
-92
lines changed

5 files changed

+177
-92
lines changed

src/components/load3d/Load3D.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@
6161
<div
6262
v-if="showRecordingControls"
6363
class="absolute top-12 right-2 z-20 pointer-events-auto"
64+
>
65+
<ViewerControls :node="node" />
66+
</div>
67+
68+
<div
69+
v-if="showRecordingControls"
70+
class="absolute top-24 right-2 z-20 pointer-events-auto"
6471
>
6572
<RecordingControls
6673
:node="node"
@@ -83,6 +90,7 @@ import { useI18n } from 'vue-i18n'
8390
import Load3DControls from '@/components/load3d/Load3DControls.vue'
8491
import Load3DScene from '@/components/load3d/Load3DScene.vue'
8592
import RecordingControls from '@/components/load3d/controls/RecordingControls.vue'
93+
import ViewerControls from '@/components/load3d/controls/ViewerControls.vue'
8694
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
8795
import {
8896
CameraType,

src/components/load3d/Load3dViewerContent.vue

Lines changed: 104 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,6 @@
66
@mouseenter="viewer.handleMouseEnter"
77
@mouseleave="viewer.handleMouseLeave"
88
>
9-
<div class="w-16 flex-col py-4 space-y-4">
10-
<Button
11-
v-for="item in menuItems"
12-
:key="item.id"
13-
class="w-12 h-12 rounded-lg items-center justify-center"
14-
:class="activePanel === item.id ? 'text-white' : 'text-gray-300'"
15-
@click="activePanel = item.id"
16-
>
17-
<i
18-
v-tooltip.right="{ value: item.title, showDelay: 300 }"
19-
:class="[item.icon, 'text-lg']"
20-
/>
21-
</Button>
22-
</div>
23-
249
<div ref="mainContentRef" class="flex-1 relative">
2510
<div
2611
ref="containerRef"
@@ -29,69 +14,109 @@
2914
/>
3015
</div>
3116

32-
<div class="w-64 p-4 flex flex-col">
33-
<div class="mb-4">
34-
{{ activePanelTitle }}
35-
</div>
36-
37-
<div class="flex-1 space-y-4">
38-
<div v-show="activePanel === 'scene'" class="space-y-4">
39-
<SceneControls
40-
v-model:background-color="viewer.backgroundColor.value"
41-
v-model:show-grid="viewer.showGrid.value"
42-
:has-background-image="viewer.hasBackgroundImage.value"
43-
@update-background-image="viewer.handleBackgroundImageUpdate"
44-
/>
45-
</div>
46-
47-
<div v-show="activePanel === 'model'" class="space-y-4">
48-
<ModelControls
49-
v-model:up-direction="viewer.upDirection.value"
50-
v-model:material-mode="viewer.materialMode.value"
51-
/>
17+
<div class="w-72 flex flex-col">
18+
<div class="flex-1 overflow-y-auto p-4">
19+
<div class="space-y-2">
20+
<Panel v-model:collapsed="panelStates.scene" toggleable>
21+
<template #header>
22+
<div class="flex items-center gap-2">
23+
<i class="pi pi-image" />
24+
<span>{{ t('load3d.viewer.sceneSettings') }}</span>
25+
</div>
26+
</template>
27+
<div class="p-4 space-y-4">
28+
<SceneControls
29+
v-model:background-color="viewer.backgroundColor.value"
30+
v-model:show-grid="viewer.showGrid.value"
31+
:has-background-image="viewer.hasBackgroundImage.value"
32+
@update-background-image="viewer.handleBackgroundImageUpdate"
33+
/>
34+
</div>
35+
</Panel>
36+
37+
<Panel v-model:collapsed="panelStates.model" toggleable>
38+
<template #header>
39+
<div class="flex items-center gap-2">
40+
<i class="pi pi-box" />
41+
<span>{{ t('load3d.viewer.modelSettings') }}</span>
42+
</div>
43+
</template>
44+
<div class="p-4 space-y-4">
45+
<ModelControls
46+
v-model:up-direction="viewer.upDirection.value"
47+
v-model:material-mode="viewer.materialMode.value"
48+
/>
49+
</div>
50+
</Panel>
51+
52+
<Panel v-model:collapsed="panelStates.camera" toggleable>
53+
<template #header>
54+
<div class="flex items-center gap-2">
55+
<i class="pi pi-camera" />
56+
<span>{{ t('load3d.viewer.cameraSettings') }}</span>
57+
</div>
58+
</template>
59+
<div class="p-4 space-y-4">
60+
<CameraControls
61+
v-model:camera-type="viewer.cameraType.value"
62+
v-model:fov="viewer.fov.value"
63+
/>
64+
</div>
65+
</Panel>
66+
67+
<Panel v-model:collapsed="panelStates.light" toggleable>
68+
<template #header>
69+
<div class="flex items-center gap-2">
70+
<i class="pi pi-sun" />
71+
<span>{{ t('load3d.viewer.lightSettings') }}</span>
72+
</div>
73+
</template>
74+
<div class="p-4 space-y-4">
75+
<LightControls
76+
v-model:light-intensity="viewer.lightIntensity.value"
77+
/>
78+
</div>
79+
</Panel>
80+
81+
<Panel v-model:collapsed="panelStates.export" toggleable>
82+
<template #header>
83+
<div class="flex items-center gap-2">
84+
<i class="pi pi-download" />
85+
<span>{{ t('load3d.viewer.exportSettings') }}</span>
86+
</div>
87+
</template>
88+
<div class="p-4 space-y-4">
89+
<ExportControls @export-model="viewer.exportModel" />
90+
</div>
91+
</Panel>
5292
</div>
93+
</div>
5394

54-
<div v-show="activePanel === 'camera'" class="space-y-4">
55-
<CameraControls
56-
v-model:camera-type="viewer.cameraType.value"
57-
v-model:fov="viewer.fov.value"
95+
<div class="p-4">
96+
<div class="flex gap-2">
97+
<Button
98+
icon="pi pi-times"
99+
severity="secondary"
100+
:label="t('g.cancel')"
101+
@click="handleCancel"
58102
/>
59-
</div>
60-
61-
<div v-show="activePanel === 'light'" class="space-y-4">
62-
<LightControls
63-
v-model:light-intensity="viewer.lightIntensity.value"
103+
<Button
104+
icon="pi pi-check"
105+
severity="secondary"
106+
:label="t('g.apply')"
107+
@click="handleConfirm"
64108
/>
65109
</div>
66-
67-
<div v-show="activePanel === 'export'" class="space-y-4">
68-
<ExportControls @export-model="viewer.exportModel" />
69-
</div>
70-
</div>
71-
72-
<div class="flex gap-2 mt-4">
73-
<Button
74-
icon="pi pi-times"
75-
severity="secondary"
76-
:label="t('g.cancel')"
77-
@click="handleCancel"
78-
/>
79-
<Button
80-
icon="pi pi-check"
81-
severity="secondary"
82-
:label="t('g.apply')"
83-
@click="handleConfirm"
84-
/>
85110
</div>
86111
</div>
87112
</div>
88113
</template>
89114

90115
<script setup lang="ts">
91116
import { LGraphNode } from '@comfyorg/litegraph'
92-
import { Tooltip } from 'primevue'
93117
import Button from 'primevue/button'
94-
import { computed, onBeforeUnmount, onMounted, ref, toRef } from 'vue'
118+
import Panel from 'primevue/panel'
119+
import { onBeforeUnmount, onMounted, ref, toRef } from 'vue'
95120
96121
import CameraControls from '@/components/load3d/controls/viewer/CameraControls.vue'
97122
import ExportControls from '@/components/load3d/controls/viewer/ExportControls.vue'
@@ -103,41 +128,26 @@ import { t } from '@/i18n'
103128
import { useLoad3dService } from '@/services/load3dService'
104129
import { useDialogStore } from '@/stores/dialogStore'
105130
106-
const vTooltip = Tooltip
107-
108131
const props = defineProps<{
109132
node: LGraphNode
110133
}>()
111134
112-
const activePanel = ref('scene')
113-
const menuItems = [
114-
{ id: 'scene', icon: 'pi pi-image', title: t('load3d.viewer.sceneSettings') },
115-
{ id: 'model', icon: 'pi pi-box', title: t('load3d.viewer.modelSettings') },
116-
{
117-
id: 'camera',
118-
icon: 'pi pi-camera',
119-
title: t('load3d.viewer.cameraSettings')
120-
},
121-
{ id: 'light', icon: 'pi pi-sun', title: t('load3d.viewer.lightSettings') },
122-
{
123-
id: 'export',
124-
icon: 'pi pi-download',
125-
title: t('load3d.viewer.exportSettings')
126-
}
127-
]
128-
129135
const viewerContentRef = ref<HTMLDivElement>()
130136
const containerRef = ref<HTMLDivElement>()
131137
const mainContentRef = ref<HTMLDivElement>()
132138
const maximized = ref(false)
133139
const mutationObserver = ref<MutationObserver | null>(null)
134140
135-
const viewer = useLoad3dViewer(toRef(props, 'node'))
136-
137-
const activePanelTitle = computed(() => {
138-
return menuItems.find((item) => item.id === activePanel.value)?.title || ''
141+
const panelStates = ref({
142+
scene: false,
143+
model: true,
144+
camera: true,
145+
light: true,
146+
export: true
139147
})
140148
149+
const viewer = useLoad3dViewer(toRef(props, 'node'))
150+
141151
onMounted(async () => {
142152
const source = useLoad3dService().getLoad3d(props.node)
143153
if (source && containerRef.value) {
@@ -196,3 +206,9 @@ onBeforeUnmount(() => {
196206
viewer.cleanup()
197207
})
198208
</script>
209+
210+
<style scoped>
211+
:deep(.p-panel-content) {
212+
padding: 0;
213+
}
214+
</style>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<div class="relative bg-gray-700 bg-opacity-30 rounded-lg">
3+
<div class="flex flex-col gap-2">
4+
<Button class="p-button-rounded p-button-text" @click="openIn3DViewer">
5+
<i
6+
v-tooltip.right="{
7+
value: t('load3d.openIn3DViewer'),
8+
showDelay: 300
9+
}"
10+
class="pi pi-expand text-white text-lg"
11+
/>
12+
</Button>
13+
</div>
14+
</div>
15+
</template>
16+
17+
<script setup lang="ts">
18+
import { LGraphNode } from '@comfyorg/litegraph'
19+
import { Tooltip } from 'primevue'
20+
import Button from 'primevue/button'
21+
22+
import Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'
23+
import { t } from '@/i18n'
24+
import { useDialogStore } from '@/stores/dialogStore'
25+
26+
const vTooltip = Tooltip
27+
28+
const { node } = defineProps<{
29+
node: LGraphNode
30+
}>()
31+
32+
const openIn3DViewer = () => {
33+
const props = { node: node }
34+
35+
useDialogStore().showDialog({
36+
key: 'global-load3d-viewer',
37+
title: t('load3d.viewer.title'),
38+
component: Load3DViewerContent,
39+
props: props,
40+
dialogComponentProps: {
41+
style: 'width: 80vw; height: 80vh;',
42+
maximizable: true
43+
}
44+
})
45+
}
46+
</script>
47+
48+
<style scoped></style>

src/composables/useLoad3dViewer.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { LGraphNode } from '@comfyorg/litegraph'
2-
import { Ref, ref, watch } from 'vue'
2+
import { Ref, ref, toRaw, watch } from 'vue'
33

44
import Load3d from '@/extensions/core/load3d/Load3d'
55
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
@@ -179,7 +179,8 @@ export const useLoad3dViewer = (node: Ref<LGraphNode>) => {
179179
cameraType.value = sourceCameraType
180180
backgroundColor.value = source.sceneManager.currentBackgroundColor
181181
showGrid.value = source.sceneManager.gridHelper.visible
182-
lightIntensity.value = source.lightingManager.lights[1]?.intensity || 1
182+
lightIntensity.value =
183+
(node.value.properties['Light Intensity'] as number) || 1
183184

184185
const backgroundInfo = source.sceneManager.getCurrentBackgroundInfo()
185186
if (
@@ -216,6 +217,16 @@ export const useLoad3dViewer = (node: Ref<LGraphNode>) => {
216217
materialMode: materialMode.value,
217218
edgeThreshold: edgeThreshold.value
218219
}
220+
221+
const width = node.value.widgets?.find((w) => w.name === 'width')
222+
const height = node.value.widgets?.find((w) => w.name === 'height')
223+
224+
if (width && height) {
225+
load3d.setTargetSize(
226+
toRaw(width).value as number,
227+
toRaw(height).value as number
228+
)
229+
}
219230
} catch (error) {
220231
console.error('Error initializing Load3d viewer:', error)
221232
useToastStore().addAlert(

src/locales/en/main.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,7 +1422,8 @@
14221422
"lightSettings": "Light Settings",
14231423
"exportSettings": "Export Settings",
14241424
"modelSettings": "Model Settings"
1425-
}
1425+
},
1426+
"openIn3DViewer": "Open in 3D Viewer"
14261427
},
14271428
"toastMessages": {
14281429
"nothingToQueue": "Nothing to queue",
@@ -1460,7 +1461,8 @@
14601461
"useApiKeyTip": "Tip: Can't access normal login? Use the Comfy API Key option.",
14611462
"nothingSelected": "Nothing selected",
14621463
"cannotCreateSubgraph": "Cannot create subgraph",
1463-
"failedToConvertToSubgraph": "Failed to convert items to subgraph"
1464+
"failedToConvertToSubgraph": "Failed to convert items to subgraph",
1465+
"failedToInitializeLoad3dViewer": "Failed to initialize 3D Viewer"
14641466
},
14651467
"auth": {
14661468
"apiKey": {

0 commit comments

Comments
 (0)