Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
23fdbe4
add control_after_generate ui
christian-byrne Oct 4, 2025
f853cb9
don't use forEach
christian-byrne Oct 5, 2025
57025df
seed widget
christian-byrne Oct 8, 2025
cfaeed1
seed widget2
christian-byrne Oct 12, 2025
04bd165
handle legacy step value
christian-byrne Oct 21, 2025
6acc7b0
feat: update NumberControlPopover with semantic design tokens
christian-byrne Nov 13, 2025
29af785
fix test
christian-byrne Nov 14, 2025
c18df9c
Fix display of controled widget values
AustinMroz Nov 25, 2025
abe600b
Revert useGraphNodeManager changes
AustinMroz Nov 25, 2025
fcf9a32
Merge origin/main
AustinMroz Nov 25, 2025
895a458
Swap NumberInputs to rekka ui to embed control
AustinMroz Nov 27, 2025
613fe12
Persist control widget value
AustinMroz Nov 27, 2025
94ade0a
[automated] Update test expectations
invalid-email-address Nov 27, 2025
0fb7466
Test fixes and nits
AustinMroz Nov 27, 2025
95b95ed
Merge branch 'main' into austin/vue-control-after-generate
christian-byrne Nov 30, 2025
69715b2
[automated] Update test expectations
invalid-email-address Nov 30, 2025
7dd2f52
Add max and mins from litegraph implementation
AustinMroz Dec 1, 2025
307771b
Merge 8e006bb8a306^
AustinMroz Dec 3, 2025
bbcb3b4
Merge main
AustinMroz Dec 3, 2025
ec07416
[automated] Update test expectations
invalid-email-address Dec 3, 2025
b5419f7
Empty commit to force tests to rerun
AustinMroz Dec 3, 2025
6d2f976
Fix number precision
AustinMroz Dec 3, 2025
9348995
[automated] Update test expectations
invalid-email-address Dec 3, 2025
9723e2b
Merge main
AustinMroz Dec 3, 2025
bb5c884
[automated] Update test expectations
invalid-email-address Dec 3, 2025
ff08660
Merge main
AustinMroz Dec 3, 2025
52daf4e
Revert to primevue, fix test
AustinMroz Dec 3, 2025
ad70768
Revert vitest changes, clear browser snapshots
AustinMroz Dec 3, 2025
4b4b90d
[automated] Update test expectations
invalid-email-address Dec 3, 2025
a84fd7a
Empty commit to trigger tests
AustinMroz Dec 4, 2025
0b5ded8
Merge main
AustinMroz Dec 5, 2025
a2391e6
[automated] Update test expectations
invalid-email-address Dec 5, 2025
5b6d835
Empty commit to force tests
AustinMroz Dec 5, 2025
939090f
nits
AustinMroz Dec 5, 2025
26e6aec
Further nits
AustinMroz Dec 6, 2025
1fa618c
Merge main
AustinMroz Dec 6, 2025
1ddf364
[automated] Update test expectations
invalid-email-address Dec 6, 2025
cfb579b
Remove global seed tests
AustinMroz Dec 6, 2025
d25cd39
Merge remote-tracking branch 'origin/main' into austin/vue-control-af…
AustinMroz Dec 9, 2025
679a1da
[automated] Update test expectations
invalid-email-address Dec 10, 2025
70f6d5b
merge: main into austin/vue-control-after-generate
christian-byrne Dec 11, 2025
ed25f72
Merge branch 'main' into austin/vue-control-after-generate
AustinMroz Dec 13, 2025
488922a
fix: restore executeNumberControls calls removed in merge
christian-byrne Dec 13, 2025
9553233
fix: remove space-y-1 causing extra margin on number inputs
christian-byrne Dec 13, 2025
437880c
[automated] Update test expectations
invalid-email-address Dec 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Persist control widget value
  • Loading branch information
AustinMroz committed Nov 27, 2025
commit 613fe124d223af235cffa6b09ea98ef8495c7b71
33 changes: 31 additions & 2 deletions src/composables/graph/useGraphNodeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ 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 } from '@/types/simplifiedWidget'
import type {
WidgetValue,
SafeControlWidget,
ControlWidgetOptions
} from '@/types/simplifiedWidget'

