Files
ComfyUI_frontend/src/components/load3d/controls/viewer/ViewerExportControls.vue
Kelly Yang 5ebf5e03ae refactor(load3d): replace PrimeVue Select/Slider/Checkbox with Reka UI (#12020)
Replace PrimeVue components in 3D node viewer controls with the
project's Reka UI equivalents across 7 files.

## Changes

| File | Replaced |
|------|---------|
| `AnimationControls.vue` | `Select` × 2 (speed + animation) |
| `ViewerModelControls.vue` | `Select` × 2 (up direction + material
mode) |
| `ViewerCameraControls.vue` | `Select` + `Slider` (camera type + FOV) |
| `ViewerExportControls.vue` | `Select` (export format) |
| `PopupSlider.vue` | `Slider` |
| `ViewerLightControls.vue` | `Slider` |
| `ViewerSceneControls.vue` | `Checkbox` → native `<input
type="checkbox">` |

## Implementation notes

- `Select` uses `@/components/ui/select/*` compound components. Numeric
model values (animation speed index) are stringified at the binding
boundary and converted back on update, matching Reka `SelectRoot`'s
`string`-only `modelValue` contract.
- `Slider` uses `@/components/ui/slider/Slider.vue`. Single-number
`defineModel` values are wrapped in a `computed` array and unwrapped in
the update handler, following the pattern established in
`LightControls.vue`.
- No new Reka UI wrapper components were created — existing ui/select
and ui/slider primitives were used directly.

## Test 

https://github.com/user-attachments/assets/afca0fc8-a7b6-49ee-b221-ee5725bd127e
1. AnimationControls.vue
- **Add Load3D node** → Upload an animated GLB file (e.g., a character
model).
- **Node preview top bar:** Play/Pause button, speed dropdown, animation
name dropdown, and progress bar.

2. PopupSlider.vue
- **Hover over Load3D preview:** Icon buttons appear in the left
toolbar.
- **"Light Intensity" button (bulb icon)** → Slider pops up on the
right.
- **"FOV" button (view icon)** → Slider pops up on the right.

3. ViewerCameraControls.vue
- **Load3D node** → Settings panel (top-right) → **"Camera"** tab.
- **Features:** Camera type dropdown (Perspective / Orthographic), FOV
slider (visible in Perspective mode).

4. ViewerExportControls.vue
- **Settings panel** → **"Export"** tab.
- **Features:** Format dropdown (GLB / OBJ / STL), Export button.

5. ViewerLightControls.vue
- **Settings panel** → **"Light"** tab.
- **Features:** Light intensity slider.

6. ViewerModelControls.vue
- **Settings panel** → **"Model"** tab.
- **Features:** "Up direction" dropdown, Material mode dropdown
(Wireframe / Normal, etc.).

7. ViewerSceneControls.vue
- **Settings panel** → **"Scene"** tab.
- **Features:** Background color picker, "Show grid" checkbox, upload
background image button.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> UI component swap touches multiple interactive viewer controls
(selects/sliders/checkbox), so small binding/typing differences (string
vs number, array slider values) could cause subtle regressions despite
test updates.
> 
> **Overview**
> Replaces PrimeVue `Select`, `Slider`, and `Checkbox` usages across
Load3D viewer controls with the project’s Reka UI-based primitives
(`@/components/ui/select/*`, `@/components/ui/slider/Slider.vue`) and a
native checkbox.
> 
> Updates v-model wiring to match the new components’ contracts: selects
now bind via string `modelValue` with explicit number casting where
needed, and sliders now wrap single numeric values into `[number]`
arrays with corresponding update handlers. Unit tests are updated to
mock the new UI components and their updated event/value shapes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
46f99db256. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->



┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-12020-refactor-load3d-replace-PrimeVue-Select-Slider-Checkbox-with-Reka-UI-3586d73d365081f58601d93031016afd)
by [Unito](https://www.unito.io)
2026-05-06 19:30:25 -04:00

54 lines
1.3 KiB
Vue

<template>
<div class="space-y-4">
<Select v-model="exportFormat">
<SelectTrigger size="md">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="fmt in exportFormats"
:key="fmt.value"
:value="fmt.value"
>
{{ fmt.label }}
</SelectItem>
</SelectContent>
</Select>
<Button
variant="muted-textonly"
class="rounded-full"
@click="exportModel(exportFormat)"
>
{{ $t('load3d.export') }}
</Button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Button from '@/components/ui/button/Button.vue'
import Select from '@/components/ui/select/Select.vue'
import SelectContent from '@/components/ui/select/SelectContent.vue'
import SelectItem from '@/components/ui/select/SelectItem.vue'
import SelectTrigger from '@/components/ui/select/SelectTrigger.vue'
import SelectValue from '@/components/ui/select/SelectValue.vue'
const emit = defineEmits<{
(e: 'exportModel', format: string): void
}>()
const exportFormats = [
{ label: 'GLB', value: 'glb' },
{ label: 'OBJ', value: 'obj' },
{ label: 'STL', value: 'stl' }
]
const exportFormat = ref('obj')
const exportModel = (format: string) => {
emit('exportModel', format)
}
</script>