-
Notifications
You must be signed in to change notification settings - Fork 433
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 34 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
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 |
|---|---|---|
| @@ -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 | ||
| icon?: string | ||
| mode: NumberControlMode | ||
| text?: string | ||
| title: string | ||
| } | ||
AustinMroz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 | ||
| } | ||
|
|
@@ -75,25 +76,7 @@ const precision = computed(() => { | |
| }) | ||
|
|
||
| // 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.
🧹 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:
Then apply emphasis via CSS in the component rather than hardcoding capitalization.
🤖 Prompt for AI Agents