Wip control widget state migration

This commit is contained in:
Austin Mroz
2025-11-27 12:33:14 -08:00
parent 5187a77234
commit 3eece91eb6
7 changed files with 113 additions and 126 deletions

View File

@@ -3,7 +3,8 @@
* Provides event-driven reactivity with performance optimizations
*/
import { reactiveComputed } from '@vueuse/core'
import { reactive, shallowReactive } from 'vue'
import { reactive, ref, shallowReactive, watch } from 'vue'
import type { Ref } from 'vue'
import { useChainCallback } from '@/composables/functional/useChainCallback'
import type {
@@ -20,7 +21,7 @@ import type { NodeId } from '@/renderer/core/layout/types'
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
import { isDOMWidget } from '@/scripts/domWidget'
import { useNodeDefStore } from '@/stores/nodeDefStore'
import type { WidgetValue, SafeControlWidget } from '@/types/simplifiedWidget'
import type { WidgetValue, ControlOptions } from '@/types/simplifiedWidget'
import { normalizeControlOption } from '@/types/simplifiedWidget'
import type {
@@ -42,14 +43,14 @@ export interface SafeWidgetData {
name: string
type: string
value: WidgetValue
borderStyle?: string
callback?: ((value: unknown) => void) | undefined
controlWidget?: () => Ref<ControlOptions>
isDOMWidget?: boolean
label?: string
options?: IWidgetOptions<unknown>
callback?: ((value: unknown) => void) | undefined
spec?: InputSpec
slotMetadata?: WidgetSlotMetadata
isDOMWidget?: boolean
controlWidget?: SafeControlWidget
borderStyle?: string
spec?: InputSpec
}
export interface VueNodeData {
@@ -86,15 +87,19 @@ export interface GraphNodeManager {
cleanup(): void
}
function getControlWidget(widget: IBaseWidget): SafeControlWidget | undefined {
function getControlWidget(widget: IBaseWidget): (() => Ref<ControlOptions>)|undefined {
const cagWidget = widget.linkedWidgets?.find(
(w) => w.name == 'control_after_generate'
)
if (!cagWidget) return
return {
value: normalizeControlOption(cagWidget.value),
update: (value) => (cagWidget.value = normalizeControlOption(value))
}
const cagRef = ref<ControlOptions>(
normalizeControlOption(cagWidget.value)
)
watch(cagRef, (value) => {
cagWidget.value = normalizeControlOption(value)
cagWidget.callback?.(cagWidget.value)
})
return () => cagRef
}
export function safeWidgetMapper(

View File

@@ -3,15 +3,15 @@ import Button from 'primevue/button'
import Popover from 'primevue/popover'
import ToggleSwitch from 'primevue/toggleswitch'
import { computed, ref } from 'vue'
import type { Ref } from 'vue'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useDialogService } from '@/services/dialogService'
import { NumberControlMode } from '../composables/useStepperControl'
import type { ControlOptions } from '@/types/simplifiedWidget'
type ControlOption = {
description: string
mode: NumberControlMode
mode: ControlOptions
icon?: string
text?: string
title: string
@@ -26,33 +26,21 @@ const toggle = (event: Event) => {
}
defineExpose({ toggle })
const ENABLE_LINK_TO_GLOBAL = false
const controlOptions: ControlOption[] = [
...(ENABLE_LINK_TO_GLOBAL
? ([
{
mode: NumberControlMode.LINK_TO_GLOBAL,
icon: 'pi pi-link',
title: 'linkToGlobal',
description: 'linkToGlobalDesc'
} satisfies ControlOption
] as ControlOption[])
: []),
{
mode: NumberControlMode.RANDOMIZE,
mode: 'randomize',
icon: 'icon-[lucide--shuffle]',
title: 'randomize',
description: 'randomizeDesc'
},
{
mode: NumberControlMode.INCREMENT,
mode: 'increment',
text: '+1',
title: 'increment',
description: 'incrementDesc'
},
{
mode: NumberControlMode.DECREMENT,
mode: 'decrement',
text: '-1',
title: 'decrement',
description: 'decrementDesc'
@@ -64,20 +52,16 @@ const widgetControlMode = computed(() =>
)
const props = defineProps<{
controlMode: NumberControlMode
controlWidget: () => Ref<ControlOptions>
}>()
const emit = defineEmits<{
'update:controlMode': [mode: NumberControlMode]
}>()
const handleToggle = (mode: NumberControlMode) => {
if (props.controlMode === mode) return
emit('update:controlMode', mode)
const handleToggle = (mode: ControlOptions) => {
if (props.controlWidget().value === mode) return
props.controlWidget().value = mode
}
const isActive = (mode: NumberControlMode) => {
return props.controlMode === mode
const isActive = (mode: ControlOptions) => {
return props.controlWidget().value === mode
}
const handleEditSettings = () => {
@@ -131,10 +115,6 @@ const handleEditSettings = () => {
<div
class="text-sm font-normal text-base-foreground leading-tight"
>
<span v-if="option.mode === NumberControlMode.LINK_TO_GLOBAL">
{{ $t('widgets.numberControl.linkToGlobal') }}
<em>{{ $t('widgets.numberControl.linkToGlobalSeed') }}</em>
</span>
<span v-else>
{{ $t(`widgets.numberControl.${option.title}`) }}
</span>

View File

@@ -1,11 +1,14 @@
<script setup lang="ts">
import { computed } from 'vue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import type {
SimplifiedControlWidget,
SimplifiedWidget
} from '@/types/simplifiedWidget'
import WidgetInputNumberInput from './WidgetInputNumberInput.vue'
import WidgetInputNumberSlider from './WidgetInputNumberSlider.vue'
import WidgetInputNumberWithControl from './WidgetInputNumberWithControl.vue'
import WidgetWithControl from './WidgetWithControl.vue'
const props = defineProps<{
widget: SimplifiedWidget<number>
@@ -19,14 +22,22 @@ const hasControlAfterGenerate = computed(() => {
</script>
<template>
<WidgetWithControl
v-if="hasControlAfterGenerate"
:widget="widget as SimplifiedControlWidget<number>"
:comp="
widget.type === 'slider'
? WidgetInputNumberSlider
: WidgetInputNumberInput
"
/>
<component
:is="
hasControlAfterGenerate
? WidgetInputNumberWithControl
: widget.type === 'slider'
? WidgetInputNumberSlider
: WidgetInputNumberInput
widget.type === 'slider'
? WidgetInputNumberSlider
: WidgetInputNumberInput
"
v-else
v-model="modelValue"
:widget="widget"
v-bind="$attrs"

View File

@@ -1,67 +0,0 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { defineAsyncComponent, ref } from 'vue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import type { NumberControlMode } from '../composables/useStepperControl'
import { useStepperControl } from '../composables/useStepperControl'
import WidgetInputNumberInput from './WidgetInputNumberInput.vue'
const NumberControlPopover = defineAsyncComponent(
() => import('./NumberControlPopover.vue')
)
const props = defineProps<{
widget: SimplifiedWidget<number>
}>()
const modelValue = defineModel<number>({ default: 0 })
const popover = ref()
const handleControlChange = (newValue: number) => {
modelValue.value = newValue
}
const { controlMode, controlButtonIcon } = useStepperControl(
modelValue,
{
...props.widget.options,
onChange: handleControlChange
},
props.widget.controlWidget!.value
)
const setControlMode = (mode: NumberControlMode) => {
controlMode.value = mode
props.widget.controlWidget!.update(mode)
}
const togglePopover = (event: Event) => {
popover.value.toggle(event)
}
</script>
<template>
<div class="relative grid grid-cols-subgrid">
<WidgetInputNumberInput
v-model="modelValue"
:widget
class="grid grid-cols-subgrid col-span-2"
>
<Button
variant="link"
size="small"
class="h-4 w-7 self-center rounded-xl bg-blue-100/30 p-0"
@click="togglePopover"
>
<i :class="`${controlButtonIcon} text-blue-100 text-xs`" />
</Button>
</WidgetInputNumberInput>
<NumberControlPopover
ref="popover"
:control-mode
@update:control-mode="setControlMode"
/>
</div>
</template>

View File

@@ -146,9 +146,11 @@ const outputItems = computed<DropdownItem[]>(() => {
})
const allItems = computed<DropdownItem[]>(() => {
if (props.isAssetMode && assetData) {
return assetData.dropdownItems.value
}
return [...inputItems.value, ...outputItems.value]
})
const dropdownItems = computed<DropdownItem[]>(() => {
if (props.isAssetMode) {
return allItems.value
@@ -161,7 +163,7 @@ const dropdownItems = computed<DropdownItem[]>(() => {
return outputItems.value
case 'all':
default:
return [...inputItems.value, ...outputItems.value]
return allItems.value
}
})

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { computed, defineAsyncComponent, ref } from 'vue'
import type { Ref } from 'vue'
import type {
ControlOptions,
SimplifiedControlWidget
} from '@/types/simplifiedWidget'
const NumberControlPopover = defineAsyncComponent(
() => import('./NumberControlPopover.vue')
)
function useControlButtonIcon(controlMode: Ref<ControlOptions>) {
return computed(() => {
switch (controlMode.value) {
case 'increment':
return 'pi pi-plus'
case 'decrement':
return 'pi pi-minus'
case 'fixed':
return 'icon-[lucide--pencil-off]'
default:
return 'icon-[lucide--shuffle]'
}
})
}
const props = defineProps<{
widget: SimplifiedControlWidget<number>
comp: unknown
}>()
const popover = ref()
const controlButtonIcon = useControlButtonIcon(props.widget.controlWidget())
const togglePopover = (event: Event) => {
popover.value.toggle(event)
}
</script>
<template>
<component :is="comp" v-bind="$attrs" :widget>
<Button
variant="link"
size="small"
class="h-4 w-7 self-center rounded-xl bg-blue-100/30 p-0"
@click="togglePopover"
>
<i :class="`${controlButtonIcon} text-blue-100 text-xs`" />
</Button>
</component>
<NumberControlPopover ref="popover" :control-widget="widget.controlWidget" />
</template>

View File

@@ -2,6 +2,8 @@
* Simplified widget interface for Vue-based node rendering
* Removes all DOM manipulation and positioning concerns
*/
import type { Ref } from 'vue'
import type { InputSpec as InputSpecV2 } from '@/schemas/nodeDef/nodeDefSchemaV2'
/** Valid types for widget values */
@@ -32,11 +34,6 @@ export function normalizeControlOption(val: WidgetValue): ControlOptions {
return 'randomize'
}
export type SafeControlWidget = {
value: ControlOptions
update: (value: WidgetValue) => void
}
export interface SimplifiedWidget<
T extends WidgetValue = WidgetValue,
O = Record<string, any>
@@ -70,5 +67,8 @@ export interface SimplifiedWidget<
/** Optional input specification backing this widget */
spec?: InputSpecV2
controlWidget?: SafeControlWidget
controlWidget?: () => Ref<ControlOptions>
}
export type SimplifiedControlWidget<T extends WidgetValue = WidgetValue> =
SimplifiedWidget<T> & Required<Pick<SimplifiedWidget<T>, 'controlWidget'>>