fix: enable enforce-consistent-class-order tailwind lint rule (#9428)

## Summary

Enable `better-tailwindcss/enforce-consistent-class-order` lint rule and
auto-fix all 1027 violations across 263 files. Stacked on #9427.

## Changes

- **What**: Sort Tailwind classes into consistent order via `eslint
--fix`
- Enable `enforce-consistent-class-order` as `'error'` in eslint config
- Purely cosmetic reordering — no behavioral or visual changes

## Review Focus

Mechanical auto-fix PR — all changes are class reordering only. This is
the largest diff but lowest risk since it changes no class names, only
their order.

**Stack:** #9417#9427 → **this PR**

Fixes #9300 (partial — 3 of 3 rules)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9428-fix-enable-enforce-consistent-class-order-tailwind-lint-rule-31a6d73d3650811c9065f5178ba3e724)
by [Unito](https://www.unito.io)
This commit is contained in:
Christian Byrne
2026-03-05 17:24:34 -08:00
committed by GitHub
parent 60267fc64c
commit ef4e4a69d5
278 changed files with 1027 additions and 1027 deletions

View File

@@ -26,7 +26,7 @@ function togglePromotion() {
<template>
<div
v-if="isSelectInputsMode"
class="col-span-2 flex flex-row pointer-events-auto cursor-pointer gap-1 relative"
class="pointer-events-auto relative col-span-2 flex cursor-pointer flex-row gap-1"
@pointerdown.capture.stop.prevent="togglePromotion"
@click.capture.stop.prevent
@pointerup.capture.stop.prevent
@@ -36,27 +36,27 @@ function togglePromotion() {
<div
:class="
cn(
'border-primary-background border rounded-sm size-4 self-center m-1',
isPromoted && 'bg-primary-background flex items-center'
'm-1 size-4 self-center rounded-sm border border-primary-background',
isPromoted && 'flex items-center bg-primary-background'
)
"
>
<i
v-if="isPromoted"
class="icon-[lucide--check] bg-primary-foreground place-center"
class="bg-primary-foreground place-center icon-[lucide--check]"
/>
</div>
<div
:class="
cn(
'grid grid-cols-2 items-stretch ring-primary-background rounded-lg pointer-events-none flex-1',
'pointer-events-none grid flex-1 grid-cols-2 items-stretch rounded-lg ring-primary-background',
isPromoted && 'ring-2'
)
"
>
<slot />
</div>
<div class="absolute size-full hover:bg-primary-background/10 rounded-lg" />
<div class="absolute size-full rounded-lg hover:bg-primary-background/10" />
</div>
<slot v-else />
</template>

View File

@@ -25,7 +25,7 @@ function togglePromotion() {
<div
:class="
cn(
'absolute size-full pointer-events-auto ring-warning-background/50 ring-5 rounded-2xl',
'pointer-events-auto absolute size-full rounded-2xl ring-5 ring-warning-background/50',
isPromoted && 'ring-warning-background'
)
"
@@ -38,13 +38,13 @@ function togglePromotion() {
<div class="absolute top-0 right-0 size-8">
<div
v-if="isPromoted"
class="absolute -top-1/2 -right-1/2 size-full p-2 bg-warning-background rounded-lg"
class="absolute -top-1/2 -right-1/2 size-full rounded-lg bg-warning-background p-2"
>
<i class="icon-[lucide--check] bg-text-foreground size-full" />
<i class="bg-text-foreground icon-[lucide--check] size-full" />
</div>
<div
v-else
class="absolute -top-1/2 -right-1/2 size-full ring-warning-background/50 ring-4 ring-inset bg-component-node-background rounded-lg"
class="absolute -top-1/2 -right-1/2 size-full rounded-lg bg-component-node-background ring-4 ring-warning-background/50 ring-inset"
/>
</div>
</div>

View File

@@ -44,8 +44,8 @@ const { isOverDropZone } = useDropZone(dropZoneRef, {
ref="dropZoneRef"
:class="
cn(
'rounded-lg ring-inset ring-primary-500',
canAcceptDrop && isOverDropZone && 'ring-4 bg-primary-500/10'
'rounded-lg ring-primary-500 ring-inset',
canAcceptDrop && isOverDropZone && 'bg-primary-500/10 ring-4'
)
"
>
@@ -54,7 +54,7 @@ const { isOverDropZone } = useDropZone(dropZoneRef, {
v-if="dropIndicator"
:class="
cn(
'flex flex-col items-center justify-center gap-2 border-dashed rounded-lg border h-25 border-border-subtle m-3 py-2',
'm-3 flex h-25 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border-subtle py-2',
dropIndicator?.onClick && 'cursor-pointer'
)
"

View File

@@ -13,7 +13,7 @@ const width = ref('')
const height = ref('')
</script>
<template>
<ZoomPane v-if="!mobile" v-slot="slotProps" class="flex-1 w-full">
<ZoomPane v-if="!mobile" v-slot="slotProps" class="w-full flex-1">
<img
ref="imageRef"
:src
@@ -31,7 +31,7 @@ const height = ref('')
<img
v-else
ref="imageRef"
class="contain-size grow object-contain"
class="grow object-contain contain-size"
:src
@load="
() => {

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex-1 min-h-0 w-full flex items-center justify-center">
<div class="flex min-h-0 w-full flex-1 items-center justify-center">
<div
class="aspect-square size-[min(50vw,50vh)] rounded-lg skeleton-shimmer"
class="skeleton-shimmer aspect-square size-[min(50vw,50vh)] rounded-lg"
/>
</div>
</template>

View File

@@ -15,12 +15,12 @@ const { hasOutputs } = storeToRefs(useAppModeStore())
v-if="hasOutputs"
role="article"
data-testid="arrange-preview"
class="flex flex-col items-center justify-center h-full w-3/4 gap-6 p-8 mx-auto"
class="mx-auto flex h-full w-3/4 flex-col items-center justify-center gap-6 p-8"
>
<div
class="border-warning-background border-2 border-dashed rounded-2xl w-full h-4/5 flex items-center justify-center flex-col p-12"
class="flex h-4/5 w-full flex-col items-center justify-center rounded-2xl border-2 border-dashed border-warning-background p-12"
>
<p class="text-base-foreground font-bold mb-0">
<p class="mb-0 font-bold text-base-foreground">
{{ t('linearMode.arrange.outputs') }}
</p>
<p>{{ t('linearMode.arrange.resultsLabel') }}</p>
@@ -30,18 +30,18 @@ const { hasOutputs } = storeToRefs(useAppModeStore())
v-else
role="article"
data-testid="arrange-no-outputs"
class="flex flex-col items-center justify-center h-full gap-6 p-8 w-lg mx-auto text-center"
class="mx-auto flex h-full w-lg flex-col items-center justify-center gap-6 p-8 text-center"
>
<p class="m-0 text-base-foreground">
{{ t('linearMode.arrange.noOutputs') }}
</p>
<div class="flex flex-col gap-1 text-muted-foreground w-lg text-[14px]">
<div class="flex w-lg flex-col gap-1 text-[14px] text-muted-foreground">
<p class="mt-0 p-0">{{ t('linearMode.arrange.switchToOutputs') }}</p>
<i18n-t keypath="linearMode.arrange.connectAtLeastOne" tag="div">
<template #atLeastOne>
<span class="italic font-bold">
<span class="font-bold italic">
{{ t('linearMode.arrange.atLeastOne') }}
</span>
</template>

View File

@@ -165,23 +165,23 @@ defineExpose({ runButtonClick })
<template>
<div
v-if="!isBuilderMode && hasOutputs"
class="flex flex-col min-w-80 h-full"
class="flex h-full min-w-80 flex-col"
v-bind="$attrs"
>
<section
v-if="!mobile"
data-testid="linear-workflow-info"
class="h-12 border-x border-border-subtle py-2 px-4 gap-2 bg-comfy-menu-bg flex items-center contain-size"
class="flex h-12 items-center gap-2 border-x border-border-subtle bg-comfy-menu-bg px-4 py-2 contain-size"
>
<span
class="font-bold truncate"
class="truncate font-bold"
v-text="workflowStore.activeWorkflow?.filename"
/>
<div class="flex-1" />
<Popover
v-if="partitionedNodes[0].length"
align="end"
class="overflow-y-auto overflow-x-clip max-h-(--reka-popover-content-available-height) z-100"
class="z-100 max-h-(--reka-popover-content-available-height) overflow-x-clip overflow-y-auto"
side="bottom"
:side-offset="-8"
>
@@ -201,7 +201,7 @@ defineExpose({ runButtonClick })
/>
<NodeWidgets
:node-data
class="py-3 gap-y-3 **:[.col-span-2]:grid-cols-1 *:has-[textarea]:h-50 rounded-lg max-w-100"
class="max-w-100 gap-y-3 rounded-lg py-3 *:has-[textarea]:h-50 **:[.col-span-2]:grid-cols-1"
/>
</template>
</div>
@@ -209,7 +209,7 @@ defineExpose({ runButtonClick })
<Button v-if="false"> {{ t('menuLabels.publish') }} </Button>
</section>
<div
class="border-x md:border-y gap-2 h-full border-(--interface-stroke) bg-comfy-menu-bg flex flex-col px-2"
class="flex h-full flex-col gap-2 border-x border-(--interface-stroke) bg-comfy-menu-bg px-2 md:border-y"
>
<section
data-testid="linear-widgets"
@@ -235,9 +235,9 @@ defineExpose({ runButtonClick })
:node-data
:class="
cn(
'py-3 gap-y-3 **:[.col-span-2]:grid-cols-1 *:has-[textarea]:h-50 rounded-lg **:[.h-7]:h-10',
'gap-y-3 rounded-lg py-3 *:has-[textarea]:h-50 **:[.col-span-2]:grid-cols-1 **:[.h-7]:h-10',
nodeData.hasErrors &&
'ring-2 ring-inset ring-node-stroke-error'
'ring-2 ring-node-stroke-error ring-inset'
)
"
/>
@@ -251,7 +251,7 @@ defineExpose({ runButtonClick })
:to="toastTo"
>
<div
class="bg-base-foreground md:bg-secondary-background text-base-background md:text-base-foreground rounded-lg flex h-10 md:h-8 p-1 pr-2 gap-2 items-center"
class="flex h-10 items-center gap-2 rounded-lg bg-base-foreground p-1 pr-2 text-base-background md:h-8 md:bg-secondary-background md:text-base-foreground"
>
<template v-if="pendingJobQueues === 0">
<i
@@ -275,13 +275,13 @@ defineExpose({ runButtonClick })
<section
v-if="mobile"
data-testid="linear-run-button"
class="p-4 pb-6 border-t border-node-component-border"
class="border-t border-node-component-border p-4 pb-6"
>
<SubscribeToRunButton
v-if="!isActiveSubscription"
class="w-full mt-4"
class="mt-4 w-full"
/>
<div v-else class="flex mt-4">
<div v-else class="mt-4 flex">
<Popover side="top" @open-auto-focus.prevent>
<template #button>
<Button size="lg" class="-mr-3 pr-7">
@@ -290,7 +290,7 @@ defineExpose({ runButtonClick })
</Button>
</template>
<div
class="mb-2 m-1 text-node-component-slot-text"
class="m-1 mb-2 text-node-component-slot-text"
v-text="t('linearMode.runCount')"
/>
<ScrubableNumberInput
@@ -315,10 +315,10 @@ defineExpose({ runButtonClick })
<section
v-else
data-testid="linear-run-button"
class="p-4 pb-6 border-t border-node-component-border"
class="border-t border-node-component-border p-4 pb-6"
>
<div
class="mb-2 m-1 text-node-component-slot-text"
class="m-1 mb-2 text-node-component-slot-text"
v-text="t('linearMode.runCount')"
/>
<ScrubableNumberInput
@@ -330,12 +330,12 @@ defineExpose({ runButtonClick })
/>
<SubscribeToRunButton
v-if="!isActiveSubscription"
class="w-full mt-4"
class="mt-4 w-full"
/>
<Button
v-else
variant="primary"
class="w-full mt-4 text-sm"
class="mt-4 w-full text-sm"
size="lg"
@click="runButtonClick"
>

View File

@@ -22,7 +22,7 @@ const visible = computed(() => sidebarOnLeft.value === (side === 'left'))
<div
:class="
cn(
'flex items-center gap-2 px-4 text-nowrap text-base-foreground self-end pb-4',
'flex items-center gap-2 self-end px-4 pb-4 text-nowrap text-base-foreground',
side === 'right' && 'flex-row-reverse',
!visible && 'invisible'
)

View File

@@ -91,7 +91,7 @@ async function rerun(e: Event) {
<section
v-if="selectedItem || selectedOutput || !executionStore.isIdle"
data-testid="linear-output-info"
class="flex flex-wrap gap-2 p-4 w-full md:z-10 tabular-nums justify-center text-sm"
class="flex w-full flex-wrap justify-center gap-2 p-4 text-sm tabular-nums md:z-10"
>
<template v-if="selectedItem">
<Button size="md" @click="rerun">
@@ -102,7 +102,7 @@ async function rerun(e: Event) {
{{ t('linearMode.reuseParameters') }}
<i class="icon-[lucide--list-restart]" />
</Button>
<div class="border-r border-border-subtle mx-1" />
<div class="mx-1 border-r border-border-subtle" />
</template>
<Button
v-if="selectedOutput"
@@ -153,17 +153,17 @@ async function rerun(e: Event) {
<VideoPreview
v-else-if="getMediaType(selectedOutput) === 'video'"
:src="selectedOutput!.url"
class="object-contain flex-1 md:contain-size md:p-3"
class="flex-1 object-contain md:p-3 md:contain-size"
/>
<audio
v-else-if="getMediaType(selectedOutput) === 'audio'"
class="w-full m-auto"
class="m-auto w-full"
controls
:src="selectedOutput!.url"
/>
<article
v-else-if="getMediaType(selectedOutput) === 'text'"
class="w-full max-w-lg m-auto my-12 overflow-y-auto"
class="m-auto my-12 w-full max-w-lg overflow-y-auto"
v-text="selectedOutput!.url"
/>
<Preview3d

View File

@@ -17,7 +17,7 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
<div
role="article"
data-testid="linear-welcome"
class="flex flex-col items-center justify-center h-full gap-6 p-8 max-w-lg mx-auto text-center"
class="mx-auto flex h-full max-w-lg flex-col items-center justify-center gap-6 p-8 text-center"
>
<div class="flex flex-col gap-2">
<h2 class="text-3xl font-semibold text-muted-foreground">
@@ -25,7 +25,7 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
</h2>
</div>
<div class="flex flex-col gap-3 text-muted-foreground max-w-md text-[14px]">
<div class="flex max-w-md flex-col gap-3 text-[14px] text-muted-foreground">
<p class="mt-0">{{ t('linearMode.welcome.message') }}</p>
<p class="mt-0">{{ t('linearMode.welcome.controls') }}</p>
<p class="mt-0">{{ t('linearMode.welcome.sharing') }}</p>
@@ -35,7 +35,7 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
<i18n-t keypath="linearMode.welcome.getStarted" tag="span">
<template #runButton>
<span
class="inline-flex items-center px-3.5 py-0.5 mx-0.5 transform -translate-y-0.5 rounded-sm bg-primary-background text-base-foreground text-xxs font-medium cursor-default"
class="mx-0.5 inline-flex -translate-y-0.5 transform cursor-default items-center rounded-sm bg-primary-background px-3.5 py-0.5 text-xxs font-medium text-base-foreground"
>
{{ t('menu.run') }}
</span>
@@ -44,7 +44,7 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
</p>
</div>
<template v-else>
<p v-if="!hasNodes" class="mt-0 text-base-foreground text-sm max-w-md">
<p v-if="!hasNodes" class="mt-0 max-w-md text-sm text-base-foreground">
{{ t('linearMode.emptyWorkflowExplanation') }}
</p>
<div class="flex flex-row gap-2">
@@ -68,7 +68,7 @@ const templateSelectorDialog = useWorkflowTemplateSelectorDialog()
<i class="icon-[lucide--hammer]" />
{{ t('linearMode.welcome.buildApp') }}
<div
class="bg-base-foreground text-base-background text-xxs rounded-full absolute -top-2 -right-2 px-1"
class="absolute -top-2 -right-2 rounded-full bg-base-foreground px-1 text-xxs text-base-background"
>
{{ t('g.experimental') }}
</div>

View File

@@ -149,9 +149,9 @@ const menuEntries = computed<MenuItem[]>(() => [
])
</script>
<template>
<section class="absolute size-full flex flex-col bg-secondary-background">
<section class="absolute flex size-full flex-col bg-secondary-background">
<header
class="w-full h-16 px-4 py-3 flex border-border-subtle border-b items-center gap-3 bg-base-background"
class="flex h-16 w-full items-center gap-3 border-b border-border-subtle bg-base-background px-4 py-3"
>
<DropdownMenu :entries="menuEntries" />
<Popover
@@ -162,52 +162,52 @@ const menuEntries = computed<MenuItem[]>(() => [
<template #button>
<!--TODO: Use button here? Probably too much work to destyle-->
<div
class="bg-secondary-background h-10 rounded-sm grow flex items-center p-2 gap-2"
class="flex h-10 grow items-center gap-2 rounded-sm bg-secondary-background p-2"
>
<i
class="shrink-0 icon-[lucide--panels-top-left] bg-primary-background"
class="icon-[lucide--panels-top-left] shrink-0 bg-primary-background"
/>
<span
class="truncate contain-size size-full"
class="size-full truncate contain-size"
v-text="workflowStore.activeWorkflow?.filename"
/>
<i
class="shrink-0 icon-[lucide--chevron-down] bg-muted-foreground"
class="icon-[lucide--chevron-down] shrink-0 bg-muted-foreground"
/>
</div>
</template>
</Popover>
<CurrentUserButton v-if="isLoggedIn" :show-arrow="false" />
</header>
<div class="size-full contain-content rounded-b-4xl">
<div class="size-full rounded-b-4xl contain-content">
<div
:class="
cn('size-full relative', !isSwiping && 'transition-[translate]')
cn('relative size-full', !isSwiping && 'transition-[translate]')
"
:style="{ translate }"
>
<div class="overflow-y-auto contain-size h-full w-screen absolute">
<div class="absolute h-full w-screen overflow-y-auto contain-size">
<LinearControls mobile @navigate-assets="activeIndex = 2" />
</div>
<div
class="w-screen absolute h-full bg-base-background left-[100vw] top-0 flex flex-col"
class="absolute top-0 left-[100vw] flex h-full w-screen flex-col bg-base-background"
>
<LinearPreview mobile />
</div>
<AssetsSidebarTab
class="h-full w-screen absolute bg-base-background left-[200vw] top-0"
class="absolute top-0 left-[200vw] h-full w-screen bg-base-background"
/>
</div>
</div>
<div
ref="sliderPaneRef"
class="h-22 p-4 w-full flex gap-4 items-center justify-around bg-secondary-background"
class="flex h-22 w-full items-center justify-around gap-4 bg-secondary-background p-4"
>
<Button
v-for="([label, icon], index) in tabs"
:key="label"
:variant="index === activeIndex ? 'secondary' : 'muted-textonly'"
class="flex-col h-14 grow"
class="h-14 grow flex-col"
@click="onClick(index)"
>
<div class="relative size-4">
@@ -218,7 +218,7 @@ const menuEntries = computed<MenuItem[]>(() => [
(queueStore.runningTasks.length > 0 ||
queueStore.pendingTasks.length > 0)
"
class="absolute bg-primary-background size-2 -top-1 -right-1 rounded-full animate-pulse"
class="absolute -top-1 -right-1 size-2 animate-pulse rounded-full bg-primary-background"
/>
</div>
{{ t(label) }}

View File

@@ -41,7 +41,7 @@ const queueCount = computed(
)
const itemClass = cn(
'shrink-0 cursor-pointer p-1 rounded-lg border-2 border-transparent outline-none',
'shrink-0 cursor-pointer rounded-lg border-2 border-transparent p-1 outline-none',
'data-[state=checked]:border-interface-panel-job-progress-border'
)
@@ -284,15 +284,15 @@ useEventListener(document.body, 'keydown', (e: KeyboardEvent) => {
<article
ref="outputsRef"
data-testid="linear-outputs"
class="py-3 overflow-y-clip overflow-x-auto min-w-0"
class="min-w-0 overflow-x-auto overflow-y-clip py-3"
>
<div class="flex items-start gap-0.5 mx-auto w-fit h-21">
<div class="mx-auto flex h-21 w-fit items-start gap-0.5">
<div
v-if="queueCount > 0 || hasActiveContent"
:class="
cn(
'sticky left-0 z-10 shrink-0 flex items-start gap-0.5',
'bg-comfy-menu-bg md:bg-comfy-menu-secondary-bg'
'sticky left-0 z-10 flex shrink-0 items-start gap-0.5',
'md:bg-comfy-menu-secondary-bg bg-comfy-menu-bg'
)
"
>
@@ -331,14 +331,14 @@ useEventListener(document.body, 'keydown', (e: KeyboardEvent) => {
<div
v-if="hasActiveContent && visibleHistory.length > 0"
class="border-l border-border-default h-12 shrink-0 mx-4"
class="mx-4 h-12 shrink-0 border-l border-border-default"
/>
</div>
<template v-for="(asset, aIdx) in visibleHistory" :key="asset.id">
<div
v-if="aIdx > 0"
class="border-l border-border-default h-12 shrink-0 mx-4"
class="mx-4 h-12 shrink-0 border-l border-border-default"
/>
<div
v-for="(output, key) in toValue(allOutputs(asset))"

View File

@@ -20,7 +20,7 @@ function clearQueue(close: () => void) {
</script>
<template>
<div
class="shrink-0 p-1 border-2 border-transparent relative"
class="relative shrink-0 border-2 border-transparent p-1"
data-testid="linear-job"
>
<Popover side="top" :show-arrow="false" @focus-outside.prevent>
@@ -30,7 +30,7 @@ function clearQueue(close: () => void) {
:aria-label="t('linearMode.queue.clickToClear')"
variant="textonly"
size="unset"
class="size-10 rounded-sm bg-secondary-background flex items-center justify-center"
class="flex size-10 items-center justify-center rounded-sm bg-secondary-background"
>
<Loader size="sm" class="text-muted-foreground" />
</Button>
@@ -39,7 +39,7 @@ function clearQueue(close: () => void) {
<Button
:disabled="queueCount === 0"
variant="textonly"
class="text-destructive-background px-4 text-sm"
class="px-4 text-sm text-destructive-background"
@click="clearQueue(close)"
>
<i class="icon-[lucide--list-x]" />
@@ -50,7 +50,7 @@ function clearQueue(close: () => void) {
<div
v-if="queueCount > 0"
aria-hidden="true"
class="absolute top-0 right-0 min-w-4 h-4 flex justify-center items-center rounded-full bg-primary-background text-text-primary text-xs"
class="absolute top-0 right-0 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary-background text-xs text-text-primary"
v-text="queueCount"
/>
</div>

View File

@@ -13,7 +13,7 @@ const { output } = defineProps<{
<template>
<img
v-if="getMediaType(output) === 'images'"
class="block size-10 rounded-sm object-cover bg-secondary-background"
class="block size-10 rounded-sm bg-secondary-background object-cover"
loading="lazy"
width="40"
height="40"

View File

@@ -12,7 +12,7 @@ const { latentPreview } = defineProps<{
class="block size-10 rounded-sm object-cover"
:src="latentPreview"
/>
<div v-else class="size-10 rounded-sm skeleton-shimmer" />
<LinearProgressBar class="w-10 h-1 mt-1" rounded />
<div v-else class="skeleton-shimmer size-10 rounded-sm" />
<LinearProgressBar class="mt-1 h-1 w-10" rounded />
</div>
</template>

View File

@@ -24,7 +24,7 @@ watch([containerRef, () => modelUrl], async () => {
<template>
<div
ref="containerRef"
class="relative size-full md:w-[calc(100%-150px)] self-center"
class="relative size-full self-center md:w-[calc(100%-150px)]"
@mouseenter="viewer.handleMouseEnter"
@mouseleave="viewer.handleMouseLeave"
@resize="viewer.handleResize"

View File

@@ -23,5 +23,5 @@ const height = ref('')
}
"
/>
<span class="self-center z-10" v-text="`${width} x ${height}`" />
<span class="z-10 self-center" v-text="`${width} x ${height}`" />
</template>

View File

@@ -21,7 +21,7 @@
<div
v-if="videoError"
role="alert"
class="flex flex-auto flex-col items-center justify-center bg-muted-background text-center text-base-foreground py-8"
class="flex flex-auto flex-col items-center justify-center bg-muted-background py-8 text-center text-base-foreground"
>
<i class="mb-2 icon-[lucide--video-off] size-12 text-base-foreground" />
<p class="text-sm text-base-foreground">

View File

@@ -1,13 +1,13 @@
<template>
<div
v-if="imageUrls.length > 0"
class="image-preview group relative flex size-full min-h-55 min-w-16 flex-col px-2 justify-center"
class="image-preview group relative flex size-full min-h-55 min-w-16 flex-col justify-center px-2"
@keydown="handleKeyDown"
>
<!-- Image Wrapper -->
<div
ref="imageWrapperEl"
class="min-h-0 flex-1 flex w-full overflow-hidden rounded-[5px] bg-muted-background relative"
class="relative flex min-h-0 w-full flex-1 overflow-hidden rounded-[5px] bg-muted-background"
tabindex="0"
role="img"
:aria-label="$t('g.imagePreview')"
@@ -21,7 +21,7 @@
<div
v-if="imageError"
role="alert"
class="flex flex-1 self-center size-full flex-col items-center justify-around bg-muted-background text-center text-base-foreground py-8"
class="flex size-full flex-1 flex-col items-center justify-around self-center bg-muted-background py-8 text-center text-base-foreground"
>
<i class="mb-2 icon-[lucide--image-off] size-12 text-base-foreground" />
<p class="text-sm text-base-foreground">
@@ -40,7 +40,7 @@
v-if="!imageError"
:src="currentImageUrl"
:alt="imageAltText"
class="absolute inset-0 block size-full object-contain pointer-events-none"
class="pointer-events-none absolute inset-0 block size-full object-contain"
@load="handleImageLoad"
@error="handleImageError"
/>

View File

@@ -5,7 +5,7 @@
v-tooltip.left="tooltipConfig"
:class="
cn(
'lg-slot lg-slot--input flex items-center group rounded-r-lg m-0',
'lg-slot lg-slot--input group m-0 flex items-center rounded-r-lg',
'cursor-crosshair',
dotOnly ? 'lg-slot--dot-only' : 'pr-6',
{
@@ -22,9 +22,9 @@
ref="connectionDotRef"
:class="
cn(
'-translate-x-1/2 w-3',
'w-3 -translate-x-1/2',
hasError &&
'before:ring-2 before:ring-error before:ring-offset-0 before:size-4 before:absolute before:rounded-full before:pointer-events-none'
'before:pointer-events-none before:absolute before:size-4 before:rounded-full before:ring-2 before:ring-error before:ring-offset-0'
)
"
:slot-data
@@ -34,13 +34,13 @@
/>
<!-- Slot Name -->
<div class="h-full flex items-center min-w-0">
<div class="flex h-full min-w-0 items-center">
<span
v-if="!props.dotOnly && !hasNoLabel"
:class="
cn(
'truncate text-node-component-slot-text',
hasError && 'text-error font-medium'
hasError && 'font-medium text-error'
)
"
>

View File

@@ -9,24 +9,24 @@
:data-node-id="nodeData.id"
:class="
cn(
'group/node bg-node-component-header-surface lg-node absolute text-sm',
'contain-style contain-layout min-w-[225px] min-h-(--node-height) w-(--node-width)',
'group/node lg-node absolute bg-node-component-header-surface text-sm',
'min-h-(--node-height) w-(--node-width) min-w-[225px] contain-layout contain-style',
shapeClass,
'touch-none flex flex-col',
'flex touch-none flex-col',
'border border-solid border-component-node-border',
// hover (only when node should handle events)
shouldHandleNodePointerEvents &&
'hover:ring-7 ring-node-component-ring',
'outline-transparent outline-3 focus-visible:outline-node-component-outline',
'ring-node-component-ring hover:ring-7',
'outline-3 outline-transparent focus-visible:outline-node-component-outline',
borderClass,
outlineClass,
cursorClass,
{
[`${beforeShapeClass} before:pointer-events-none before:absolute before:bg-bypass/60 before:inset-0`]:
[`${beforeShapeClass} before:pointer-events-none before:absolute before:inset-0 before:bg-bypass/60`]:
bypassed,
[`${beforeShapeClass} before:pointer-events-none before:absolute before:inset-0`]:
muted,
'ring-4 ring-primary-500 bg-primary-500/10': isDraggingOver
'bg-primary-500/10 ring-4 ring-primary-500': isDraggingOver
},
shouldHandleNodePointerEvents && !nodeData.flags?.ghost
? 'pointer-events-auto'
@@ -58,7 +58,7 @@
/>
<div
v-if="displayHeader"
class="flex flex-col justify-center items-center relative"
class="relative flex flex-col items-center justify-center"
>
<template v-if="isCollapsed">
<SlotConnectionDot
@@ -110,14 +110,14 @@
</div>
<div
class="flex flex-1 flex-col gap-1 pt-1 pb-3 bg-component-node-background rounded-b-2xl"
class="flex flex-1 flex-col gap-1 rounded-b-2xl bg-component-node-background pt-1 pb-3"
:data-testid="`node-body-${nodeData.id}`"
>
<NodeSlots :node-data="nodeData" />
<NodeWidgets v-if="nodeData.widgets?.length" :node-data="nodeData" />
<div v-if="hasCustomContent" class="min-h-0 flex-1 flex flex-col">
<div v-if="hasCustomContent" class="flex min-h-0 flex-1 flex-col">
<NodeContent
v-if="nodeMedia"
:node-data="nodeData"
@@ -147,7 +147,7 @@
"
:class="
cn(
'flex w-full h-7 rounded-b-2xl -z-1 text-xs rounded-t-none overflow-hidden divide-x divide-component-node-border',
'-z-1 flex h-7 w-full divide-x divide-component-node-border overflow-hidden rounded-t-none rounded-b-2xl text-xs',
!isCollapsed && '-mt-5 h-12'
)
"
@@ -157,7 +157,7 @@
variant="textonly"
:class="
cn(
'flex-1 rounded-none h-full',
'h-full flex-1 rounded-none',
hasAnyError &&
showErrorsTabEnabled &&
!nodeData.color &&
@@ -181,7 +181,7 @@
variant="textonly"
:class="
cn(
'flex-1 rounded-none h-full bg-error hover:bg-destructive-background-hover',
'h-full flex-1 rounded-none bg-error hover:bg-destructive-background-hover',
isCollapsed ? 'py-2' : 'pt-7 pb-2'
)
"
@@ -199,7 +199,7 @@
"
variant="textonly"
:class="
cn('flex-1 rounded-none h-full', isCollapsed ? 'py-2' : 'pt-7 pb-2')
cn('h-full flex-1 rounded-none', isCollapsed ? 'py-2' : 'pt-7 pb-2')
"
@click.stop="showAdvancedState = !showAdvancedState"
>
@@ -238,7 +238,7 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 12 12"
:class="cn('size-2/5 absolute', handle.svgPositionClasses)"
:class="cn('absolute size-2/5', handle.svgPositionClasses)"
:style="
handle.svgTransform ? { transform: handle.svgTransform } : undefined
"

View File

@@ -3,18 +3,18 @@
:data-node-id="nodeData.id"
:class="
cn(
'bg-component-node-background lg-node pb-1 contain-style contain-layout w-[350px] rounded-2xl touch-none flex flex-col border border-solid outline-transparent outline-2 border-node-stroke',
'lg-node flex w-[350px] touch-none flex-col rounded-2xl border border-solid border-node-stroke bg-component-node-background pb-1 outline-2 outline-transparent contain-layout contain-style',
position
)
"
>
<div
class="flex flex-col justify-center items-center relative pointer-events-none"
class="pointer-events-none relative flex flex-col items-center justify-center"
>
<NodeHeader :node-data="nodeData" />
</div>
<div
class="flex flex-1 flex-col gap-1 pb-2 pointer-events-none"
class="pointer-events-none flex flex-1 flex-col gap-1 pb-2"
:data-testid="`node-body-${nodeData.id}`"
>
<NodeSlots :node-data="nodeData" />

View File

@@ -11,7 +11,7 @@
v-else
:src="imageUrl"
:alt="$t('g.liveSamplingPreview')"
class="pointer-events-none w-full object-contain contain-size min-h-55 flex-1"
class="pointer-events-none min-h-55 w-full flex-1 object-contain contain-size"
@load="handleImageLoad"
@error="handleImageError"
/>

View File

@@ -1,7 +1,7 @@
<template>
<div
v-if="text"
class="min-w-max rounded-sm bg-node-component-surface px-1 py-0.5 text-xs flex items-center gap-1"
class="flex min-w-max items-center gap-1 rounded-sm bg-node-component-surface px-1 py-0.5 text-xs"
:style="{
color: fgColor,
backgroundColor: bgColor

View File

@@ -15,18 +15,18 @@ defineProps<{
>
<div
v-if="hasComfyBadge"
class="rounded-full bg-component-node-widget-background size-6 flex justify-center items-center"
class="flex size-6 items-center justify-center rounded-full bg-component-node-widget-background"
>
<i class="icon-[comfy--comfy-c] size-3" />
</div>
<div
v-if="core.length"
class="rounded-full bg-component-node-widget-background h-6 flex justify-center items-center overflow-clip"
class="flex h-6 items-center justify-center overflow-clip rounded-full bg-component-node-widget-background"
>
<template v-for="(badge, index) of core" :key="badge.text">
<div
v-if="index !== 0"
class="border-muted-foreground border-r h-4 mr-0.5 pr-0.5"
class="mr-0.5 h-4 border-r border-muted-foreground pr-0.5"
/>
<NodeBadge
bg-color="transparent"
@@ -37,12 +37,12 @@ defineProps<{
</div>
<div
v-if="extension.length"
class="rounded-full bg-component-node-widget-background h-6 flex justify-center items-center overflow-clip"
class="flex h-6 items-center justify-center overflow-clip rounded-full bg-component-node-widget-background"
>
<template v-for="(badge, index) of extension" :key="badge.text">
<div
v-if="index !== 0"
class="border-muted-foreground border-r h-4 mr-0.5 pr-0.5"
class="mr-0.5 h-4 border-r border-muted-foreground pr-0.5"
/>
<NodeBadge
bg-color="transparent"

View File

@@ -2,7 +2,7 @@
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ st('nodeErrors.content', 'Node Content Error') }}
</div>
<div v-else class="lg-node-content flex grow flex-col flex-auto">
<div v-else class="lg-node-content flex flex-auto grow flex-col">
<!-- Default slot for custom content -->
<slot>
<VideoPreview

View File

@@ -6,7 +6,7 @@
v-else
:class="
cn(
'lg-node-header text-sm py-2 pl-2 pr-3 w-full min-w-0',
'lg-node-header w-full min-w-0 py-2 pr-3 pl-2 text-sm',
'text-node-component-header',
headerShapeClass
)
@@ -14,9 +14,9 @@
:data-testid="`node-header-${nodeData?.id || ''}`"
@dblclick="handleDoubleClick"
>
<div class="flex items-center justify-between gap-2.5 min-w-0">
<div class="flex min-w-0 items-center justify-between gap-2.5">
<!-- Collapse/Expand Button -->
<div class="relative flex items-center gap-2.5 min-w-0 shrink mr-auto">
<div class="relative mr-auto flex min-w-0 shrink items-center gap-2.5">
<div class="flex shrink-0 items-center px-0.5">
<Button
size="icon-sm"
@@ -43,7 +43,7 @@
class="flex min-w-0 flex-1 items-center gap-2"
data-testid="node-title"
>
<div class="truncate flex-1">
<div class="flex-1 truncate">
<EditableText
:model-value="displayTitle"
:is-editing="isEditing"
@@ -59,17 +59,17 @@
<span
:class="
cn(
'flex h-5 bg-component-node-widget-background p-1 items-center text-xs shrink-0',
'flex h-5 shrink-0 items-center bg-component-node-widget-background p-1 text-xs',
badge.rest ? 'rounded-l-full pr-1' : 'rounded-full'
)
"
>
<i class="h-full icon-[lucide--component] bg-amber-400" />
<i class="icon-[lucide--component] h-full bg-amber-400" />
<span class="truncate" v-text="badge.required" />
</span>
<span
v-if="badge.rest"
class="truncate -ml-2.5 grow basis-0 bg-component-node-widget-background rounded-r-full max-w-max min-w-0"
class="-ml-2.5 max-w-max min-w-0 grow basis-0 truncate rounded-r-full bg-component-node-widget-background"
>
<span class="pr-2" v-text="badge.rest" />
</span>
@@ -77,7 +77,7 @@
<NodeBadge v-if="statusBadge" v-bind="statusBadge" />
<i
v-if="isPinned"
class="size-5 icon-[comfy--pin]"
class="icon-[comfy--pin] size-5"
data-testid="node-pin-indicator"
/>
</div>

View File

@@ -2,10 +2,10 @@
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ st('nodeErrors.slots', 'Node Slots Error') }}
</div>
<div v-else :class="cn('flex justify-between min-w-0', unifiedWrapperClass)">
<div v-else :class="cn('flex min-w-0 justify-between', unifiedWrapperClass)">
<div
v-if="filteredInputs.length"
:class="cn('flex flex-col min-w-0', unifiedDotsClass)"
:class="cn('flex min-w-0 flex-col', unifiedDotsClass)"
>
<InputSlot
v-for="(input, index) in filteredInputs"
@@ -19,7 +19,7 @@
<div
v-if="nodeData?.outputs?.length"
:class="cn('ml-auto flex flex-col min-w-0', unifiedDotsClass)"
:class="cn('ml-auto flex min-w-0 flex-col', unifiedDotsClass)"
>
<OutputSlot
v-for="(output, index) in nodeData.outputs"
@@ -68,13 +68,13 @@ const filteredInputs = computed(() => [
const unifiedWrapperClass = computed((): string =>
cn(
unified &&
'absolute inset-0 items-center pointer-events-none opacity-0 z-30'
'pointer-events-none absolute inset-0 z-30 items-center opacity-0'
)
)
const unifiedDotsClass = computed((): string =>
cn(
unified &&
'grid grid-cols-1 grid-rows-1 gap-0 *:row-span-full *:col-span-full place-items-center'
'grid grid-cols-1 grid-rows-1 place-items-center gap-0 *:col-span-full *:row-span-full'
)
)

View File

@@ -33,7 +33,7 @@
<div
:class="
cn(
'z-10 w-3 opacity-0 transition-opacity duration-150 group-hover:opacity-100 flex items-stretch',
'z-10 flex w-3 items-stretch opacity-0 transition-opacity duration-150 group-hover:opacity-100',
widget.slotMetadata?.linked && 'opacity-100'
)
"
@@ -64,7 +64,7 @@
:class="
cn(
'col-span-2',
widget.hasError && 'text-node-stroke-error font-bold'
widget.hasError && 'font-bold text-node-stroke-error'
)
"
@update:model-value="widget.updateHandler"

View File

@@ -1,7 +1,7 @@
<template>
<div v-if="renderError" class="node-error p-1 text-xs text-red-500"></div>
<div v-else v-tooltip.right="tooltipConfig" :class="slotWrapperClass">
<div class="relative h-full flex items-center min-w-0">
<div class="relative flex h-full min-w-0 items-center">
<!-- Slot Name -->
<span
v-if="!props.dotOnly && !hasNoLabel"
@@ -97,7 +97,7 @@ const shouldDim = computed(() => {
const slotWrapperClass = computed(() =>
cn(
'lg-slot lg-slot--output flex items-center justify-end group rounded-l-lg h-6',
'lg-slot lg-slot--output group flex h-6 items-center justify-end rounded-l-lg',
'cursor-crosshair',
dotOnly.value ? 'lg-slot--dot-only justify-center' : 'pl-6',
{

View File

@@ -46,13 +46,13 @@ const isListShape = computed(() => props.slotData?.shape === RenderShape.GRID)
const slotClass = computed(() =>
cn(
'bg-slate-300 slot-dot',
'slot-dot bg-slate-300',
isListShape.value ? 'rounded-[1px]' : 'rounded-full',
'transition-all duration-150',
'border border-solid border-node-component-slot-dot-outline',
props.multi
? 'w-3 h-6'
: 'size-3 cursor-crosshair group-hover/slot:[--node-component-slot-dot-outline-opacity-mult:5] group-hover/slot:scale-125'
? 'h-6 w-3'
: 'size-3 cursor-crosshair group-hover/slot:scale-125 group-hover/slot:[--node-component-slot-dot-outline-opacity-mult:5]'
)
)
</script>
@@ -61,7 +61,7 @@ const slotClass = computed(() =>
<div
:class="
cn(
'after:absolute after:inset-y-0 after:w-5/2 relative size-6 flex items-center justify-center group/slot',
'group/slot relative flex size-6 items-center justify-center after:absolute after:inset-y-0 after:w-5/2',
props.class
)
"

View File

@@ -51,10 +51,10 @@ const controlMode = defineModel<ControlOptions>()
</script>
<template>
<div class="w-113 max-w-md p-4 space-y-4">
<div class="w-113 max-w-md space-y-4 p-4">
<div class="text-sm/tight text-muted-foreground">
{{ $t('widgets.valueControl.header.prefix') }}
<span class="text-base-foreground font-medium">
<span class="font-medium text-base-foreground">
{{
widgetControlMode === 'before'
? $t('widgets.valueControl.header.before')
@@ -71,12 +71,12 @@ const controlMode = defineModel<ControlOptions>()
as="label"
variant="textonly"
size="lg"
class="flex w-full h-[unset] text-left items-center justify-between py-2 gap-7"
class="flex h-[unset] w-full items-center justify-between gap-7 py-2 text-left"
:for="option.mode"
>
<div class="flex items-center gap-2 flex-1 min-w-0 text-wrap">
<div class="flex min-w-0 flex-1 items-center gap-2 text-wrap">
<div
class="flex items-center justify-center size-8 rounded-lg shrink-0 bg-secondary-background border border-border-subtle"
class="flex size-8 shrink-0 items-center justify-center rounded-lg border border-border-subtle bg-secondary-background"
>
<i
v-if="option.icon"
@@ -91,7 +91,7 @@ const controlMode = defineModel<ControlOptions>()
</span>
</div>
<div class="flex flex-col gap-0.5 min-w-0 flex-1">
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
<div class="text-sm/tight font-normal text-base-foreground">
<span>
{{ $t(`widgets.valueControl.${option.title}`) }}

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-1">
<Button
class="text-base-foreground w-full border-0 bg-component-node-widget-background p-2"
class="w-full border-0 bg-component-node-widget-background p-2 text-base-foreground"
:aria-label="widget.label"
size="sm"
variant="textonly"

View File

@@ -3,7 +3,7 @@
<WidgetLayoutField :widget="widget">
<label
:class="
cn(WidgetInputBaseClass, 'flex items-center gap-2 w-full px-4 py-2')
cn(WidgetInputBaseClass, 'flex w-full items-center gap-2 px-4 py-2')
"
>
<ColorPicker
@@ -17,7 +17,7 @@
@update:model-value="onPickerUpdate"
/>
<span
class="text-xs truncate min-w-[4ch]"
class="min-w-[4ch] truncate text-xs"
data-testid="widget-color-text"
>{{ toHexFromFormat(localValue, format) }}</span
>

View File

@@ -1,6 +1,6 @@
<template>
<WidgetLayoutField :widget="widget">
<div :class="cn(WidgetInputBaseClass, 'flex items-center gap-2 pl-3 pr-2')">
<div :class="cn(WidgetInputBaseClass, 'flex items-center gap-2 pr-2 pl-3')">
<GradientSlider
v-model="modelValue"
:stops="gradientStops"
@@ -9,7 +9,7 @@
:step="stepValue"
:disabled="widget.options?.disabled"
:aria-label="widget.name"
class="flex-1 min-w-0"
class="min-w-0 flex-1"
/>
<InputNumber
:key="timesEmptied"

View File

@@ -170,7 +170,7 @@ const inputAriaAttrs = computed(() => ({
:hide-buttons="buttonsDisabled"
:parse-value="parseWidgetValue"
:input-attrs="inputAriaAttrs"
:class="cn(WidgetInputBaseClass, 'grow text-xs flex h-7 relative')"
:class="cn(WidgetInputBaseClass, 'relative flex h-7 grow text-xs')"
@keydown.up.prevent="updateValueBy(stepValue)"
@keydown.down.prevent="updateValueBy(-stepValue)"
@keydown.page-up.prevent="updateValueBy(10 * stepValue)"
@@ -178,10 +178,10 @@ const inputAriaAttrs = computed(() => ({
>
<template #background>
<div
class="absolute size-full rounded-lg pointer-events-none overflow-clip"
class="pointer-events-none absolute size-full overflow-clip rounded-lg"
>
<div
class="bg-primary-background/15 size-full"
class="size-full bg-primary-background/15"
:style="{ width: `${sliderWidth}%` }"
/>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<WidgetLayoutField :widget="widget">
<div :class="cn(WidgetInputBaseClass, 'flex items-center gap-2 pl-3 pr-2')">
<div :class="cn(WidgetInputBaseClass, 'flex items-center gap-2 pr-2 pl-3')">
<Slider
:model-value="[modelValue]"
v-bind="filteredProps"

View File

@@ -4,7 +4,7 @@
<Loader
v-if="loading"
size="sm"
class="absolute left-3 top-1/2 z-10 -translate-y-1/2 text-component-node-foreground"
class="absolute top-1/2 left-3 z-10 -translate-y-1/2 text-component-node-foreground"
/>
<InputText
v-model="modelValue"
@@ -13,7 +13,7 @@
cn(
WidgetInputBaseClass,
'w-full px-4 hover:bg-component-node-widget-background-hovered',
size === 'large' ? 'text-sm py-3' : 'text-xs py-2',
size === 'large' ? 'py-3 text-sm' : 'py-2 text-xs',
loading && 'pl-9'
)
"

View File

@@ -2,7 +2,7 @@
<div class="relative" @pointerdown.stop>
<div class="mb-4">
<Button
class="text-base-foreground w-full border-0 bg-secondary-background hover:bg-secondary-background-hover"
class="w-full border-0 bg-secondary-background text-base-foreground hover:bg-secondary-background-hover"
:disabled="isRecording || readonly"
@click="handleStartRecording"
>
@@ -12,7 +12,7 @@
</div>
<div
v-if="isRecording || isPlaying || recordedURL"
class="flex h-14 w-full min-w-0 items-center gap-2 rounded-lg px-3 bg-node-component-surface text-text-secondary"
class="flex h-14 w-full min-w-0 items-center gap-2 rounded-lg bg-node-component-surface px-3 text-text-secondary"
>
<!-- Recording Status -->
<div class="flex shrink-0 items-center gap-1">
@@ -45,7 +45,7 @@
<button
v-if="isRecording"
:title="t('g.stopRecording', 'Stop Recording')"
class="flex shrink-0 size-8 animate-pulse items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
class="flex size-8 shrink-0 animate-pulse items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
@click="handleStopRecording"
>
<div class="size-2.5 rounded-sm bg-danger-100" />
@@ -54,19 +54,19 @@
<button
v-else-if="!isRecording && recordedURL && !isPlaying"
:title="t('g.playRecording') || 'Play Recording'"
class="flex shrink-0 size-8 items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
class="flex size-8 shrink-0 items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
@click="handlePlayRecording"
>
<i class="text-text-secondary icon-[lucide--play] size-4" />
<i class="icon-[lucide--play] size-4 text-text-secondary" />
</button>
<button
v-else-if="isPlaying"
:title="t('g.stopPlayback') || 'Stop Playback'"
class="flex shrink-0 size-8 items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
class="flex size-8 shrink-0 items-center justify-center rounded-full border-0 bg-smoke-500/33 transition-colors"
@click="handleStopPlayback"
>
<i class="text-text-secondary icon-[lucide--square] size-4" />
<i class="icon-[lucide--square] size-4 text-text-secondary" />
</button>
</div>
<audio

View File

@@ -13,7 +13,7 @@
:pt="{
option: 'text-xs',
dropdown: 'w-8',
label: cn('truncate min-w-[4ch]', $slots.default && 'mr-5'),
label: cn('min-w-[4ch] truncate', $slots.default && 'mr-5'),
overlay: 'w-fit min-w-full'
}"
data-capture-wheel="true"
@@ -24,7 +24,7 @@
/>
</template>
</SelectPlus>
<div class="absolute top-5 right-8 h-4 w-7 -translate-y-4/5 flex">
<div class="absolute top-5 right-8 flex h-4 w-7 -translate-y-4/5">
<slot />
</div>
</WidgetLayoutField>

View File

@@ -2,7 +2,7 @@
<div
:class="
cn(
'group relative rounded-lg hover:bg-component-node-widget-background-hovered focus-within:ring focus-within:ring-component-node-widget-background-highlighted transition-all',
'group relative rounded-lg transition-all focus-within:ring focus-within:ring-component-node-widget-background-highlighted hover:bg-component-node-widget-background-hovered',
widget.borderStyle
)
"
@@ -10,7 +10,7 @@
<label
v-if="!hideLayoutField"
:for="id"
class="pointer-events-none absolute left-3 top-1.5 z-10 text-xxs text-muted-foreground"
class="pointer-events-none absolute top-1.5 left-3 z-10 text-xxs text-muted-foreground"
>
{{ displayName }}
</label>
@@ -21,7 +21,7 @@
:class="
cn(
WidgetInputBaseClass,
'size-full text-xs resize-none',
'size-full resize-none text-xs',
!hideLayoutField && 'pt-5'
)
"
@@ -37,7 +37,7 @@
v-if="isReadOnly"
variant="textonly"
size="icon"
class="invisible absolute top-1.5 right-1.5 z-10 hover:bg-base-foreground/10 group-hover:visible group-focus-within:visible"
class="invisible absolute top-1.5 right-1.5 z-10 group-focus-within:visible group-hover:visible hover:bg-base-foreground/10"
:title="$t('g.copyToClipboard')"
:aria-label="$t('g.copyToClipboard')"
@click="handleCopy"

View File

@@ -9,7 +9,7 @@
:class="
cn(
WidgetInputBaseClass,
'w-full min-w-0 p-1 flex items-center justify-center gap-1'
'flex w-full min-w-0 items-center justify-center gap-1 p-1'
)
"
@update:model-value="(v) => handleOptionChange(v as string)"

View File

@@ -45,10 +45,10 @@ watch(controlModel, props.widget.controlWidget.update)
<Button
variant="textonly"
size="sm"
class="h-4 w-7 p-0 self-center rounded-xl bg-primary-background/30 hover:bg-primary-background-hover/30"
class="h-4 w-7 self-center rounded-xl bg-primary-background/30 p-0 hover:bg-primary-background-hover/30"
>
<i
:class="`${controlButtonIcon} text-primary-background text-xs w-full`"
:class="`${controlButtonIcon} w-full text-xs text-primary-background`"
/>
</Button>
</template>

View File

@@ -2,7 +2,7 @@
<div class="relative" @pointerdown.stop>
<div
v-if="!hideWhenEmpty || modelValue"
class="bg-component-node-widget-background box-border flex gap-4 items-center justify-start relative rounded-lg w-full h-16 px-4 py-0"
class="relative box-border flex h-16 w-full items-center justify-start gap-4 rounded-lg bg-component-node-widget-background px-4 py-0"
>
<!-- Hidden audio element -->
<audio
@@ -108,7 +108,7 @@
popup
class="audio-player-menu"
:pt:root:class="
cn('bg-component-node-widget-background border-component-node-border')
cn('border-component-node-border bg-component-node-widget-background')
"
:pt:submenu:class="cn('bg-component-node-widget-background')"
>

View File

@@ -65,9 +65,9 @@ function handleFocus(event: FocusEvent) {
:class="
cn(
'group',
'bg-component-node-widget-background rounded-lg transition-all duration-150',
'flex-1 flex items-center',
'text-base-foreground border-0',
'rounded-lg bg-component-node-widget-background transition-all duration-150',
'flex flex-1 items-center',
'border-0 text-base-foreground',
'focus-within:ring focus-within:ring-component-node-widget-background-highlighted/80',
customClass
)
@@ -76,27 +76,27 @@ function handleFocus(event: FocusEvent) {
<i
:class="
cn(
'size-4 ml-2 shrink-0 transition-colors duration-150',
'ml-2 size-4 shrink-0 transition-colors duration-150',
isQuerying
? 'icon-[lucide--loader-circle] animate-spin'
: 'icon-[lucide--search]',
searchQuery?.trim() !== ''
? 'text-base-foreground'
: 'text-muted-foreground group-hover:text-base-foreground group-focus-within:text-base-foreground'
: 'text-muted-foreground group-focus-within:text-base-foreground group-hover:text-base-foreground'
)
"
/>
<input
v-model="searchQuery"
type="text"
class="bg-transparent border-0 outline-0 ring-0 h-5 w-full my-1.5 mx-2 min-w-0"
class="mx-2 my-1.5 h-5 w-full min-w-0 border-0 bg-transparent ring-0 outline-0"
:placeholder="$t('g.searchPlaceholder', { subject: '' })"
:autofocus
@focus="handleFocus"
/>
<button
v-if="searchQuery.trim().length > 0"
class="text-muted-foreground hover:text-base-foreground bg-transparent shrink-0 border-0 outline-0 ring-0 p-0 m-0 pr-3 pl-1 flex items-center justify-center transition-all duration-150 hover:scale-108"
class="m-0 flex shrink-0 items-center justify-center border-0 bg-transparent p-0 pr-3 pl-1 text-muted-foreground ring-0 outline-0 transition-all duration-150 hover:scale-108 hover:text-base-foreground"
:aria-label="$t('g.clear')"
@click="searchQuery = ''"
>

View File

@@ -1,7 +1,7 @@
<template>
<div
:class="
cn(WidgetInputBaseClass, 'w-full p-1 flex min-w-0 items-center gap-1')
cn(WidgetInputBaseClass, 'flex w-full min-w-0 items-center gap-1 p-1')
"
>
<button
@@ -9,15 +9,15 @@
:key="getOptionValue(option, index)"
:class="
cn(
'flex-1 min-w-0 h-6 px-5 py-[5px] rounded-sm flex justify-center items-center gap-1 transition-all duration-150 ease-in-out truncate',
'bg-transparent border-none',
'flex h-6 min-w-0 flex-1 items-center justify-center gap-1 truncate rounded-sm px-5 py-[5px] transition-all duration-150 ease-in-out',
'border-none bg-transparent',
'text-center text-xs font-normal',
{
'bg-interface-menu-component-surface-selected':
isSelected(index) && !disabled,
'hover:bg-interface-menu-component-surface-selected/50':
!isSelected(index) && !disabled,
'opacity-50 cursor-not-allowed': disabled,
'cursor-not-allowed opacity-50': disabled,
'cursor-pointer': !disabled
},
isSelected(index) && !disabled

View File

@@ -43,10 +43,10 @@ const selectedItems = computed(() => {
const theButtonStyle = computed(() =>
cn(
'border-0 bg-component-node-widget-background outline-none text-text-secondary',
'border-0 bg-component-node-widget-background text-text-secondary outline-none',
disabled
? 'cursor-not-allowed'
: 'hover:bg-component-node-widget-background-hovered cursor-pointer',
: 'cursor-pointer hover:bg-component-node-widget-background-hovered',
selectedItems.value.length > 0 && 'text-text-primary'
)
)
@@ -56,7 +56,7 @@ const theButtonStyle = computed(() =>
<div
:class="
cn(WidgetInputBaseClass, 'flex text-base leading-none', {
'opacity-50 cursor-not-allowed outline-node-component-border': disabled
'cursor-not-allowed opacity-50 outline-node-component-border': disabled
})
"
>
@@ -64,7 +64,7 @@ const theButtonStyle = computed(() =>
:class="
cn(
theButtonStyle,
'flex justify-between items-center flex-1 min-w-0 h-8',
'flex h-8 min-w-0 flex-1 items-center justify-between',
{
'rounded-l-lg': uploadable,
'rounded-lg': !uploadable
@@ -73,7 +73,7 @@ const theButtonStyle = computed(() =>
"
@click="emit('select-click', $event)"
>
<span class="min-w-0 flex-1 px-1 py-2 text-left truncate">
<span class="min-w-0 flex-1 truncate px-1 py-2 text-left">
<span v-if="!selectedItems.length">
{{ placeholder }}
</span>
@@ -85,7 +85,7 @@ const theButtonStyle = computed(() =>
class="icon-[lucide--chevron-down]"
:class="
cn(
'mr-2 size-4 transition-transform duration-200 shrink-0 text-component-node-foreground-secondary',
'mr-2 size-4 shrink-0 text-component-node-foreground-secondary transition-transform duration-200',
isOpen && 'rotate-180'
)
"
@@ -97,7 +97,7 @@ const theButtonStyle = computed(() =>
cn(
theButtonStyle,
'relative',
'size-8 flex justify-center items-center border-l rounded-r-lg border-node-component-border'
'flex size-8 items-center justify-center rounded-r-lg border-l border-node-component-border'
)
"
>

View File

@@ -42,7 +42,7 @@ const baseModelSelected = defineModel<Set<string>>('baseModelSelected', {
})
const actionButtonStyle = cn(
'h-8 bg-zinc-500/20 rounded-lg outline-1 -outline-offset-1 outline-node-component-border transition-all duration-150'
'h-8 rounded-lg bg-zinc-500/20 outline-1 -outline-offset-1 outline-node-component-border transition-all duration-150'
)
const layoutSwitchItemStyle =
@@ -114,7 +114,7 @@ function toggleBaseModelSelection(item: FilterOption) {
cn(
actionButtonStyle,
'hover:outline-component-node-widget-background-highlighted/80',
'focus-within:outline-component-node-widget-background-highlighted/80 focus-within:ring-0'
'focus-within:ring-0 focus-within:outline-component-node-widget-background-highlighted/80'
)
"
/>
@@ -155,7 +155,7 @@ function toggleBaseModelSelection(item: FilterOption) {
<div
:class="
cn(
'flex flex-col gap-2 p-2 min-w-32',
'flex min-w-32 flex-col gap-2 p-2',
'bg-component-node-background',
'rounded-lg outline -outline-offset-1 outline-component-node-border'
)
@@ -166,7 +166,7 @@ function toggleBaseModelSelection(item: FilterOption) {
:key="item.name"
variant="textonly"
size="unset"
:class="cn('flex justify-between items-center h-6 text-left')"
:class="cn('flex h-6 items-center justify-between text-left')"
@click="handleSortSelected(item)"
>
<span>{{ item.name }}</span>
@@ -217,7 +217,7 @@ function toggleBaseModelSelection(item: FilterOption) {
<div
:class="
cn(
'flex flex-col gap-2 p-2 min-w-32',
'flex min-w-32 flex-col gap-2 p-2',
'bg-component-node-background',
'rounded-lg outline -outline-offset-1 outline-component-node-border'
)
@@ -228,7 +228,7 @@ function toggleBaseModelSelection(item: FilterOption) {
:key="item.value"
variant="textonly"
size="unset"
:class="cn('flex justify-between items-center h-6 text-left')"
:class="cn('flex h-6 items-center justify-between text-left')"
@click="handleOwnershipSelected(item)"
>
<span>{{ item.name }}</span>
@@ -279,7 +279,7 @@ function toggleBaseModelSelection(item: FilterOption) {
<div
:class="
cn(
'flex flex-col gap-2 p-2 min-w-32',
'flex min-w-32 flex-col gap-2 p-2',
'bg-component-node-background',
'rounded-lg outline -outline-offset-1 outline-component-node-border'
)
@@ -290,7 +290,7 @@ function toggleBaseModelSelection(item: FilterOption) {
:key="item.value"
variant="textonly"
size="unset"
:class="cn('flex justify-between items-center h-6 text-left')"
:class="cn('flex h-6 items-center justify-between text-left')"
@click="toggleBaseModelSelection(item)"
>
<span>{{ item.name }}</span>
@@ -303,7 +303,7 @@ function toggleBaseModelSelection(item: FilterOption) {
<Button
variant="textonly"
size="unset"
:class="cn('flex justify-between items-center h-6 text-left')"
:class="cn('flex h-6 items-center justify-between text-left')"
@click="baseModelSelected = new Set()"
>
{{ t('g.clearFilters') }}
@@ -315,7 +315,7 @@ function toggleBaseModelSelection(item: FilterOption) {
:class="
cn(
actionButtonStyle,
'flex justify-center items-center p-1 gap-1 hover:outline-component-node-widget-background-highlighted'
'flex items-center justify-center gap-1 p-1 hover:outline-component-node-widget-background-highlighted'
)
"
>

View File

@@ -18,7 +18,7 @@ const singleFilterOption = computed(() => filterOptions.length === 1)
</script>
<template>
<div class="text-secondary mb-4 flex gap-1 px-4 justify-start">
<div class="text-secondary mb-4 flex justify-start gap-1 px-4">
<button
v-for="option in filterOptions"
:key="option.value"
@@ -26,9 +26,9 @@ const singleFilterOption = computed(() => filterOptions.length === 1)
:disabled="singleFilterOption"
:class="
cn(
'px-4 py-2 rounded-md inline-flex justify-center items-center select-none appearance-none border-0 text-base-foreground',
'inline-flex appearance-none items-center justify-center rounded-md border-0 px-4 py-2 text-base-foreground select-none',
!singleFilterOption &&
'transition-all duration-150 hover:text-base-foreground hover:bg-interface-menu-component-surface-hovered cursor-pointer active:scale-95',
'cursor-pointer transition-all duration-150 hover:bg-interface-menu-component-surface-hovered hover:text-base-foreground active:scale-95',
!singleFilterOption && filterSelected === option.value
? 'bg-interface-menu-component-surface-selected! text-base-foreground'
: 'bg-transparent'

View File

@@ -55,13 +55,13 @@ function handleVideoLoad(event: Event) {
<div
:class="
cn(
'flex gap-1 select-none group/item cursor-pointer bg-component-node-widget-background',
'group/item flex cursor-pointer gap-1 bg-component-node-widget-background select-none',
'transition-[transform,box-shadow,background-color] duration-150',
{
'flex-col text-center': layout === 'grid',
'flex-row text-left max-h-16 rounded-lg hover:scale-102 active:scale-98':
'max-h-16 flex-row rounded-lg text-left hover:scale-102 active:scale-98':
layout === 'list',
'flex-row text-left hover:bg-component-node-widget-background-hovered rounded-lg':
'flex-row rounded-lg text-left hover:bg-component-node-widget-background-hovered':
layout === 'list-small',
// selection
'ring-2 ring-component-node-widget-background-highlighted':
@@ -77,10 +77,10 @@ function handleVideoLoad(event: Event) {
:class="
cn(
'relative',
'w-full aspect-square overflow-hidden outline-1 -outline-offset-1 outline-interface-stroke',
'aspect-square w-full overflow-hidden outline-1 -outline-offset-1 outline-interface-stroke',
'transition-[transform,box-shadow] duration-150',
{
'min-w-16 max-w-16 rounded-l-lg': layout === 'list',
'max-w-16 min-w-16 rounded-l-lg': layout === 'list',
'rounded-sm group-hover/item:scale-108 group-active/item:scale-95':
layout === 'grid',
// selection
@@ -96,7 +96,7 @@ function handleVideoLoad(event: Event) {
class="absolute top-1 left-1 size-4 rounded-full border border-base-foreground bg-primary-background"
>
<i
class="icon-[lucide--check] size-3 translate-y-[-0.5px] text-base-foreground bold"
class="bold icon-[lucide--check] size-3 translate-y-[-0.5px] text-base-foreground"
/>
</div>
<video
@@ -125,8 +125,8 @@ function handleVideoLoad(event: Event) {
:class="
cn('flex gap-1', {
'flex-col': layout === 'grid',
'flex-col px-4 py-1 w-full justify-center min-w-0': layout === 'list',
'flex-row p-2 items-center justify-between w-full':
'w-full min-w-0 flex-col justify-center px-4 py-1': layout === 'list',
'w-full flex-row items-center justify-between p-2':
layout === 'list-small'
})
"
@@ -135,7 +135,7 @@ function handleVideoLoad(event: Event) {
v-tooltip="layout === 'grid' ? (label ?? name) : undefined"
:class="
cn(
'block text-xs line-clamp-2 wrap-break-word overflow-hidden',
'line-clamp-2 block overflow-hidden text-xs wrap-break-word',
'transition-colors duration-150',
// selection
!!selected && 'text-base-foreground'

View File

@@ -18,12 +18,12 @@ const hideLayoutField = useHideLayoutField()
<div
:class="
cn(
'grid grid-cols-subgrid min-w-0 justify-between gap-1 text-node-component-slot-text',
'grid min-w-0 grid-cols-subgrid justify-between gap-1 text-node-component-slot-text',
rootClass
)
"
>
<div v-if="!hideLayoutField" class="truncate content-center-safe">
<div v-if="!hideLayoutField" class="content-center-safe truncate">
<template v-if="widget.name">
{{ widget.label || widget.name }}
</template>
@@ -33,7 +33,7 @@ const hideLayoutField = useHideLayoutField()
<div
:class="
cn(
'cursor-default min-w-0 rounded-lg focus-within:ring focus-within:ring-component-node-widget-background-highlighted transition-all',
'min-w-0 cursor-default rounded-lg transition-all focus-within:ring focus-within:ring-component-node-widget-background-highlighted',
widget.borderStyle
)
"

View File

@@ -3,7 +3,7 @@ import { cn } from '@comfyorg/tailwind-utils'
const sharedButtonClasses = cn(
'inline-flex items-center justify-center border-0 bg-transparent text-inherit transition-colors duration-150 ease-in-out',
'hover:bg-node-component-surface-hovered active:bg-node-component-surface-selected',
'disabled:bg-node-component-disabled disabled:text-node-icon-disabled disabled:cursor-not-allowed'
'disabled:cursor-not-allowed disabled:bg-node-component-disabled disabled:text-node-icon-disabled'
)
export function useNumberWidgetButtonPt(options?: {