Lint: Add tailwind linter (#5984)

## Summary

Adds the [tailwind lint
plugin](https://github.com/francoismassart/eslint-plugin-tailwindcss/?tab=readme-ov-file#eslint-plugin-tailwindcss)
and fixes the currently fixable rules ([v4 is still in
beta](https://github.com/francoismassart/eslint-plugin-tailwindcss/?tab=readme-ov-file#about-tailwind-css-4-support)).

## Changes

- **What**: Enforces things like consistent class order, and eventually
can prohibit extra classes that could be utilities instead
- **Dependencies**: The plugin and its types

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5984-Lint-Add-tailwind-linter-2866d73d365081d89db0d998232533bb)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Alexander Brown
2025-10-08 19:39:14 -07:00
committed by GitHub
parent c9da8b200d
commit b943c0fa75
177 changed files with 731 additions and 652 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-1">
<div
class="p-4 border border-gray-300 dark-theme:border-gray-600 rounded max-h-[48rem]"
class="max-h-[48rem] rounded border border-gray-300 p-4 dark-theme:border-gray-600"
>
<Chart :type="chartType" :data="chartData" :options="chartOptions" />
</div>

View File

@@ -9,7 +9,7 @@
<ColorPicker
v-model="localValue"
v-bind="filteredProps"
class="w-8 h-4 !rounded-full overflow-hidden border-none"
class="h-4 w-8 overflow-hidden !rounded-full border-none"
:pt="{
preview: '!w-full !h-full !border-none'
}"

View File

@@ -7,10 +7,10 @@
style="width: calc(100% + 1rem)"
>
<!-- Select section above image -->
<div class="flex items-center justify-between gap-4 mb-2 px-2">
<div class="mb-2 flex items-center justify-between gap-4 px-2">
<label
v-if="widget.name"
class="text-xs opacity-80 min-w-[4em] truncate"
class="min-w-[4em] truncate text-xs opacity-80"
>{{ widget.name }}</label
>
<!-- Group select and folder button together on the right -->
@@ -21,7 +21,7 @@
:options="[selectedFile?.name || '']"
:disabled="true"
v-bind="transformCompatProps"
class="min-w-[8em] max-w-[20em] text-xs"
class="max-w-[20em] min-w-[8em] text-xs"
size="small"
:pt="{
option: 'text-xs'
@@ -30,7 +30,7 @@
<Button
icon="pi pi-folder"
size="small"
class="!w-8 !h-8"
class="!h-8 !w-8"
@click="triggerFileInput"
/>
</div>
@@ -38,31 +38,31 @@
<!-- Image preview -->
<!-- TODO: change hardcoded colors when design system incorporated -->
<div class="relative group">
<img :src="imageUrl" :alt="selectedFile?.name" class="w-full h-auto" />
<div class="group relative">
<img :src="imageUrl" :alt="selectedFile?.name" class="h-auto w-full" />
<!-- Darkening overlay on hover -->
<div
class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-all duration-200 pointer-events-none"
class="bg-opacity-0 group-hover:bg-opacity-20 pointer-events-none absolute inset-0 bg-black transition-all duration-200"
/>
<!-- Control buttons in top right on hover -->
<div
class="absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
class="absolute top-2 right-2 flex gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100"
>
<!-- Edit button -->
<button
class="w-6 h-6 rounded flex items-center justify-center transition-all duration-150 focus:outline-none border-none"
class="flex h-6 w-6 items-center justify-center rounded border-none transition-all duration-150 focus:outline-none"
style="background-color: #262729"
@click="handleEdit"
>
<i class="pi pi-pencil text-white text-xs"></i>
<i class="pi pi-pencil text-xs text-white"></i>
</button>
<!-- Delete button -->
<button
class="w-6 h-6 rounded flex items-center justify-center transition-all duration-150 focus:outline-none border-none"
class="flex h-6 w-6 items-center justify-center rounded border-none transition-all duration-150 focus:outline-none"
style="background-color: #262729"
@click="clearFile"
>
<i class="pi pi-times text-white text-xs"></i>
<i class="pi pi-times text-xs text-white"></i>
</button>
</div>
</div>
@@ -75,10 +75,10 @@
style="width: calc(100% + 1rem)"
>
<!-- Select section above audio player -->
<div class="flex items-center justify-between gap-4 mb-2 px-2">
<div class="mb-2 flex items-center justify-between gap-4 px-2">
<label
v-if="widget.name"
class="text-xs opacity-80 min-w-[4em] truncate"
class="min-w-[4em] truncate text-xs opacity-80"
>{{ widget.name }}</label
>
<!-- Group select and folder button together on the right -->
@@ -88,7 +88,7 @@
:options="[selectedFile?.name || '']"
:disabled="true"
v-bind="transformCompatProps"
class="min-w-[8em] max-w-[20em] text-xs"
class="max-w-[20em] min-w-[8em] text-xs"
size="small"
:pt="{
option: 'text-xs'
@@ -97,16 +97,16 @@
<Button
icon="pi pi-folder"
size="small"
class="!w-8 !h-8"
class="!h-8 !w-8"
@click="triggerFileInput"
/>
</div>
</div>
<!-- Audio player -->
<div class="relative group px-2">
<div class="group relative px-2">
<div
class="bg-[#1a1b1e] rounded-lg p-4 flex items-center gap-4"
class="flex items-center gap-4 rounded-lg bg-[#1a1b1e] p-4"
style="border: 1px solid #262729"
>
<!-- Audio icon -->
@@ -116,7 +116,7 @@
<!-- File info and controls -->
<div class="flex-1">
<div class="text-sm font-medium mb-1">{{ selectedFile?.name }}</div>
<div class="mb-1 text-sm font-medium">{{ selectedFile?.name }}</div>
<div class="text-xs opacity-60">
{{
selectedFile ? (selectedFile.size / 1024).toFixed(1) + ' KB' : ''
@@ -128,10 +128,10 @@
<div class="flex gap-1">
<!-- Delete button -->
<button
class="w-8 h-8 rounded flex items-center justify-center transition-all duration-150 focus:outline-none border-none hover:bg-[#262729]"
class="flex h-8 w-8 items-center justify-center rounded border-none transition-all duration-150 hover:bg-[#262729] focus:outline-none"
@click="clearFile"
>
<i class="pi pi-times text-white text-sm"></i>
<i class="pi pi-times text-sm text-white"></i>
</button>
</div>
</div>
@@ -141,14 +141,14 @@
<!-- Show normal file upload UI when no image or audio is loaded -->
<div
v-else
class="flex flex-col gap-1 w-full border border-solid p-1 rounded-lg"
class="flex w-full flex-col gap-1 rounded-lg border border-solid p-1"
:style="{ borderColor: '#262729' }"
>
<div
class="border border-dashed p-1 rounded-md transition-colors duration-200 hover:border-slate-300"
class="rounded-md border border-dashed p-1 transition-colors duration-200 hover:border-slate-300"
:style="{ borderColor: '#262729' }"
>
<div class="flex flex-col items-center gap-2 w-full py-4">
<div class="flex w-full flex-col items-center gap-2 py-4">
<span class="text-xs opacity-60"> {{ $t('Drop your file or') }} </span>
<div>
<Button

View File

@@ -29,18 +29,18 @@
item?.alt ||
`${t('g.galleryImage')} ${activeIndex + 1} of ${galleryImages.length}`
"
class="w-full h-auto max-h-64 object-contain"
class="h-auto max-h-64 w-full object-contain"
/>
</template>
<template #thumbnail="{ item }">
<div class="p-1 w-full h-full">
<div class="h-full w-full p-1">
<img
:src="item?.thumbnailImageSrc || item?.src || ''"
:alt="
item?.alt ||
`${t('g.galleryThumbnail')} ${galleryImages.findIndex((img) => img === item) + 1} of ${galleryImages.length}`
"
class="w-full h-full object-cover rounded-lg"
class="h-full w-full rounded-lg object-cover"
/>
</div>
</template>

View File

@@ -11,14 +11,14 @@
<img
:src="beforeImage"
:alt="beforeAlt"
class="w-full h-full object-cover"
class="h-full w-full object-cover"
/>
</template>
<template #right>
<img
:src="afterImage"
:alt="afterAlt"
class="w-full h-full object-cover"
class="h-full w-full object-cover"
/>
</template>
</ImageCompare>

View File

@@ -5,7 +5,7 @@
>
<!-- Display mode: Rendered markdown -->
<div
class="comfy-markdown-content hover:bg-[var(--p-content-hover-background)] text-sm min-h-[60px] w-full rounded-lg px-4 py-2 overflow-y-auto lod-toggle"
class="comfy-markdown-content lod-toggle min-h-[60px] w-full overflow-y-auto rounded-lg px-4 py-2 text-sm hover:bg-[var(--p-content-hover-background)]"
:class="isEditing === false ? 'visible' : 'invisible'"
v-html="renderedHtml"
/>
@@ -15,7 +15,7 @@
v-show="isEditing"
ref="textareaRef"
v-model="localValue"
class="w-full min-h-[60px] absolute inset-0 resize-none"
class="absolute inset-0 min-h-[60px] w-full resize-none"
:pt="{
root: {
class: 'text-sm w-full h-full',

View File

@@ -65,7 +65,7 @@ const theButtonStyle = computed(() =>
"
@click="emit('select-click', $event)"
>
<span class="px-4 py-2 min-w-0 text-left">
<span class="min-w-0 px-4 py-2 text-left">
<span v-if="!selectedItems.length" class="min-w-0">
{{ props.placeholder }}
</span>
@@ -89,7 +89,7 @@ const theButtonStyle = computed(() =>
<i class="icon-[lucide--folder-search] size-4" />
<input
type="file"
class="opacity-0 absolute inset-0 -z-1"
class="absolute inset-0 -z-1 opacity-0"
:multiple="maxSelectable > 1"
:disabled="disabled"
:accept="accept"

View File

@@ -36,7 +36,7 @@ const searchQuery = defineModel<string>('searchQuery')
<template>
<div
class="w-103 max-h-[640px] pt-4 bg-node-component-surface rounded-lg outline outline-offset-[-1px] outline-node-component-border flex flex-col"
class="flex max-h-[640px] w-103 flex-col rounded-lg bg-node-component-surface pt-4 outline outline-offset-[-1px] outline-node-component-border"
>
<!-- Filter -->
<FormDropdownMenuFilter
@@ -53,7 +53,7 @@ const searchQuery = defineModel<string>('searchQuery')
:is-querying="isQuerying"
/>
<!-- List -->
<div class="flex overflow-hidden relative h-full">
<div class="relative flex h-full overflow-hidden">
<div
:class="
cn(
@@ -67,11 +67,11 @@ const searchQuery = defineModel<string>('searchQuery')
"
>
<div
class="absolute top-0 inset-x-3 h-5 bg-gradient-to-b from-backdrop to-transparent pointer-events-none z-10"
class="pointer-events-none absolute inset-x-3 top-0 z-10 h-5 bg-gradient-to-b from-backdrop to-transparent"
/>
<div
v-if="items.length === 0"
class="flex justify-center items-center absolute inset-0"
class="absolute inset-0 flex items-center justify-center"
>
<i
title="No items"

View File

@@ -44,7 +44,7 @@ function handleSortSelected(item: SortOption) {
</script>
<template>
<div class="flex gap-2 text-zinc-400 px-4">
<div class="flex gap-2 px-4 text-zinc-400">
<label
:class="
cn(
@@ -58,9 +58,9 @@ function handleSortSelected(item: SortOption) {
>
<i
v-if="isQuerying"
class="icon-[lucide--loader-circle] mr-2 size-4 animate-spin"
class="mr-2 icon-[lucide--loader-circle] size-4 animate-spin"
/>
<i v-else class="icon-[lucide--search] mr-2 size-4" />
<i v-else class="mr-2 icon-[lucide--search] size-4" />
<input
v-model="searchQuery"
type="text"
@@ -85,7 +85,7 @@ function handleSortSelected(item: SortOption) {
>
<div
v-if="sortSelected !== 'default'"
class="size-2 absolute top-[-2px] left-[-2px] bg-blue-500 rounded-full"
class="absolute top-[-2px] left-[-2px] size-2 rounded-full bg-blue-500"
/>
<i class="icon-[lucide--arrow-up-down] size-4" />
</button>

View File

@@ -11,7 +11,7 @@ const filterSelected = defineModel<OptionId>('filterSelected')
</script>
<template>
<div class="flex gap-1 text-zinc-400 px-4 mb-4">
<div class="mb-4 flex gap-1 px-4 text-zinc-400">
<div
v-for="option in filterOptions"
:key="option.id"

View File

@@ -92,10 +92,10 @@ function handleVideoLoad(event: Event) {
<!-- Selected Icon -->
<div
v-if="selected"
class="rounded-full bg-blue-500 border-1 border-white size-4 absolute top-1 left-1"
class="absolute top-1 left-1 size-4 rounded-full border-1 border-white bg-blue-500"
>
<i
class="icon-[lucide--check] size-3 text-white -translate-y-[0.5px]"
class="icon-[lucide--check] size-3 translate-y-[-0.5px] text-white"
/>
</div>
<video

View File

@@ -12,12 +12,12 @@ defineProps<{
<template>
<div
class="flex items-center justify-between gap-2 h-[30px] overscroll-contain"
class="flex h-[30px] items-center justify-between gap-2 overscroll-contain"
>
<div class="relative h-6 flex items-center mr-4">
<div class="relative mr-4 flex h-6 items-center">
<p
v-if="widget.name"
class="text-sm text-node-component-slot-text font-normal flex-1 truncate w-20 lod-toggle"
class="lod-toggle w-20 flex-1 truncate text-sm font-normal text-node-component-slot-text"
>
{{ widget.label || widget.name }}
</p>
@@ -25,7 +25,7 @@ defineProps<{
</div>
<div class="relative">
<div
class="w-75 cursor-default lod-toggle"
class="lod-toggle w-75 cursor-default"
@pointerdown.stop="noop"
@pointermove.stop="noop"
@pointerup.stop="noop"