-
Notifications
You must be signed in to change notification settings - Fork 435
Support "control after generate" in vue #6985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
23fdbe4
f853cb9
57025df
cfaeed1
04bd165
6acc7b0
29af785
c18df9c
abe600b
fcf9a32
895a458
613fe12
94ade0a
0fb7466
95b95ed
69715b2
7dd2f52
307771b
bbcb3b4
ec07416
b5419f7
6d2f976
9348995
9723e2b
bb5c884
ff08660
52daf4e
ad70768
4b4b90d
a84fd7a
0b5ded8
a2391e6
5b6d835
939090f
26e6aec
1fa618c
1ddf364
cfb579b
d25cd39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1911,6 +1911,24 @@ | |
| "placeholderVideo": "Select video...", | ||
| "placeholderModel": "Select model...", | ||
| "placeholderUnknown": "Select media..." | ||
| }, | ||
| "numberControl": { | ||
| "header" : { | ||
| "prefix": "Automatically update the value", | ||
| "after": "AFTER", | ||
| "before": "BEFORE", | ||
| "postfix": "running the workflow:" | ||
| }, | ||
| "linkToGlobal": "Link to", | ||
| "linkToGlobalSeed": "Global Value", | ||
| "linkToGlobalDesc": "Unique value linked to the Global Value's control setting", | ||
| "randomize": "Randomize Value", | ||
| "randomizeDesc": "Shuffles the value randomly after each generation", | ||
| "increment": "Increment Value", | ||
| "incrementDesc": "Adds 1 to the value number", | ||
| "decrement": "Decrement Value", | ||
| "decrementDesc": "Subtracts 1 from the value number", | ||
| "editSettings": "Edit control settings" | ||
| } | ||
|
Comment on lines
+1915
to
1932
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Verify intentional ALL CAPS usage for mode labels. Lines 1917-1918 use ALL CAPS for "AFTER" and "BEFORE" while surrounding text uses mixed case. This creates visual emphasis but is inconsistent with typical UI text conventions. Please confirm this capitalization is intentional for design/UX reasons. If the goal is to emphasize the control mode, consider alternative approaches like bold styling or a separate UI treatment rather than ALL CAPS in the localization string. Example alternative structure: "header": {
"prefix": "Automatically update the value",
"afterMode": "after",
"beforeMode": "before",
"postfix": "running the workflow:"
}Then apply emphasis via CSS in the component rather than hardcoding capitalization. 🤖 Prompt for AI Agents |
||
| }, | ||
| "widgetFileUpload": { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| <script setup lang="ts"> | ||
| import Button from 'primevue/button' | ||
| import Popover from 'primevue/popover' | ||
| import ToggleSwitch from 'primevue/toggleswitch' | ||
|
Comment on lines
+2
to
+4
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider future migration away from PrimeVue components. This component uses PrimeVue's Based on coding guidelines. 🤖 Prompt for AI Agents |
||
| import { computed, ref } from 'vue' | ||
| import { useSettingStore } from '@/platform/settings/settingStore' | ||
| import { useDialogService } from '@/services/dialogService' | ||
| import { NumberControlMode } from '../composables/useStepperControl' | ||
| type ControlOption = { | ||
| description: string | ||
| mode: NumberControlMode | ||
| icon?: string | ||
| text?: string | ||
| title: string | ||
| } | ||
| const popover = ref() | ||
| const settingStore = useSettingStore() | ||
| const dialogService = useDialogService() | ||
| const toggle = (event: Event) => { | ||
| popover.value.toggle(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, | ||
| icon: 'icon-[lucide--shuffle]', | ||
| title: 'randomize', | ||
| description: 'randomizeDesc' | ||
| }, | ||
| { | ||
| mode: NumberControlMode.INCREMENT, | ||
| text: '+1', | ||
| title: 'increment', | ||
| description: 'incrementDesc' | ||
| }, | ||
| { | ||
| mode: NumberControlMode.DECREMENT, | ||
| text: '-1', | ||
| title: 'decrement', | ||
| description: 'decrementDesc' | ||
| } | ||
| ] | ||
| const widgetControlMode = computed(() => | ||
| settingStore.get('Comfy.WidgetControlMode') | ||
| ) | ||
| const props = defineProps<{ | ||
| controlMode: NumberControlMode | ||
| }>() | ||
| const emit = defineEmits<{ | ||
| 'update:controlMode': [mode: NumberControlMode] | ||
| }>() | ||
| const handleToggle = (mode: NumberControlMode) => { | ||
| if (props.controlMode === mode) return | ||
| emit('update:controlMode', mode) | ||
| } | ||
| const isActive = (mode: NumberControlMode) => { | ||
| return props.controlMode === mode | ||
| } | ||
| const handleEditSettings = () => { | ||
| popover.value.hide() | ||
| dialogService.showSettingsDialog() | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <Popover | ||
| ref="popover" | ||
| class="bg-interface-panel-surface border border-interface-stroke rounded-lg" | ||
| > | ||
| <div class="w-113 max-w-md p-4 space-y-4"> | ||
| <div class="text-sm text-muted-foreground leading-tight"> | ||
| {{ $t('widgets.numberControl.header.prefix') }} | ||
| <span class="text-base-foreground font-medium"> | ||
| {{ | ||
| widgetControlMode === 'before' | ||
| ? $t('widgets.numberControl.header.before') | ||
| : $t('widgets.numberControl.header.after') | ||
| }} | ||
| </span> | ||
| {{ $t('widgets.numberControl.header.postfix') }} | ||
| </div> | ||
|
|
||
| <div class="space-y-2"> | ||
| <div | ||
| v-for="option in controlOptions" | ||
| :key="option.mode" | ||
| class="flex items-center justify-between py-2 gap-7" | ||
| > | ||
| <div class="flex items-center gap-2 flex-1 min-w-0"> | ||
| <div | ||
| class="flex items-center justify-center w-8 h-8 rounded-lg flex-shrink-0 bg-secondary-background border border-border-subtle" | ||
| > | ||
| <i | ||
| v-if="option.icon" | ||
| :class="option.icon" | ||
| class="text-base text-base-foreground" | ||
| /> | ||
| <span | ||
| v-if="option.text" | ||
| class="text-xs font-normal text-base-foreground" | ||
| > | ||
| {{ option.text }} | ||
| </span> | ||
| </div> | ||
|
|
||
| <div class="flex flex-col gap-0.5 min-w-0 flex-1"> | ||
| <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> | ||
| </div> | ||
| <div | ||
| class="text-sm font-normal text-muted-foreground leading-tight" | ||
| > | ||
| {{ $t(`widgets.numberControl.${option.description}`) }} | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <ToggleSwitch | ||
| :model-value="isActive(option.mode)" | ||
| class="flex-shrink-0" | ||
| @update:model-value="handleToggle(option.mode)" | ||
| /> | ||
| </div> | ||
| </div> | ||
| <div class="border-t border-border-subtle"></div> | ||
| <Button | ||
| class="w-full bg-secondary-background hover:bg-secondary-background-hover border-0 rounded-lg p-2 text-sm" | ||
| @click="handleEditSettings" | ||
| > | ||
| <div class="flex items-center justify-center gap-1"> | ||
| <i class="pi pi-cog text-xs text-muted-foreground" /> | ||
| <span class="font-normal text-base-foreground">{{ | ||
| $t('widgets.numberControl.editSettings') | ||
| }}</span> | ||
| </div> | ||
| </Button> | ||
| </div> | ||
| </Popover> | ||
| </template> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,7 @@ import { | |
| filterWidgetProps | ||
| } from '@/utils/widgetPropFilter' | ||
|
|
||
| import { useNumberStepCalculation } from '../composables/useNumberStepCalculation' | ||
| import { useNumberWidgetButtonPt } from '../composables/useNumberWidgetButtonPt' | ||
| import { WidgetInputBaseClass } from './layout' | ||
| import WidgetLayoutField from './layout/WidgetLayoutField.vue' | ||
|
|
@@ -56,7 +57,7 @@ const updateLocalValue = (newValue: number[] | undefined): void => { | |
| } | ||
|
|
||
| const handleNumberInputUpdate = (newValue: number | undefined) => { | ||
| if (newValue) { | ||
| if (newValue !== undefined) { | ||
| updateLocalValue([newValue]) | ||
| return | ||
| } | ||
|
|
@@ -67,33 +68,11 @@ const filteredProps = computed(() => | |
| filterWidgetProps(widget.options, STANDARD_EXCLUDED_PROPS) | ||
| ) | ||
|
|
||
| // Get the precision value for proper number formatting | ||
| const precision = computed(() => { | ||
| const p = widget.options?.precision | ||
| // Treat negative or non-numeric precision as undefined | ||
| return typeof p === 'number' && p >= 0 ? p : undefined | ||
| }) | ||
| const p = widget.options?.precision | ||
| const precision = typeof p === 'number' && p >= 0 ? p : undefined | ||
|
|
||
| // Calculate the step value based on precision or widget options | ||
| const stepValue = computed(() => { | ||
| // Use step2 (correct input spec value) instead of step (legacy 10x value) | ||
| if (widget.options?.step2 !== undefined) { | ||
| return widget.options.step2 | ||
| } | ||
|
|
||
| // Otherwise, derive from precision | ||
| if (precision.value === undefined) { | ||
| return undefined | ||
| } | ||
|
|
||
| if (precision.value === 0) { | ||
| return 1 | ||
| } | ||
|
|
||
| // For precision > 0, step = 1 / (10^precision) | ||
| // precision 1 → 0.1, precision 2 → 0.01, etc. | ||
| return 1 / Math.pow(10, precision.value) | ||
| }) | ||
| const stepValue = useNumberStepCalculation(widget.options, precision, true) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this could be a utility function instead of a composable for now. |
||
|
|
||
| const sliderNumberPt = useNumberWidgetButtonPt({ | ||
| roundedLeft: true, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use strict equality when locating the control widget
getControlWidgetcurrently uses loose equality:Since
w.nameis a string, there’s no benefit to==over===, and sticking to strict equality avoids surprises and matches the rest of the codebase’s style.The rest of the helper (normalizing the value on read/write and exposing
controlWidgetviasafeWidgetMapper) looks good.Also applies to: 130-132
🤖 Prompt for AI Agents