mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-12 08:30:08 +00:00
## Summary - Replace hardcoded `text-white` class with theme-aware alternatives to fix invisible text on light themes - Update Load3D control backgrounds to use semantic tokens - Update dropdown menus to use `bg-interface-menu-surface` - Update overlay backgrounds to use `bg-backdrop` with opacity ## Changes | Component | Old | New | |-----------|-----|-----| | Text on primary bg | `text-white` | `text-base-foreground` | | Dropdown menus | `bg-black/50` | `bg-interface-menu-surface` | | Control panels | `bg-smoke-700/30` | `bg-backdrop/30` | | Loading overlays | `bg-black bg-opacity-50` | `bg-backdrop/50` | | Selected states | `bg-smoke-600` | `bg-button-active-surface` | ## Files Modified (14) - `src/components/TopMenuSection.vue` - `src/components/input/MultiSelect.vue` - `src/components/load3d/*.vue` (12 files) - `src/renderer/extensions/vueNodes/VideoPreview.vue` ## Test plan - [ ] Verify text visibility in light theme - [ ] Verify text visibility in dark theme - [ ] Test Load3D viewer controls functionality - [ ] Test MultiSelect dropdown checkbox visibility ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7908-fix-replace-text-white-with-theme-aware-color-tokens-2e26d73d36508107bb01d1d6e3b74f6a) by [Unito](https://www.unito.io)
204 lines
5.6 KiB
Vue
204 lines
5.6 KiB
Vue
<template>
|
|
<div
|
|
class="pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30"
|
|
@pointerdown.stop
|
|
@pointermove.stop
|
|
@pointerup.stop
|
|
@wheel.stop
|
|
>
|
|
<div class="show-menu relative">
|
|
<Button
|
|
variant="textonly"
|
|
size="icon"
|
|
:aria-label="$t('menu.showMenu')"
|
|
class="rounded-full"
|
|
@click="toggleMenu"
|
|
>
|
|
<i class="pi pi-bars text-lg text-base-foreground" />
|
|
</Button>
|
|
|
|
<div
|
|
v-show="isMenuOpen"
|
|
class="absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg"
|
|
>
|
|
<div class="flex flex-col">
|
|
<Button
|
|
v-for="category in availableCategories"
|
|
:key="category"
|
|
variant="textonly"
|
|
:class="
|
|
cn(
|
|
'flex w-full items-center justify-start',
|
|
activeCategory === category && 'bg-button-active-surface'
|
|
)
|
|
"
|
|
@click="selectCategory(category)"
|
|
>
|
|
<i :class="getCategoryIcon(category)" />
|
|
<span class="whitespace-nowrap text-base-foreground">{{
|
|
$t(categoryLabels[category])
|
|
}}</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-show="activeCategory" class="rounded-lg bg-smoke-700/30">
|
|
<SceneControls
|
|
v-if="showSceneControls"
|
|
v-model:show-grid="sceneConfig!.showGrid"
|
|
v-model:background-color="sceneConfig!.backgroundColor"
|
|
v-model:background-image="sceneConfig!.backgroundImage"
|
|
v-model:background-render-mode="sceneConfig!.backgroundRenderMode"
|
|
v-model:fov="cameraConfig!.fov"
|
|
@update-background-image="handleBackgroundImageUpdate"
|
|
/>
|
|
|
|
<ModelControls
|
|
v-if="showModelControls"
|
|
v-model:material-mode="modelConfig!.materialMode"
|
|
v-model:up-direction="modelConfig!.upDirection"
|
|
v-model:show-skeleton="modelConfig!.showSkeleton"
|
|
:hide-material-mode="isSplatModel"
|
|
:is-ply-model="isPlyModel"
|
|
:has-skeleton="hasSkeleton"
|
|
/>
|
|
|
|
<CameraControls
|
|
v-if="showCameraControls"
|
|
v-model:camera-type="cameraConfig!.cameraType"
|
|
v-model:fov="cameraConfig!.fov"
|
|
/>
|
|
|
|
<LightControls
|
|
v-if="showLightControls"
|
|
v-model:light-intensity="lightConfig!.intensity"
|
|
v-model:material-mode="modelConfig!.materialMode"
|
|
/>
|
|
|
|
<ExportControls
|
|
v-if="showExportControls"
|
|
@export-model="handleExportModel"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
import CameraControls from '@/components/load3d/controls/CameraControls.vue'
|
|
import ExportControls from '@/components/load3d/controls/ExportControls.vue'
|
|
import LightControls from '@/components/load3d/controls/LightControls.vue'
|
|
import ModelControls from '@/components/load3d/controls/ModelControls.vue'
|
|
import SceneControls from '@/components/load3d/controls/SceneControls.vue'
|
|
import Button from '@/components/ui/button/Button.vue'
|
|
import type {
|
|
CameraConfig,
|
|
LightConfig,
|
|
ModelConfig,
|
|
SceneConfig
|
|
} from '@/extensions/core/load3d/interfaces'
|
|
import { cn } from '@/utils/tailwindUtil'
|
|
|
|
const {
|
|
isSplatModel = false,
|
|
isPlyModel = false,
|
|
hasSkeleton = false
|
|
} = defineProps<{
|
|
isSplatModel?: boolean
|
|
isPlyModel?: boolean
|
|
hasSkeleton?: boolean
|
|
}>()
|
|
|
|
const sceneConfig = defineModel<SceneConfig>('sceneConfig')
|
|
const modelConfig = defineModel<ModelConfig>('modelConfig')
|
|
const cameraConfig = defineModel<CameraConfig>('cameraConfig')
|
|
const lightConfig = defineModel<LightConfig>('lightConfig')
|
|
|
|
const isMenuOpen = ref(false)
|
|
const activeCategory = ref<string>('scene')
|
|
const categoryLabels: Record<string, string> = {
|
|
scene: 'load3d.scene',
|
|
model: 'load3d.model',
|
|
camera: 'load3d.camera',
|
|
light: 'load3d.light',
|
|
export: 'load3d.export'
|
|
}
|
|
|
|
const availableCategories = computed(() => {
|
|
if (isSplatModel) {
|
|
return ['scene', 'model', 'camera']
|
|
}
|
|
|
|
return ['scene', 'model', 'camera', 'light', 'export']
|
|
})
|
|
|
|
const showSceneControls = computed(
|
|
() => activeCategory.value === 'scene' && !!sceneConfig.value
|
|
)
|
|
const showModelControls = computed(
|
|
() => activeCategory.value === 'model' && !!modelConfig.value
|
|
)
|
|
const showCameraControls = computed(
|
|
() => activeCategory.value === 'camera' && !!cameraConfig.value
|
|
)
|
|
const showLightControls = computed(
|
|
() =>
|
|
activeCategory.value === 'light' &&
|
|
!!lightConfig.value &&
|
|
!!modelConfig.value
|
|
)
|
|
const showExportControls = computed(() => activeCategory.value === 'export')
|
|
|
|
const toggleMenu = () => {
|
|
isMenuOpen.value = !isMenuOpen.value
|
|
}
|
|
|
|
const selectCategory = (category: string) => {
|
|
activeCategory.value = category
|
|
isMenuOpen.value = false
|
|
}
|
|
|
|
const getCategoryIcon = (category: string) => {
|
|
const icons = {
|
|
scene: 'pi pi-image',
|
|
model: 'pi pi-box',
|
|
camera: 'pi pi-camera',
|
|
light: 'pi pi-sun',
|
|
export: 'pi pi-download'
|
|
}
|
|
// @ts-expect-error fixme ts strict error
|
|
return `${icons[category]} text-base-foreground text-lg`
|
|
}
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'updateBackgroundImage', file: File | null): void
|
|
(e: 'exportModel', format: string): void
|
|
}>()
|
|
|
|
const handleBackgroundImageUpdate = (file: File | null) => {
|
|
emit('updateBackgroundImage', file)
|
|
}
|
|
|
|
const handleExportModel = (format: string) => {
|
|
emit('exportModel', format)
|
|
}
|
|
|
|
const closeSlider = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement
|
|
|
|
if (!target.closest('.show-menu')) {
|
|
isMenuOpen.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
document.addEventListener('click', closeSlider)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
document.removeEventListener('click', closeSlider)
|
|
})
|
|
</script>
|