import type {
LGraph,
Expand All @@ -44,6 +48,7 @@ export interface SafeWidgetData {
spec?: InputSpec
slotMetadata?: WidgetSlotMetadata
isDOMWidget?: boolean
controlWidget?: SafeControlWidget
}

export interface VueNodeData {
Expand Down Expand Up @@ -79,6 +84,29 @@ export interface GraphNodeManager {
cleanup(): void
}

function validateControlWidgetValue(val: unknown): ControlWidgetOptions {
//TODO: Is there a way to do this without repeating?
switch (val) {
case 'fixed':
return 'fixed'
case 'increment':
return 'increment'
case 'decrement':
return 'decrement'
}
return 'randomize'
}
function getControlWidget(widget: IBaseWidget): SafeControlWidget | undefined {
const cagWidget = widget.linkedWidgets?.find(
(w) => (w.name = 'control_after_generate')
)
if (!cagWidget) return
return {
value: validateControlWidgetValue(cagWidget.value),
update: (value) => (cagWidget.value = validateControlWidgetValue(value))
}
}

export function safeWidgetMapper(
node: LGraphNode,
slotMetadata: Map<string, WidgetSlotMetadata>
Expand Down Expand Up @@ -111,7 +139,8 @@ export function safeWidgetMapper(
callback: widget.callback,
spec,
slotMetadata: slotInfo,
isDOMWidget: isDOMWidget(widget)
isDOMWidget: isDOMWidget(widget),
controlWidget: getControlWidget(widget)
}
} catch (error) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ const processedWidgets = computed((): ProcessedWidget[] => {
label: widget.label,
options: widgetOptions,
callback: widget.callback,
spec: widget.spec
spec: widget.spec,
controlWidget: widget.controlWidget
}

const updateHandler = (value: WidgetValue) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const props = defineProps<{
const modelValue = defineModel<number>({ default: 0 })

const hasControlAfterGenerate = computed(() => {
return props.widget.spec?.control_after_generate === true
return !!props.widget.controlWidget
})
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import { defineAsyncComponent, ref } from 'vue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'

import { useControlButtonIcon } from '../composables/useControlButtonIcon'
import {
NumberControlMode,
useStepperControl
} from '../composables/useStepperControl'
import type { NumberControlMode } from '../composables/useStepperControl'
import { useStepperControl } from '../composables/useStepperControl'
import WidgetInputNumberInput from './WidgetInputNumberInput.vue'

const NumberControlPopover = defineAsyncComponent(
Expand All @@ -20,31 +18,27 @@ const props = defineProps<{
readonly?: boolean
}>()

const emit = defineEmits<{
'update:modelValue': [value: number]
}>()

const modelValue = defineModel<number>({ default: 0 })
const popover = ref()

const handleControlChange = (newValue: number) => {
modelValue.value = newValue
emit('update:modelValue', newValue)
}

const { controlMode } = useStepperControl(modelValue, {
...props.widget.options,
onChange: handleControlChange
})

if (controlMode.value === NumberControlMode.FIXED) {
controlMode.value = NumberControlMode.RANDOMIZE
}
const { controlMode } = useStepperControl(
modelValue,
{
...props.widget.options,
onChange: handleControlChange
},
props.widget.controlWidget!.value
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unsafe non-null assertions on controlWidget.

Lines 34 and 41 use non-null assertions (widget.controlWidget!) without defensive checks. If controlWidget is undefined, this will cause a runtime error. While the parent component may conditionally render this component only when controlWidget exists, TypeScript cannot verify this.

Consider adding a runtime guard or updating the prop type to guarantee controlWidget is defined:

 const props = defineProps<{
-  widget: SimplifiedWidget<number>
+  widget: SimplifiedWidget<number> & { controlWidget: SafeControlWidget }
   readonly?: boolean
 }>()

Or add a defensive check:

if (!props.widget.controlWidget) {
  throw new Error('WidgetInputNumberWithControl requires a widget with controlWidget')
}
🤖 Prompt for AI Agents
In
src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue
around lines 28 to 35 (and usages at line 41), non-null assertions are used on
props.widget.controlWidget which can throw at runtime if undefined; add a
runtime guard at the top of the setup (or component entry) that checks for
props.widget.controlWidget and throws a clear error (or return early) if absent,
or else tighten the prop type to require controlWidget so TypeScript guarantees
presence; ensure all subsequent uses remove the `!` after you’ve validated or
updated the prop type.


const controlButtonIcon = useControlButtonIcon(controlMode)

const setControlMode = (mode: NumberControlMode) => {
controlMode.value = mode
props.widget.controlWidget!.update(mode)
}

const togglePopover = (event: Event) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export function useControlButtonIcon(controlMode: Ref<NumberControlMode>) {
return 'pi pi-plus'
case NumberControlMode.DECREMENT:
return 'pi pi-minus'
case NumberControlMode.RANDOMIZE:
return 'icon-[lucide--shuffle]'
case NumberControlMode.FIXED:
return 'icon-[lucide--pencil-off]'
case NumberControlMode.LINK_TO_GLOBAL:
return 'pi pi-link'
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { onMounted, onUnmounted, ref } from 'vue'
import type { Ref } from 'vue'

import { useGlobalSeedStore } from '@/stores/globalSeedStore'
import type { ControlWidgetOptions } from '@/types/simplifiedWidget'

import { numberControlRegistry } from '../services/NumberControlRegistry'

Expand All @@ -21,11 +22,26 @@ interface StepperControlOptions {
onChange?: (value: number) => void
}

function convertToEnum(str?: ControlWidgetOptions): NumberControlMode {
switch (str) {
case 'fixed':
return NumberControlMode.FIXED
case 'increment':
return NumberControlMode.INCREMENT
case 'decrement':
return NumberControlMode.DECREMENT
case 'randomize':
return NumberControlMode.RANDOMIZE
}
return NumberControlMode.RANDOMIZE
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing case for 'linkToGlobal' in convertToEnum.

The function handles 'fixed', 'increment', 'decrement', and 'randomize', but does not handle 'linkToGlobal'. If a user passes 'linkToGlobal' as the defaultValue, it will incorrectly fall through to the default case and return RANDOMIZE instead of LINK_TO_GLOBAL.

Apply this diff to add the missing case:

 function convertToEnum(str?: ControlWidgetOptions): NumberControlMode {
   switch (str) {
     case 'fixed':
       return NumberControlMode.FIXED
     case 'increment':
       return NumberControlMode.INCREMENT
     case 'decrement':
       return NumberControlMode.DECREMENT
     case 'randomize':
       return NumberControlMode.RANDOMIZE
+    case 'linkToGlobal':
+      return NumberControlMode.LINK_TO_GLOBAL
   }
   return NumberControlMode.RANDOMIZE
 }
🤖 Prompt for AI Agents
In src/renderer/extensions/vueNodes/widgets/composables/useStepperControl.ts
around lines 25 to 37, the convertToEnum function is missing a case for
'linkToGlobal' so that value falls through to RANDOMIZE; add a case clause for
'linkToGlobal' that returns NumberControlMode.LINK_TO_GLOBAL (keeping the
existing default return as RANDOMIZE) so the function maps that string to the
correct enum.


export function useStepperControl(
modelValue: Ref<number>,
options: StepperControlOptions
options: StepperControlOptions,
defaultValue?: ControlWidgetOptions
) {
const controlMode = ref<NumberControlMode>(NumberControlMode.FIXED)
const controlMode = ref<NumberControlMode>(convertToEnum(defaultValue))
const controlId = Symbol('numberControl')
const globalSeedStore = useGlobalSeedStore()

Expand Down
12 changes: 12 additions & 0 deletions src/types/simplifiedWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ export type WidgetValue =
| void
| File[]

export type ControlWidgetOptions =
| 'fixed'
| 'increment'
| 'decrement'
| 'randomize'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing 'linkToGlobal' option in ControlWidgetOptions type.

The ControlWidgetOptions union type does not include 'linkToGlobal', but NumberControlMode.LINK_TO_GLOBAL exists in useStepperControl.ts (line 14). While ENABLE_LINK_TO_GLOBAL is currently false in NumberControlPopover.vue (line 29), the type should be complete to support the full enum when the feature is enabled.

Apply this diff to add the missing option:

 export type ControlWidgetOptions =
   | 'fixed'
   | 'increment'
   | 'decrement'
   | 'randomize'
+  | 'linkToGlobal'
🤖 Prompt for AI Agents
In src/types/simplifiedWidget.ts around lines 18 to 22, the ControlWidgetOptions
union type is missing the 'linkToGlobal' member referenced by
NumberControlMode.LINK_TO_GLOBAL; add 'linkToGlobal' to the union so the type
covers that option (i.e., extend the existing union to include 'linkToGlobal'),
keeping formatting consistent with the other string literals.

export type SafeControlWidget = {
value: ControlWidgetOptions
update: (value: unknown) => void
}

export interface SimplifiedWidget<
T extends WidgetValue = WidgetValue,
O = Record<string, any>
Expand Down Expand Up @@ -45,4 +55,6 @@ export interface SimplifiedWidget<

/** Optional method to compute widget size requirements */
computeSize?: () => { minHeight: number; maxHeight?: number }

controlWidget?: SafeControlWidget
}
Loading