style: apply Tailwind CSS class sorting across codebase

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019bd8c8-bce1-70bc-a125-baf2a1503ee8
This commit is contained in:
Alexander Brown
2026-01-19 16:19:18 -08:00
parent 0bbb2d73e1
commit e7ce294aeb
168 changed files with 1777 additions and 1103 deletions

View File

@@ -226,8 +226,8 @@ In this project, only the `<i class="icon-[lucide--folder]" />` syntax from unpl
<script setup lang="ts"></script>
<template>
<i class="icon-[lucide--trophy] text-neutral size-4" />
<i class="icon-[lucide--settings] text-neutral size-4" />
<i class="text-neutral icon-[lucide--trophy] size-4" />
<i class="text-neutral icon-[lucide--settings] size-4" />
</template>
```

View File

@@ -1,24 +0,0 @@
import path from 'node:path'
export default {
'tests-ui/**': () =>
'echo "Files in tests-ui/ are deprecated. Colocate tests with source files." && exit 1',
'./**/*.js': (stagedFiles) => formatAndEslint(stagedFiles),
'./**/*.{ts,tsx,vue,mts}': (stagedFiles) => [
...formatAndEslint(stagedFiles),
'pnpm typecheck'
]
}
function formatAndEslint(fileNames) {
// Convert absolute paths to relative paths for better ESLint resolution
const relativePaths = fileNames.map((f) => path.relative(process.cwd(), f))
const joinedPaths = relativePaths.map((p) => `"${p}"`).join(' ')
return [
`pnpm exec oxfmt ${joinedPaths}`,
`pnpm exec oxlint --fix ${joinedPaths}`,
`pnpm exec eslint --cache --fix --no-warn-ignored ${joinedPaths}`
]
}

View File

@@ -1,6 +1,9 @@
import path from 'node:path'
export default {
'tests-ui/**': () =>
'echo "Files in tests-ui/ are deprecated. Colocate tests with source files." && exit 1',
'./**/*.js': (stagedFiles: string[]) => formatAndEslint(stagedFiles),
'./**/*.{ts,tsx,vue,mts}': (stagedFiles: string[]) => [
@@ -14,7 +17,7 @@ function formatAndEslint(fileNames: string[]) {
const relativePaths = fileNames.map((f) => path.relative(process.cwd(), f))
const joinedPaths = relativePaths.map((p) => `"${p}"`).join(' ')
return [
`pnpm exec prettier --cache --write ${joinedPaths}`,
`pnpm exec oxfmt ${joinedPaths}`,
`pnpm exec oxlint --fix ${joinedPaths}`,
`pnpm exec eslint --cache --fix --no-warn-ignored ${joinedPaths}`
]

View File

@@ -1,6 +1,6 @@
<template>
<div
class="w-full h-full absolute top-0 left-0 z-999 pointer-events-none flex flex-col"
class="pointer-events-none absolute top-0 left-0 z-999 flex h-full w-full flex-col"
>
<slot name="workflow-tabs" />
@@ -17,7 +17,7 @@
<Splitter
:key="splitterRefreshKey"
class="bg-transparent pointer-events-none border-none flex-1 overflow-hidden"
class="pointer-events-none flex-1 overflow-hidden border-none bg-transparent"
:state-key="sidebarStateKey"
state-storage="local"
@resizestart="onResizestart"
@@ -30,10 +30,10 @@
:class="
sidebarLocation === 'left'
? cn(
'side-bar-panel bg-comfy-menu-bg pointer-events-auto',
sidebarPanelVisible && 'min-w-78'
)
: 'bg-comfy-menu-bg pointer-events-auto'
'side-bar-panel pointer-events-auto bg-comfy-menu-bg',
sidebarPanelVisible && 'min-w-78'
)
: 'pointer-events-auto bg-comfy-menu-bg'
"
:min-size="sidebarLocation === 'left' ? 10 : 15"
:size="20"
@@ -54,11 +54,17 @@
</SplitterPanel>
<!-- Main panel (always present) -->
<SplitterPanel :size="80" class="flex flex-col">
<slot name="topmenu" :sidebar-panel-visible />
<SplitterPanel
:size="80"
class="flex flex-col"
>
<slot
name="topmenu"
:sidebar-panel-visible
/>
<Splitter
class="bg-transparent pointer-events-none border-none splitter-overlay-bottom mr-1 mb-1 ml-1 flex-1"
class="splitter-overlay-bottom pointer-events-none mr-1 mb-1 ml-1 flex-1 border-none bg-transparent"
layout="vertical"
:pt:gutter="
cn(
@@ -75,7 +81,7 @@
</SplitterPanel>
<SplitterPanel
v-show="bottomPanelVisible && !focusMode"
class="bottom-panel border border-(--p-panel-border-color) max-w-full overflow-x-auto bg-comfy-menu-bg pointer-events-auto rounded-lg"
class="bottom-panel pointer-events-auto max-w-full overflow-x-auto rounded-lg border border-(--p-panel-border-color) bg-comfy-menu-bg"
>
<slot name="bottom-panel" />
</SplitterPanel>
@@ -90,10 +96,10 @@
:class="
sidebarLocation === 'right'
? cn(
'side-bar-panel bg-comfy-menu-bg pointer-events-auto',
sidebarPanelVisible && 'min-w-78'
)
: 'bg-comfy-menu-bg pointer-events-auto'
'side-bar-panel pointer-events-auto bg-comfy-menu-bg',
sidebarPanelVisible && 'min-w-78'
)
: 'pointer-events-auto bg-comfy-menu-bg'
"
:min-size="sidebarLocation === 'right' ? 10 : 15"
:size="20"
@@ -103,7 +109,10 @@
sidebarLocation === 'right' ? t('sideToolbar.sidebar') : undefined
"
>
<slot v-if="sidebarLocation === 'left'" name="right-side-panel" />
<slot
v-if="sidebarLocation === 'left'"
name="right-side-panel"
/>
<slot
v-else-if="sidebarLocation === 'right' && sidebarPanelVisible"
name="side-bar-panel"

View File

@@ -1,7 +1,7 @@
<template>
<div
v-show="workspaceState.focusMode"
class="fixed z-9999 flex flex-row no-drag top-0 right-0"
class="no-drag fixed top-0 right-0 z-9999 flex flex-row"
>
<Button
v-tooltip="{ value: $t('menu.showMenu'), showDelay: 300 }"

View File

@@ -32,14 +32,14 @@
</div>
<div
class="actionbar-container pointer-events-auto flex gap-2 h-12 items-center rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
class="actionbar-container pointer-events-auto flex h-12 items-center gap-2 rounded-lg border border-interface-stroke bg-comfy-menu-bg px-2 shadow-interface"
>
<ActionBarButtons />
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
<div
ref="legacyCommandsContainerRef"
class="[&:not(:has(*>*:not(:empty)))]:hidden"
></div>
/>
<ComfyActionbar />
<Button
v-tooltip.bottom="queueHistoryTooltipConfig"
@@ -54,7 +54,7 @@
<i class="icon-[lucide--history] size-4" />
<span
v-if="queuedCount > 0"
class="absolute -top-1 -right-1 min-w-[16px] rounded-full bg-primary-background py-0.25 text-[10px] font-medium leading-[14px] text-base-foreground"
class="absolute -top-1 -right-1 min-w-[16px] rounded-full bg-primary-background py-0.25 text-[10px] leading-[14px] font-medium text-base-foreground"
>
{{ queuedCount }}
</span>

View File

@@ -1,5 +1,8 @@
<template>
<div class="flex h-full items-center" :class="cn(!isDocked && '-ml-2')">
<div
class="flex h-full items-center"
:class="cn(!isDocked && '-ml-2')"
>
<div
v-if="isDragging && !isDocked"
:class="actionbarClass"
@@ -18,12 +21,15 @@
content: { class: isDocked ? 'p-0' : 'p-1' }
}"
>
<div ref="panelRef" class="flex items-center select-none gap-2">
<div
ref="panelRef"
class="flex items-center gap-2 select-none"
>
<span
ref="dragHandleRef"
:class="
cn(
'drag-handle cursor-grab w-3 h-max',
'drag-handle h-max w-3 cursor-grab',
isDragging && 'cursor-grabbing'
)
"
@@ -282,18 +288,18 @@ const actionbarClass = computed(() =>
cn(
'w-[200px] border-dashed border-blue-500 opacity-80',
'm-1.5 flex items-center justify-center self-stretch',
'rounded-md before:w-50 before:-ml-50 before:h-full',
'rounded-md before:-ml-50 before:h-full before:w-50',
'pointer-events-auto',
isMouseOverDropZone.value &&
'border-[3px] opacity-100 scale-105 shadow-[0_0_20px] shadow-blue-500'
'scale-105 border-[3px] opacity-100 shadow-[0_0_20px] shadow-blue-500'
)
)
const panelClass = computed(() =>
cn(
'actionbar pointer-events-auto z-1300',
isDragging.value && 'select-none pointer-events-none',
isDragging.value && 'pointer-events-none select-none',
isDocked.value
? 'p-0 static border-none bg-transparent'
? 'static border-none bg-transparent p-0'
: 'fixed shadow-interface'
)
)

View File

@@ -4,7 +4,10 @@
class="relative h-full w-full overflow-hidden bg-neutral-900"
>
<div class="p-terminal h-full w-full rounded-none p-2">
<div ref="terminalEl" class="terminal-host h-full" />
<div
ref="terminalEl"
class="terminal-host h-full"
/>
</div>
<Button
v-tooltip.left="{
@@ -15,7 +18,7 @@
size="sm"
:class="
cn('absolute top-2 right-8 transition-opacity', {
'opacity-0 pointer-events-none select-none': !isHovered
'pointer-events-none opacity-0 select-none': !isHovered
})
"
:aria-label="tooltipText"
@@ -109,6 +112,6 @@ onUnmounted(() => {
}
:deep(.p-terminal) .xterm-screen {
@apply bg-neutral-900 overflow-hidden;
@apply overflow-hidden bg-neutral-900;
}
</style>

View File

@@ -46,9 +46,9 @@
:is-active="item.key === activeItemKey"
/>
</template>
<template #separator
><span style="transform: scale(1.5)"> / </span></template
>
<template #separator>
<span style="transform: scale(1.5)"> / </span>
</template>
</Breadcrumb>
</div>
</template>
@@ -233,7 +233,7 @@ onUpdated(() => {
}
:deep(.p-breadcrumb-item) {
@apply flex items-center overflow-hidden h-8;
@apply flex h-8 items-center overflow-hidden;
min-width: calc(var(--p-breadcrumb-item-min-width) + 1rem);
border: 1px solid transparent;
background-color: transparent;

View File

@@ -21,8 +21,15 @@
class="icon-[lucide--triangle-alert] text-warning-background"
/>
<span class="p-breadcrumb-item-label px-2">{{ item.label }}</span>
<Tag v-if="item.isBlueprint" value="Blueprint" severity="primary" />
<i v-if="isActive" class="pi pi-angle-down text-[10px]"></i>
<Tag
v-if="item.isBlueprint"
value="Blueprint"
severity="primary"
/>
<i
v-if="isActive"
class="pi pi-angle-down text-[10px]"
/>
</a>
<Menu
v-if="isActive || isRoot"
@@ -208,7 +215,7 @@ defineExpose({
}
.p-breadcrumb-item-label {
@apply whitespace-nowrap text-ellipsis overflow-hidden;
@apply overflow-hidden text-ellipsis whitespace-nowrap;
}
.active-breadcrumb-item {

View File

@@ -2,12 +2,12 @@
<div
:class="
cn(
'flex justify-center items-center shrink-0 outline-hidden border-none p-0 rounded-lg shadow-sm transition-all duration-200 cursor-pointer bg-secondary-background',
'flex shrink-0 cursor-pointer items-center justify-center rounded-lg border-none bg-secondary-background p-0 shadow-sm outline-hidden transition-all duration-200',
backgroundClass
)
"
>
<slot></slot>
<slot />
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div :class="containerClasses">
<slot></slot>
<slot />
</div>
</template>
@@ -14,6 +14,6 @@ const { fullHeight = true } = defineProps<{
}>()
const containerClasses = computed(() =>
cn('flex-1 w-full', fullHeight && 'h-full')
cn('w-full flex-1', fullHeight && 'h-full')
)
</script>

View File

@@ -1,7 +1,7 @@
<template>
<div :class="containerClasses">
<slot name="top"></slot>
<slot name="bottom"></slot>
<slot name="top" />
<slot name="bottom" />
</div>
</template>
@@ -59,7 +59,7 @@ const containerClasses = computed(() => {
outline: cn(
hasBorder && 'border-2 border-border-subtle',
hasCursor && 'cursor-pointer',
'hover:border-border-subtle/50 transition-colors'
'transition-colors hover:border-border-subtle/50'
)
}

View File

@@ -1,6 +1,6 @@
<template>
<div :class="chipClasses">
<slot name="icon"></slot>
<slot name="icon" />
<span>{{ label }}</span>
</div>
</template>
@@ -19,9 +19,9 @@ const baseClasses =
const variantStyles = {
dark: 'bg-zinc-500/40 text-white/90',
light: cn('backdrop-blur-[2px] bg-base-background/50 text-base-foreground'),
light: cn('bg-base-background/50 text-base-foreground backdrop-blur-[2px]'),
gray: cn(
'backdrop-blur-[2px] bg-modal-card-tag-background text-base-foreground'
'bg-modal-card-tag-background text-base-foreground backdrop-blur-[2px]'
)
}

View File

@@ -1,11 +1,14 @@
<template>
<span class="relative inline-flex items-center justify-center size-[1em]">
<i :class="mainIcon" class="text-[1em]" />
<span class="relative inline-flex size-[1em] items-center justify-center">
<i
:class="mainIcon"
class="text-[1em]"
/>
<i
:class="
cn(
subIcon,
'absolute leading-none pointer-events-none',
'pointer-events-none absolute leading-none',
positionX === 'left' ? 'left-0' : 'right-0',
positionY === 'top' ? 'top-0' : 'bottom-0'
)

View File

@@ -2,7 +2,7 @@
<div
:class="
cn(
'relative flex w-full items-center gap-2 bg-comfy-input cursor-text text-comfy-input-foreground',
'relative flex w-full cursor-text items-center gap-2 bg-comfy-input text-comfy-input-foreground',
customClass,
wrapperStyle
)
@@ -14,19 +14,22 @@
:placeholder
:autofocus
unstyled
class="absolute inset-0 size-full pl-11 border-none outline-none bg-transparent text-sm"
class="absolute inset-0 size-full border-none bg-transparent pl-11 text-sm outline-none"
:aria-label="placeholder"
/>
<Button
v-if="filterIcon"
size="icon"
variant="textonly"
class="filter-button absolute right-0 inset-y-0 m-0 p-0"
class="filter-button absolute inset-y-0 right-0 m-0 p-0"
@click="$emit('showFilter', $event)"
>
<i :class="filterIcon" />
</Button>
<InputIcon v-if="!modelValue" :class="icon" />
<InputIcon
v-if="!modelValue"
:class="icon"
/>
<Button
v-if="modelValue"
class="clear-button absolute left-0"
@@ -37,7 +40,10 @@
<i class="icon-[lucide--x] size-4" />
</Button>
</div>
<div v-if="filters?.length" class="search-filters flex flex-wrap gap-2 pt-2">
<div
v-if="filters?.length"
class="search-filters flex flex-wrap gap-2 pt-2"
>
<SearchFilterChip
v-for="filter in filters"
:key="filter.id"
@@ -109,7 +115,7 @@ watchDebounced(
const wrapperStyle = computed(() => {
if (showBorder) {
return cn('rounded p-2 border border-solid border-border-default')
return cn('rounded border border-solid border-border-default p-2')
}
// Size-specific classes matching button sizes for consistency

View File

@@ -2,7 +2,7 @@
<Tree
v-model:expanded-keys="expandedKeys"
v-model:selection-keys="selectionKeys"
class="tree-explorer px-2 py-0 2xl:px-4 bg-transparent"
class="tree-explorer bg-transparent px-2 py-0 2xl:px-4"
:class="props.class"
:value="renderedRoot.children"
selection-mode="single"
@@ -23,17 +23,26 @@
}"
>
<template #folder="{ node }">
<slot name="folder" :node="node">
<slot
name="folder"
:node="node"
>
<TreeExplorerTreeNode :node="node" />
</slot>
</template>
<template #node="{ node }">
<slot name="node" :node="node">
<slot
name="node"
:node="node"
>
<TreeExplorerTreeNode :node="node" />
</slot>
</template>
</Tree>
<ContextMenu ref="menu" :model="menuItems" />
<ContextMenu
ref="menu"
:model="menuItems"
/>
</template>
<script setup lang="ts">
import ContextMenu from 'primevue/contextmenu'

View File

@@ -12,13 +12,19 @@
>
<div class="node-content">
<span class="node-label">
<slot name="before-label" :node="props.node" />
<slot
name="before-label"
:node="props.node"
/>
<EditableText
:model-value="node.label"
:is-editing="isEditing"
@edit="handleRename"
/>
<slot name="after-label" :node="props.node" />
<slot
name="after-label"
:node="props.node"
/>
</span>
<Badge
v-if="showNodeBadgeText"
@@ -28,9 +34,12 @@
/>
</div>
<div
class="node-actions touch:opacity-100 motion-safe:opacity-0 motion-safe:group-hover/tree-node:opacity-100"
class="node-actions motion-safe:opacity-0 motion-safe:group-hover/tree-node:opacity-100 touch:opacity-100"
>
<slot name="actions" :node="props.node" />
<slot
name="actions"
:node="props.node"
/>
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div
class="flex flex-col px-4 py-2 text-sm text-muted-foreground border-t border-border-default"
class="flex flex-col border-t border-border-default px-4 py-2 text-sm text-muted-foreground"
>
<p v-if="promptTextReal">
{{ promptTextReal }}

View File

@@ -1,6 +1,11 @@
<template>
<section class="w-full flex gap-2 justify-end px-2 pb-2">
<Button :disabled variant="textonly" autofocus @click="$emit('cancel')">
<section class="flex w-full justify-end gap-2 px-2 pb-2">
<Button
:disabled
variant="textonly"
autofocus
@click="$emit('cancel')"
>
{{ cancelTextX }}
</Button>
<Button

View File

@@ -1,8 +1,11 @@
<template>
<div
class="flex items-center gap-2 p-4 font-bold text-sm text-base-foreground font-inter"
class="flex items-center gap-2 p-4 font-inter text-sm font-bold text-base-foreground"
>
<span v-if="title" class="flex-auto">{{ title }}</span>
<span
v-if="title"
class="flex-auto"
>{{ title }}</span>
</div>
</template>
<script setup lang="ts">

View File

@@ -14,21 +14,27 @@
}}
</p>
</div>
<MissingCoreNodesMessage v-if="!isCloud" :missing-core-nodes />
<MissingCoreNodesMessage
v-if="!isCloud"
:missing-core-nodes
/>
<!-- Missing Nodes List Wrapper -->
<div
class="comfy-missing-nodes flex flex-col max-h-[256px] rounded-lg py-2 scrollbar-custom bg-secondary-background"
class="comfy-missing-nodes flex scrollbar-custom max-h-[256px] flex-col rounded-lg bg-secondary-background py-2"
>
<div
v-for="(node, i) in uniqueNodes"
:key="i"
class="flex min-h-8 items-center justify-between px-4 py-2 bg-secondary-background text-muted-foreground"
class="flex min-h-8 items-center justify-between bg-secondary-background px-4 py-2 text-muted-foreground"
>
<span class="text-xs">
{{ node.label }}
</span>
<span v-if="node.hint" class="text-xs">{{ node.hint }}</span>
<span
v-if="node.hint"
class="text-xs"
>{{ node.hint }}</span>
</div>
</div>

View File

@@ -2,7 +2,7 @@
<!-- Cloud mode: Learn More + Got It buttons -->
<div
v-if="isCloud"
class="flex w-full items-center justify-between gap-2 py-2 px-4"
class="flex w-full items-center justify-between gap-2 px-4 py-2"
>
<Button
variant="textonly"
@@ -12,19 +12,33 @@
target="_blank"
rel="noopener noreferrer"
>
<i class="icon-[lucide--info]"></i>
<i class="icon-[lucide--info]" />
<span>{{ $t('missingNodes.cloud.learnMore') }}</span>
</Button>
<Button variant="secondary" size="md" @click="handleGotItClick">{{
$t('missingNodes.cloud.gotIt')
}}</Button>
<Button
variant="secondary"
size="md"
@click="handleGotItClick"
>
{{
$t('missingNodes.cloud.gotIt')
}}
</Button>
</div>
<!-- OSS mode: Open Manager + Install All buttons -->
<div v-else-if="showManagerButtons" class="flex justify-end gap-1 py-2 px-4">
<Button variant="textonly" @click="openManager">{{
$t('g.openManager')
}}</Button>
<div
v-else-if="showManagerButtons"
class="flex justify-end gap-1 px-4 py-2"
>
<Button
variant="textonly"
@click="openManager"
>
{{
$t('g.openManager')
}}
</Button>
<PackInstallButton
v-if="showInstallAllButton"
type="secondary"

View File

@@ -3,8 +3,8 @@
class="flex min-w-[460px] flex-col rounded-2xl border border-border-default bg-base-background shadow-[1px_1px_8px_0_rgba(0,0,0,0.4)]"
>
<!-- Header -->
<div class="flex py-8 items-center justify-between px-8">
<h2 class="text-lg font-bold text-base-foreground m-0">
<div class="flex items-center justify-between px-8 py-8">
<h2 class="m-0 text-lg font-bold text-base-foreground">
{{
isInsufficientCredits
? $t('credits.topUp.addMoreCreditsToRun')
@@ -12,7 +12,7 @@
}}
</h2>
<button
class="cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-secondary-foreground"
class="focus-visible:ring-secondary-foreground cursor-pointer rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:text-base-foreground focus-visible:ring-1 focus-visible:outline-none"
@click="() => handleClose()"
>
<i class="icon-[lucide--x] size-6" />
@@ -20,7 +20,7 @@
</div>
<p
v-if="isInsufficientCredits"
class="text-sm text-muted-foreground m-0 px-8"
class="m-0 px-8 text-sm text-muted-foreground"
>
{{ $t('credits.topUp.insufficientWorkflowMessage') }}
</p>
@@ -39,7 +39,7 @@
size="lg"
:class="
cn(
'h-10 text-base font-medium w-full focus-visible:ring-secondary-foreground',
'focus-visible:ring-secondary-foreground h-10 w-full text-base font-medium',
selectedPreset === amount && 'bg-secondary-background-selected'
)
"
@@ -65,9 +65,7 @@
@max-reached="showCeilingWarning = true"
>
<template #prefix>
<span class="shrink-0 text-base font-semibold text-base-foreground"
>$</span
>
<span class="shrink-0 text-base font-semibold text-base-foreground">$</span>
</template>
</FormattedNumberStepper>
</div>
@@ -95,7 +93,7 @@
<p
v-if="isBelowMin"
class="text-sm text-red-500 m-0 px-8 pt-4 text-center flex items-center justify-center gap-1"
class="m-0 flex items-center justify-center gap-1 px-8 pt-4 text-center text-sm text-red-500"
>
<i class="icon-[lucide--component] size-4" />
{{
@@ -106,7 +104,7 @@
</p>
<p
v-if="showCeilingWarning"
class="text-sm text-gold-500 m-0 px-8 pt-4 text-center flex items-center justify-center gap-1"
class="m-0 flex items-center justify-center gap-1 px-8 pt-4 text-center text-sm text-gold-500"
>
<i class="icon-[lucide--component] size-4" />
{{
@@ -119,11 +117,10 @@
href="https://www.comfy.org/cloud/enterprise"
target="_blank"
class="ml-1 text-inherit"
>{{ $t('credits.topUp.contactUs') }}</a
>
>{{ $t('credits.topUp.contactUs') }}</a>
</p>
<div class="pt-8 pb-8 flex flex-col gap-8 px-8">
<div class="flex flex-col gap-8 px-8 pt-8 pb-8">
<Button
:disabled="!isValidAmount || loading"
:loading="loading"

View File

@@ -1,10 +1,10 @@
<template>
<div
class="flex items-center justify-between p-2 rounded-lg cursor-pointer transition-all duration-200"
class="flex cursor-pointer items-center justify-between rounded-lg p-2 transition-all duration-200"
:class="[
selected
? 'bg-secondary-background border-2 border-border-default'
: 'bg-component-node-disabled hover:bg-secondary-background border-2 border-transparent'
? 'border-2 border-border-default bg-secondary-background'
: 'bg-component-node-disabled border-2 border-transparent hover:bg-secondary-background'
]"
@click="$emit('select')"
>

View File

@@ -7,7 +7,10 @@
>
<!-- Email Field -->
<div class="flex flex-col gap-2">
<label class="mb-2 text-base font-medium opacity-80" :for="emailInputId">
<label
class="mb-2 text-base font-medium opacity-80"
:for="emailInputId"
>
{{ t('auth.login.emailLabel') }}
</label>
<InputText
@@ -19,7 +22,10 @@
:placeholder="t('auth.login.emailPlaceholder')"
:invalid="$form.email?.invalid"
/>
<small v-if="$form.email?.invalid" class="text-red-500">{{
<small
v-if="$form.email?.invalid"
class="text-red-500"
>{{
$form.email.error.message
}}</small>
</div>
@@ -54,13 +60,19 @@
fluid
class="h-10"
/>
<small v-if="$form.password?.invalid" class="text-red-500">{{
<small
v-if="$form.password?.invalid"
class="text-red-500"
>{{
$form.password.error.message
}}</small>
</div>
<!-- Submit Button -->
<ProgressSpinner v-if="loading" class="mx-auto h-8 w-8" />
<ProgressSpinner
v-if="loading"
class="mx-auto h-8 w-8"
/>
<Button
v-else
type="submit"
@@ -131,6 +143,6 @@ const handleForgotPassword = async (
@reference '../../../../assets/css/style.css';
.text-link-disabled {
@apply opacity-50 cursor-not-allowed;
@apply cursor-not-allowed opacity-50;
}
</style>

View File

@@ -2,7 +2,7 @@
<ContextMenu
ref="contextMenu"
:model="menuItems"
class="max-h-[80vh] md:max-h-none overflow-y-auto md:overflow-y-visible"
class="max-h-[80vh] overflow-y-auto md:max-h-none md:overflow-y-visible"
@show="onMenuShow"
@hide="onMenuHide"
>
@@ -12,7 +12,10 @@
class="flex items-center gap-2 px-3 py-1.5"
@click="item.isColorSubmenu ? showColorPopover($event) : undefined"
>
<i v-if="item.icon" :class="[item.icon, 'size-4']" />
<i
v-if="item.icon"
:class="[item.icon, 'size-4']"
/>
<span class="flex-1">{{ item.label }}</span>
<span
v-if="item.shortcut"

View File

@@ -11,7 +11,10 @@
@click="() => (showColorPicker = !showColorPicker)"
>
<div class="flex items-center gap-1 px-0">
<i class="pi pi-circle-fill" :style="{ color: currentColor ?? '' }" />
<i
class="pi pi-circle-fill"
:style="{ color: currentColor ?? '' }"
/>
<i class="icon-[lucide--chevron-down]" />
</div>
</Button>
@@ -166,6 +169,6 @@ watch(
}
:deep(.p-togglebutton) {
@apply py-2 px-1;
@apply px-1 py-2;
}
</style>

View File

@@ -23,7 +23,7 @@
:class="
isColorSubmenu
? 'flex flex-col gap-1 p-2'
: 'flex flex-col p-2 min-w-40'
: 'flex min-w-40 flex-col p-2'
"
>
<div
@@ -31,12 +31,12 @@
:key="subOption.label"
:class="
cn(
'hover:bg-secondary-background-hover rounded cursor-pointer',
'cursor-pointer rounded hover:bg-secondary-background-hover',
isColorSubmenu
? 'w-7 h-7 flex items-center justify-center'
? 'flex h-7 w-7 items-center justify-center'
: 'flex items-center gap-2 px-3 py-1.5 text-sm',
subOption.disabled
? 'cursor-not-allowed pointer-events-none text-node-icon-disabled'
? 'pointer-events-none cursor-not-allowed text-node-icon-disabled'
: 'hover:bg-secondary-background-hover'
)
"
@@ -53,7 +53,10 @@
v-if="isShapeSelected(subOption)"
class="icon-[lucide--check] size-4 flex-shrink-0"
/>
<div v-else class="w-4 flex-shrink-0" />
<div
v-else
class="w-4 flex-shrink-0"
/>
<span>{{ subOption.label }}</span>
</template>
</div>

View File

@@ -6,7 +6,10 @@
>
<!-- Main Menu Items -->
<div class="w-full">
<nav class="flex w-full flex-col gap-2" role="menubar">
<nav
class="flex w-full flex-col gap-2"
role="menubar"
>
<button
v-for="menuItem in menuItems"
v-show="menuItem.visible !== false"
@@ -26,14 +29,20 @@
v-if="typeof menuItem.icon === 'object'"
:size="16"
/>
<i v-else :class="menuItem.icon" />
<i
v-else
:class="menuItem.icon"
/>
</div>
<div v-if="menuItem.showRedDot" class="menu-red-dot" />
<div
v-if="menuItem.showRedDot"
class="menu-red-dot"
/>
</div>
<span class="menu-label">{{ menuItem.label }}</span>
<i
v-if="menuItem.showExternalIcon"
class="icon-[lucide--external-link] text-primary w-4 h-4 ml-auto"
class="ml-auto icon-[lucide--external-link] h-4 w-4 text-primary"
/>
<i
v-if="menuItem.key === 'more'"
@@ -109,7 +118,10 @@
@keydown.enter="onReleaseClick(release)"
@keydown.space.prevent="onReleaseClick(release)"
>
<i class="help-menu-icon icon-[lucide--package]" aria-hidden="true" />
<i
class="help-menu-icon icon-[lucide--package]"
aria-hidden="true"
/>
<div class="release-content">
<span class="release-title">
{{
@@ -119,7 +131,10 @@
})
}}
</span>
<time class="release-date" :datetime="release.published_at">
<time
class="release-date"
:datetime="release.published_at"
>
<span class="normal-state">
{{ formatReleaseDate(release.published_at) }}
</span>
@@ -138,13 +153,23 @@
role="status"
aria-live="polite"
>
<i class="pi pi-spin pi-spinner help-menu-icon" aria-hidden="true" />
<i
class="pi pi-spin pi-spinner help-menu-icon"
aria-hidden="true"
/>
<span>{{ $t('helpCenter.loadingReleases') }}</span>
</div>
<!-- No Releases State -->
<div v-else class="help-menu-item" role="status">
<i class="pi pi-info-circle help-menu-icon" aria-hidden="true" />
<div
v-else
class="help-menu-item"
role="status"
>
<i
class="pi pi-info-circle help-menu-icon"
aria-hidden="true"
/>
<span>{{ $t('helpCenter.noRecentReleases') }}</span>
</div>
</section>

View File

@@ -10,7 +10,10 @@
ref="containerEl"
class="relative min-h-0 flex-1 overflow-hidden rounded-[5px] bg-node-component-surface"
>
<div v-if="isLoading" class="flex size-full items-center justify-center">
<div
v-if="isLoading"
class="flex size-full items-center justify-center"
>
<span class="text-sm">{{ $t('imageCrop.loading') }}</span>
</div>
@@ -19,7 +22,9 @@
class="flex size-full flex-col items-center justify-center text-center"
>
<i class="mb-2 icon-[lucide--image] h-12 w-12" />
<p class="text-sm">{{ $t('imageCrop.noInputImage') }}</p>
<p class="text-sm">
{{ $t('imageCrop.noInputImage') }}
</p>
</div>
<img
@@ -28,11 +33,11 @@
:src="imageUrl"
:alt="$t('imageCrop.cropPreviewAlt')"
draggable="false"
class="block size-full object-contain select-none brightness-50"
class="block size-full object-contain brightness-50 select-none"
@load="handleImageLoad"
@error="handleImageError"
@dragstart.prevent
/>
>
<div
v-if="imageUrl && !isLoading"
@@ -42,7 +47,10 @@
@pointermove="handleDragMove"
@pointerup="handleDragEnd"
>
<div class="pointer-events-none size-full" :style="cropImageStyle" />
<div
class="pointer-events-none size-full"
:style="cropImageStyle"
/>
</div>
<div
@@ -57,7 +65,10 @@
/>
</div>
<WidgetBoundingBox v-model="modelValue" class="shrink-0" />
<WidgetBoundingBox
v-model="modelValue"
class="shrink-0"
/>
</div>
</template>

View File

@@ -121,7 +121,7 @@
{{ $t('g.clearAll') }}
</Button>
</div>
<div class="my-4 h-px bg-border-default"></div>
<div class="my-4 h-px bg-border-default" />
</div>
</template>
@@ -147,7 +147,7 @@
<template #option="slotProps">
<div
role="button"
class="flex items-center gap-2 cursor-pointer"
class="flex cursor-pointer items-center gap-2"
:style="popoverStyle"
>
<div

View File

@@ -1,7 +1,7 @@
<template>
<div
ref="container"
class="relative h-full w-full min-h-[200px]"
class="relative h-full min-h-[200px] w-full"
data-capture-wheel="true"
@pointerdown.stop
@pointermove.stop
@@ -14,7 +14,10 @@
@dragleave.stop="handleDragLeave"
@drop.prevent.stop="handleDrop"
>
<LoadingOverlay :loading="loading" :loading-message="loadingMessage" />
<LoadingOverlay
:loading="loading"
:loading-message="loadingMessage"
/>
<div
v-if="!isPreview && isDragging"
class="pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"

View File

@@ -1,5 +1,5 @@
<template>
<div class="relative show-slider">
<div class="show-slider relative">
<Button
v-tooltip.right="{ value: tooltipText, showDelay: 300 }"
size="icon"
@@ -12,7 +12,7 @@
</Button>
<div
v-show="showSlider"
class="absolute top-0 left-12 rounded-lg bg-interface-menu-surface p-4 shadow-lg w-[150px]"
class="absolute top-0 left-12 w-[150px] rounded-lg bg-interface-menu-surface p-4 shadow-lg"
>
<Slider
v-model="value"

View File

@@ -13,7 +13,7 @@
:class="
cn(
'rounded-full',
isRecording && 'text-red-500 recording-button-blink'
isRecording && 'recording-button-blink text-red-500'
)
"
:aria-label="

View File

@@ -1,75 +1,78 @@
<template>
<div class="flex flex-col gap-3 pb-3">
<h3 class="text-center text-[15px] font-sans text-descrip-text mt-2.5">
<h3 class="text-descrip-text mt-2.5 text-center font-sans text-[15px]">
{{ t('maskEditor.brushSettings') }}
</h3>
<button :class="textButtonClass" @click="resetToDefault">
<button
:class="textButtonClass"
@click="resetToDefault"
>
{{ t('maskEditor.resetToDefault') }}
</button>
<!-- Brush Shape -->
<div class="flex flex-col gap-3 pb-3">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.brushShape') }}
</span>
<div
class="flex flex-row gap-2.5 items-center h-[50px] w-full rounded-[10px] bg-secondary-background-hover"
class="flex h-[50px] w-full flex-row items-center gap-2.5 rounded-[10px] bg-secondary-background-hover"
>
<div
class="maskEditor_sidePanelBrushShapeCircle hover:bg-comfy-menu-bg"
:class="
cn(
store.brushSettings.type === BrushShape.Arc
? 'bg-[var(--p-button-text-primary-color)] active'
? 'active bg-[var(--p-button-text-primary-color)]'
: 'bg-transparent'
)
"
@click="setBrushShape(BrushShape.Arc)"
></div>
/>
<div
class="maskEditor_sidePanelBrushShapeSquare hover:bg-comfy-menu-bg"
:class="
cn(
store.brushSettings.type === BrushShape.Rect
? 'bg-[var(--p-button-text-primary-color)] active'
? 'active bg-[var(--p-button-text-primary-color)]'
: 'bg-transparent'
)
"
@click="setBrushShape(BrushShape.Rect)"
></div>
/>
</div>
</div>
<!-- Color -->
<div class="flex flex-col gap-3 pb-3">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.colorSelector') }}
</span>
<input
ref="colorInputRef"
v-model="store.rgbColor"
type="color"
class="h-10 rounded-md cursor-pointer"
/>
class="h-10 cursor-pointer rounded-md"
>
</div>
<!-- Thickness -->
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.thickness') }}
</span>
<input
v-model.number="brushSize"
type="number"
class="w-16 px-2 py-1 text-sm text-center border rounded-md bg-comfy-menu-bg border-p-form-field-border-color text-input-text"
class="border-p-form-field-border-color text-input-text w-16 rounded-md border bg-comfy-menu-bg px-2 py-1 text-center text-sm"
:min="1"
:max="250"
:step="1"
/>
>
</div>
<SliderControl
v-model="brushSize"
@@ -84,17 +87,17 @@
<!-- Opacity -->
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.opacity') }}
</span>
<input
v-model.number="brushOpacity"
type="number"
class="w-16 px-2 py-1 text-sm text-center border rounded-md bg-comfy-menu-bg border-p-form-field-border-color text-input-text"
class="border-p-form-field-border-color text-input-text w-16 rounded-md border bg-comfy-menu-bg px-2 py-1 text-center text-sm"
:min="0"
:max="1"
:step="0.01"
/>
>
</div>
<SliderControl
v-model="brushOpacity"
@@ -109,17 +112,17 @@
<!-- Hardness -->
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.hardness') }}
</span>
<input
v-model.number="brushHardness"
type="number"
class="w-16 px-2 py-1 text-sm text-center border rounded-md bg-comfy-menu-bg border-p-form-field-border-color text-input-text"
class="border-p-form-field-border-color text-input-text w-16 rounded-md border bg-comfy-menu-bg px-2 py-1 text-center text-sm"
:min="0"
:max="1"
:step="0.01"
/>
>
</div>
<SliderControl
v-model="brushHardness"
@@ -134,17 +137,17 @@
<!-- Step Size -->
<div class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<span class="text-left text-xs font-sans text-descrip-text">
<span class="text-descrip-text text-left font-sans text-xs">
{{ t('maskEditor.stepSize') }}
</span>
<input
v-model.number="brushStepSize"
type="number"
class="w-16 px-2 py-1 text-sm text-center border rounded-md bg-comfy-menu-bg border-p-form-field-border-color text-input-text"
class="border-p-form-field-border-color text-input-text w-16 rounded-md border bg-comfy-menu-bg px-2 py-1 text-center text-sm"
:min="1"
:max="100"
:step="1"
/>
>
</div>
<SliderControl
v-model="brushStepSize"

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-3 pb-3">
<h3
class="text-center text-[15px] font-sans text-[var(--descrip-text)] mt-2.5"
class="mt-2.5 text-center font-sans text-[15px] text-[var(--descrip-text)]"
>
{{ t('maskEditor.colorSelectSettings') }}
</h3>

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-3 pb-3">
<h3
class="text-center text-[15px] font-sans text-[var(--descrip-text)] mt-2.5"
class="mt-2.5 text-center font-sans text-[15px] text-[var(--descrip-text)]"
>
{{ t('maskEditor.layers') }}
</h3>
@@ -15,29 +15,35 @@
@update:model-value="onMaskOpacityChange"
/>
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
t('maskEditor.maskBlendingOptions')
}}</span>
<div
class="flex flex-row gap-2.5 items-center min-h-6 relative h-[50px] w-full rounded-[10px] -mt-2 -mb-1.5"
class="relative -mt-2 -mb-1.5 flex h-[50px] min-h-6 w-full flex-row items-center gap-2.5 rounded-[10px]"
>
<select
class="maskEditor_sidePanelDropdown"
:value="store.maskBlendMode"
@change="onBlendModeChange"
>
<option value="black">{{ t('maskEditor.black') }}</option>
<option value="white">{{ t('maskEditor.white') }}</option>
<option value="negative">{{ t('maskEditor.negative') }}</option>
<option value="black">
{{ t('maskEditor.black') }}
</option>
<option value="white">
{{ t('maskEditor.white') }}
</option>
<option value="negative">
{{ t('maskEditor.negative') }}
</option>
</select>
</div>
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
t('maskEditor.maskLayer')
}}</span>
<div
class="flex flex-row gap-2.5 items-center min-h-6 relative h-[50px] w-full rounded-[10px] bg-secondary-background-hover"
class="relative flex h-[50px] min-h-6 w-full flex-row items-center gap-2.5 rounded-[10px] bg-secondary-background-hover"
:style="{
border: store.activeLayer === 'mask' ? '2px solid #007acc' : 'none'
}"
@@ -47,9 +53,12 @@
class="maskEditor_sidePanelLayerCheckbox"
:checked="maskLayerVisible"
@change="onMaskLayerVisibilityChange"
/>
>
<div class="maskEditor_sidePanelLayerPreviewContainer">
<svg viewBox="0 0 20 20" style="">
<svg
viewBox="0 0 20 20"
style=""
>
<path
class="cls-1"
d="M1.31,5.32v9.36c0,.55.45,1,1,1h15.38c.55,0,1-.45,1-1V5.32c0-.55-.45-1-1-1H2.31c-.55,0-1,.45-1,1ZM11.19,13.44c-2.91.94-5.57-1.72-4.63-4.63.34-1.05,1.19-1.9,2.24-2.24,2.91-.94,5.57,1.72,4.63,4.63-.34,1.05-1.19-1.9-2.24,2.24Z"
@@ -66,11 +75,11 @@
</button>
</div>
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
t('maskEditor.paintLayer')
}}</span>
<div
class="flex flex-row gap-2.5 items-center min-h-6 relative h-[50px] w-full rounded-[10px] bg-secondary-background-hover"
class="relative flex h-[50px] min-h-6 w-full flex-row items-center gap-2.5 rounded-[10px] bg-secondary-background-hover"
:style="{
border: store.activeLayer === 'rgb' ? '2px solid #007acc' : 'none'
}"
@@ -80,7 +89,7 @@
class="maskEditor_sidePanelLayerCheckbox"
:checked="paintLayerVisible"
@change="onPaintLayerVisibilityChange"
/>
>
<div class="maskEditor_sidePanelLayerPreviewContainer">
<svg viewBox="0 0 20 20">
<path
@@ -106,24 +115,24 @@
</button>
</div>
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
t('maskEditor.baseImageLayer')
}}</span>
<div
class="flex flex-row gap-2.5 items-center min-h-6 relative h-[50px] w-full rounded-[10px] bg-secondary-background-hover"
class="relative flex h-[50px] min-h-6 w-full flex-row items-center gap-2.5 rounded-[10px] bg-secondary-background-hover"
>
<input
type="checkbox"
class="maskEditor_sidePanelLayerCheckbox"
:checked="baseImageLayerVisible"
@change="onBaseImageLayerVisibilityChange"
/>
>
<div class="maskEditor_sidePanelLayerPreviewContainer">
<img
class="maskEditor_sidePanelImageLayerImage"
:src="baseImageSrc"
:alt="t('maskEditor.baseLayerPreview')"
/>
>
</div>
</div>
</div>

View File

@@ -13,29 +13,32 @@
>
<canvas
ref="imgCanvasRef"
class="absolute top-0 left-0 w-full h-full z-0"
class="absolute top-0 left-0 z-0 h-full w-full"
@contextmenu.prevent
/>
<canvas
ref="rgbCanvasRef"
class="absolute top-0 left-0 w-full h-full z-10"
class="absolute top-0 left-0 z-10 h-full w-full"
@contextmenu.prevent
/>
<canvas
ref="maskCanvasRef"
class="absolute top-0 left-0 w-full h-full z-30"
class="absolute top-0 left-0 z-30 h-full w-full"
@contextmenu.prevent
/>
<!-- GPU Preview Canvas -->
<canvas
ref="gpuCanvasRef"
class="absolute top-0 left-0 w-full h-full pointer-events-none"
class="pointer-events-none absolute top-0 left-0 h-full w-full"
:class="{
'z-20': store.activeLayer === 'rgb',
'z-40': store.activeLayer === 'mask'
}"
/>
<div ref="canvasBackgroundRef" class="bg-white w-full h-full" />
<div
ref="canvasBackgroundRef"
class="h-full w-full bg-white"
/>
</div>
<div class="maskEditor-ui-container flex min-h-0 flex-1 flex-col">
@@ -60,7 +63,10 @@
</div>
</div>
<BrushCursor v-if="initialized" :container-ref="containerRef" />
<BrushCursor
v-if="initialized"
:container-ref="containerRef"
/>
</div>
</template>

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex flex-col gap-3 pb-3">
<h3
class="text-center text-[15px] font-sans text-[var(--descrip-text)] mt-2.5"
class="mt-2.5 text-center font-sans text-[15px] text-[var(--descrip-text)]"
>
{{ t('maskEditor.paintBucketSettings') }}
</h3>

View File

@@ -1,7 +1,7 @@
<template>
<div
ref="pointerZoneRef"
class="w-[calc(100%-4rem-220px)] h-full"
class="h-full w-[calc(100%-4rem-220px)]"
@pointerdown="handlePointerDown"
@pointermove="handlePointerMove"
@pointerup="handlePointerUp"

View File

@@ -1,11 +1,11 @@
<template>
<div
class="flex flex-col gap-3 pb-3 h-full !items-stretch bg-[var(--comfy-menu-bg)] overflow-y-auto w-55 px-2.5"
class="flex h-full w-55 flex-col !items-stretch gap-3 overflow-y-auto bg-[var(--comfy-menu-bg)] px-2.5 pb-3"
>
<div class="w-full min-h-full">
<div class="min-h-full w-full">
<SettingsPanelContainer />
<div class="w-full h-0.5 bg-[var(--border-color)] mt-6 mb-1.5" />
<div class="mt-6 mb-1.5 h-0.5 w-full bg-[var(--border-color)]" />
<ImageLayerSettingsPanel :tool-manager="toolManager" />
</div>

View File

@@ -1,5 +1,5 @@
<template>
<div class="h-full z-8888 flex flex-col justify-between bg-comfy-menu-bg">
<div class="z-8888 flex h-full flex-col justify-between bg-comfy-menu-bg">
<div class="flex flex-col">
<div
v-for="tool in allTools"
@@ -13,13 +13,13 @@
<div
class="flex items-center justify-center"
v-html="iconsHtml[tool]"
></div>
<div class="maskEditor_toolPanelIndicator"></div>
/>
<div class="maskEditor_toolPanelIndicator" />
</div>
</div>
<div
class="flex flex-col items-center cursor-pointer rounded-md mb-2 transition-colors duration-200 hover:bg-secondary-background-hover"
class="mb-2 flex cursor-pointer flex-col items-center rounded-md transition-colors duration-200 hover:bg-secondary-background-hover"
:title="t('maskEditor.clickToResetZoom')"
@click="onResetZoom"
>

View File

@@ -1,10 +1,10 @@
<template>
<div class="flex flex-row gap-2.5 items-center min-h-6 relative">
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<div class="relative flex min-h-6 flex-row items-center gap-2.5">
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
label
}}</span>
<select
class="absolute right-0 h-6 px-1.5 rounded-md border border-border-default transition-colors duration-100 bg-secondary-background focus:outline focus:outline-node-component-border"
class="absolute right-0 h-6 rounded-md border border-border-default bg-secondary-background px-1.5 transition-colors duration-100 focus:outline focus:outline-node-component-border"
:value="modelValue"
@change="onChange"
>

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex flex-col gap-3 pb-3">
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
label
}}</span>
<input
@@ -11,7 +11,7 @@
:step="step"
:value="modelValue"
@input="onInput"
/>
>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<template>
<div class="flex flex-row gap-2.5 items-center min-h-6 relative">
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
<div class="relative flex min-h-6 flex-row items-center gap-2.5">
<span class="text-left font-sans text-xs text-[var(--descrip-text)]">{{
label
}}</span>
<label class="maskEditor_sidePanelToggleContainer">
@@ -9,8 +9,8 @@
class="maskEditor_sidePanelToggleCheckbox"
:checked="modelValue"
@change="onChange"
/>
<div class="maskEditor_sidePanelToggleSwitch"></div>
>
<div class="maskEditor_sidePanelToggleSwitch" />
</label>
</div>
</template>

View File

@@ -1,7 +1,9 @@
<template>
<div class="flex w-full items-center justify-between gap-3">
<div class="flex items-center gap-3">
<h3 class="m-0 text-lg font-semibold">{{ t('maskEditor.title') }}</h3>
<h3 class="m-0 text-lg font-semibold">
{{ t('maskEditor.title') }}
</h3>
<div class="flex items-center gap-4">
<button
@@ -11,7 +13,7 @@
>
<svg
viewBox="0 0 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-current"
class="pointer-events-none h-6.25 w-6.25 fill-current"
>
<path
d="M8.77,12.18c-.25,0-.46-.2-.46-.46s.2-.46.46-.46c1.47,0,2.67-1.2,2.67-2.67,0-1.57-1.34-2.67-3.26-2.67h-3.98l1.43,1.43c.18.18.18.47,0,.64-.18.18-.47.18-.64,0l-2.21-2.21c-.18-.18-.18-.47,0-.64l2.21-2.21c.18-.18.47-.18.64,0,.18.18.18.47,0,.64l-1.43,1.43h3.98c2.45,0,4.17,1.47,4.17,3.58,0,1.97-1.61,3.58-3.58,3.58Z"
@@ -26,7 +28,7 @@
>
<svg
viewBox="0 0 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-[var(--input-text)]"
class="pointer-events-none h-6.25 w-6.25 fill-[var(--input-text)]"
>
<path
class="cls-1"
@@ -35,7 +37,7 @@
</svg>
</button>
<div class="h-5 border-l border-border" />
<div class="border-border h-5 border-l" />
<button
:class="iconButtonClass"
@@ -44,7 +46,7 @@
>
<svg
viewBox="-6 -7 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-[var(--input-text)]"
class="pointer-events-none h-6.25 w-6.25 fill-[var(--input-text)]"
>
<path
d="m2.25-2.625c.3452 0 .625.2798.625.625v5c0 .3452-.2798.625-.625.625h-5c-.3452 0-.625-.2798-.625-.625v-5c0-.3452.2798-.625.625-.625h5zm1.25.625v5c0 .6904-.5596 1.25-1.25 1.25h-5c-.6904 0-1.25-.5596-1.25-1.25v-5c0-.6904.5596-1.25 1.25-1.25h5c.6904 0 1.25.5596 1.25 1.25zm-.1673-2.3757-.4419.4419-1.5246-1.5246 1.5416-1.5417.442.4419-.7871.7872h.9373c1.3807 0 2.5 1.1193 2.5 2.5h-.625c0-1.0355-.8395-1.875-1.875-1.875h-.9375l.7702.7702z"
@@ -59,7 +61,7 @@
>
<svg
viewBox="-9 -7 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-[var(--input-text)]"
class="pointer-events-none h-6.25 w-6.25 fill-[var(--input-text)]"
>
<g transform="scale(-1, 1)">
<path
@@ -76,7 +78,7 @@
>
<svg
viewBox="0 0 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-[var(--input-text)]"
class="pointer-events-none h-6.25 w-6.25 fill-[var(--input-text)]"
>
<path
d="M7.5,1.5c-.28,0-.5.22-.5.5v11c0,.28.22.5.5.5s.5-.22.5-.5v-11c0-.28-.22-.5-.5-.5Z"
@@ -92,7 +94,7 @@
>
<svg
viewBox="0 0 15 15"
class="h-6.25 w-6.25 pointer-events-none fill-[var(--input-text)]"
class="pointer-events-none h-6.25 w-6.25 fill-[var(--input-text)]"
>
<path
d="M2,7.5c0-.28.22-.5.5-.5h11c.28,0,.5.22.5.5s-.22.5-.5.5h-11c-.28,0-.5-.22-.5-.5Z"
@@ -103,22 +105,35 @@
<div class="h-5 w-px bg-[var(--p-form-field-border-color)]" />
<button :class="textButtonClass" @click="onInvert">
<button
:class="textButtonClass"
@click="onInvert"
>
{{ t('maskEditor.invert') }}
</button>
<button :class="textButtonClass" @click="onClear">
<button
:class="textButtonClass"
@click="onClear"
>
{{ t('maskEditor.clear') }}
</button>
</div>
</div>
<div class="flex gap-3">
<Button variant="primary" :disabled="!saveEnabled" @click="handleSave">
<Button
variant="primary"
:disabled="!saveEnabled"
@click="handleSave"
>
<i class="pi pi-check" />
{{ saveButtonText }}
</Button>
<Button variant="secondary" @click="handleCancel">
<Button
variant="secondary"
@click="handleCancel"
>
<i class="pi pi-times" />
{{ t('g.cancel') }}
</Button>

View File

@@ -12,7 +12,10 @@
v-html="renderedHelpHtml"
/>
<!-- Fallback: markdown not found or fetch error -->
<div v-else class="fallback-content space-y-6 text-sm">
<div
v-else
class="fallback-content space-y-6 text-sm"
>
<p v-if="node.description">
<strong>{{ $t('g.description') }}:</strong> {{ node.description }}
</p>
@@ -31,7 +34,10 @@
</tr>
</thead>
<tbody>
<tr v-for="input in inputList" :key="input.name">
<tr
v-for="input in inputList"
:key="input.name"
>
<td>
<code>{{ input.name }}</code>
</td>
@@ -55,7 +61,10 @@
</tr>
</thead>
<tbody>
<tr v-for="output in outputList" :key="output.name">
<tr
v-for="output in outputList"
:key="output.name"
>
<td>
<code>{{ output.name }}</code>
</td>
@@ -103,27 +112,27 @@ const outputList = computed(() =>
@reference './../../assets/css/style.css';
.node-help-content :deep(:is(img, video)) {
@apply max-w-full h-auto block mb-4;
@apply mb-4 block h-auto max-w-full;
}
.markdown-content,
.fallback-content {
@apply text-sm overflow-visible;
@apply overflow-visible text-sm;
}
.markdown-content :deep(h1),
.fallback-content h1 {
@apply text-[22px] font-bold mt-8 mb-4 first:mt-0;
@apply mt-8 mb-4 text-[22px] font-bold first:mt-0;
}
.markdown-content :deep(h2),
.fallback-content h2 {
@apply text-[18px] font-bold mt-8 mb-4 first:mt-0;
@apply mt-8 mb-4 text-[18px] font-bold first:mt-0;
}
.markdown-content :deep(h3),
.fallback-content h3 {
@apply text-[16px] font-bold mt-8 mb-4 first:mt-0;
@apply mt-8 mb-4 text-[16px] font-bold first:mt-0;
}
.markdown-content :deep(h4),
@@ -155,7 +164,7 @@ const outputList = computed(() =>
.markdown-content :deep(ol),
.fallback-content ul,
.fallback-content ol {
@apply pl-8 my-2;
@apply my-2 pl-8;
}
.markdown-content :deep(ul ul),
@@ -166,7 +175,7 @@ const outputList = computed(() =>
.fallback-content ol ol,
.fallback-content ul ol,
.fallback-content ol ul {
@apply pl-6 my-2;
@apply my-2 pl-6;
}
.markdown-content :deep(li),
@@ -215,7 +224,7 @@ const outputList = computed(() =>
.markdown-content :deep(pre),
.fallback-content pre {
@apply rounded p-4 my-4 overflow-x-auto;
@apply my-4 overflow-x-auto rounded p-4;
background-color: var(--code-block-bg-color);
code {

View File

@@ -9,7 +9,7 @@
<div class="flex items-center justify-between px-3">
<Button
class="grow gap-1 justify-center"
class="grow justify-center gap-1"
variant="secondary"
size="sm"
@click="$emit('showAssets')"
@@ -51,7 +51,7 @@
@update:selected-sort-mode="$emit('update:selectedSortMode', $event)"
/>
<div class="flex-1 min-h-0 overflow-y-auto">
<div class="min-h-0 flex-1 overflow-y-auto">
<JobGroupsList
:displayed-job-groups="displayedJobGroups"
@cancel-item="onCancelItemEvent"

View File

@@ -4,7 +4,7 @@
:class="['flex', 'justify-end', 'w-full', 'pointer-events-none']"
>
<div
class="pointer-events-auto flex w-[350px] min-w-[310px] max-h-[60vh] flex-col overflow-hidden rounded-lg border font-inter transition-colors duration-200 ease-in-out"
class="pointer-events-auto flex max-h-[60vh] w-[350px] min-w-[310px] flex-col overflow-hidden rounded-lg border font-inter transition-colors duration-200 ease-in-out"
:class="containerClass"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
@@ -15,7 +15,7 @@
v-model:selected-job-tab="selectedJobTab"
v-model:selected-workflow-filter="selectedWorkflowFilter"
v-model:selected-sort-mode="selectedSortMode"
class="flex-1 min-h-0"
class="min-h-0 flex-1"
:header-title="headerTitle"
:show-concurrent-indicator="showConcurrentIndicator"
:concurrent-workflow-count="concurrentWorkflowCount"

View File

@@ -1,11 +1,11 @@
<template>
<section
class="w-[360px] rounded-2xl border border-interface-stroke bg-interface-panel-surface text-text-primary shadow-interface font-inter"
class="w-[360px] rounded-2xl border border-interface-stroke bg-interface-panel-surface font-inter text-text-primary shadow-interface"
>
<header
class="flex items-center justify-between border-b border-interface-stroke px-4 py-4"
>
<p class="m-0 text-[14px] font-normal leading-none">
<p class="m-0 text-[14px] leading-none font-normal">
{{ t('sideToolbar.queueProgressOverlay.clearHistoryDialogTitle') }}
</p>
<Button
@@ -31,7 +31,11 @@
<footer class="flex items-center justify-end px-4 py-4">
<div class="flex items-center gap-4 leading-none">
<Button variant="muted-textonly" size="lg" @click="onCancel">
<Button
variant="muted-textonly"
size="lg"
@click="onCancel"
>
{{ t('g.cancel') }}
</Button>
<Button
@@ -39,8 +43,9 @@
size="lg"
:disabled="isClearing"
@click="onConfirm"
>{{ t('g.clear') }}</Button
>
{{ t('g.clear') }}
</Button>
</div>
</footer>
</section>

View File

@@ -50,10 +50,10 @@
<div
v-if="
props.state === 'running' &&
hasAnyProgressPercent(
props.progressTotalPercent,
props.progressCurrentPercent
)
hasAnyProgressPercent(
props.progressTotalPercent,
props.progressCurrentPercent
)
"
:class="progressBarContainerClass"
>
@@ -72,7 +72,7 @@
<div class="relative z-1 flex items-center gap-1">
<div class="relative inline-flex items-center justify-center">
<div
class="absolute left-1/2 top-1/2 size-10 -translate-x-1/2 -translate-y-1/2"
class="absolute top-1/2 left-1/2 size-10 -translate-x-1/2 -translate-y-1/2"
@mouseenter.stop="onIconEnter"
@mouseleave.stop="onIconLeave"
/>
@@ -83,7 +83,7 @@
v-if="iconImageUrl"
:src="iconImageUrl"
class="h-full w-full object-cover"
/>
>
<i
v-else
:class="cn(iconClass, 'size-4', shouldSpin && 'animate-spin')"
@@ -93,8 +93,13 @@
</div>
<div class="relative z-1 min-w-0 flex-1">
<div class="truncate opacity-90" :title="props.title">
<slot name="primary">{{ props.title }}</slot>
<div
class="truncate opacity-90"
:title="props.title"
>
<slot name="primary">
{{ props.title }}
</slot>
</div>
</div>
@@ -143,8 +148,8 @@
<Button
v-else-if="
props.state !== 'completed' &&
props.state !== 'running' &&
computedShowClear
props.state !== 'running' &&
computedShowClear
"
v-tooltip.top="cancelTooltipConfig"
variant="destructive"
@@ -159,8 +164,9 @@
variant="textonly"
size="sm"
@click.stop="emit('view')"
>{{ t('menuLabels.View') }}</Button
>
{{ t('menuLabels.View') }}
</Button>
<Button
v-if="props.showMenu !== undefined ? props.showMenu : true"
v-tooltip.top="moreTooltipConfig"
@@ -177,7 +183,9 @@
key="secondary"
class="pr-2"
>
<slot name="secondary">{{ props.rightText }}</slot>
<slot name="secondary">
{{ props.rightText }}
</slot>
</div>
</Transition>
<!-- Running job cancel button - always visible -->

View File

@@ -194,8 +194,8 @@ function handleTitleCancel() {
>
<!-- Panel Header -->
<section class="pt-1">
<div class="flex items-center justify-between pl-4 pr-3">
<h3 class="my-3.5 text-sm font-semibold line-clamp-2 cursor-default">
<div class="flex items-center justify-between pr-3 pl-4">
<h3 class="my-3.5 line-clamp-2 cursor-default text-sm font-semibold">
<template v-if="allowTitleEdit">
<EditableText
:model-value="panelTitle"
@@ -208,7 +208,7 @@ function handleTitleCancel() {
/>
<i
v-if="!isEditing"
class="icon-[lucide--pencil] size-4 text-muted-foreground ml-2 content-center relative top-[2px] hover:text-base-foreground cursor-pointer shrink-0"
class="relative top-[2px] ml-2 icon-[lucide--pencil] size-4 shrink-0 cursor-pointer content-center text-muted-foreground hover:text-base-foreground"
@click="isEditing = true"
/>
</template>
@@ -242,7 +242,7 @@ function handleTitleCancel() {
</Button>
</div>
</div>
<nav class="px-4 pb-2 pt-1 overflow-x-auto">
<nav class="overflow-x-auto px-4 pt-1 pb-2">
<TabList
:model-value="activeTab"
@update:model-value="
@@ -254,7 +254,7 @@ function handleTitleCancel() {
<Tab
v-for="tab in tabs"
:key="tab.value"
class="text-sm py-1 px-2 font-inter transition-all active:scale-95"
class="px-2 py-1 font-inter text-sm transition-all active:scale-95"
:value="tab.value"
>
{{ tab.label() }}
@@ -284,7 +284,10 @@ function handleTitleCancel() {
:nodes="selectedNodes"
:must-show-node-title="selectedGroups.length > 0"
/>
<TabInfo v-else-if="activeTab === 'info'" :nodes="selectedNodes" />
<TabInfo
v-else-if="activeTab === 'info'"
:nodes="selectedNodes"
/>
<TabSettings
v-else-if="activeTab === 'settings'"
:nodes="flattedItems"

View File

@@ -25,21 +25,21 @@ const tooltipConfig = computed(() => {
<template>
<div class="flex flex-col bg-comfy-menu-bg">
<div
class="sticky top-0 z-10 flex items-center justify-between backdrop-blur-xl bg-inherit"
class="sticky top-0 z-10 flex items-center justify-between bg-inherit backdrop-blur-xl"
>
<button
v-tooltip="tooltipConfig"
type="button"
:class="
cn(
'group min-h-12 bg-transparent border-0 outline-0 ring-0 w-full text-left flex items-center justify-between pl-4 pr-3',
'group flex min-h-12 w-full items-center justify-between border-0 bg-transparent pr-3 pl-4 text-left ring-0 outline-0',
!disabled && 'cursor-pointer'
)
"
:disabled="disabled"
@click="isCollapse = !isCollapse"
>
<span class="text-sm font-semibold line-clamp-2 flex-1">
<span class="line-clamp-2 flex-1 text-sm font-semibold">
<slot name="label">
{{ label }}
</slot>
@@ -48,7 +48,7 @@ const tooltipConfig = computed(() => {
<i
:class="
cn(
'text-muted-foreground group-hover:text-base-foreground group-has-[.subbutton:hover]:text-muted-foreground group-focus:text-base-foreground icon-[lucide--chevron-up] size-4 transition-all',
'icon-[lucide--chevron-up] size-4 text-muted-foreground transition-all group-hover:text-base-foreground group-focus:text-base-foreground group-has-[.subbutton:hover]:text-muted-foreground',
isCollapse && '-rotate-180',
disabled && 'opacity-0'
)
@@ -57,10 +57,16 @@ const tooltipConfig = computed(() => {
</button>
</div>
<TransitionCollapse>
<div v-if="isExpanded" class="pb-4">
<div
v-if="isExpanded"
class="pb-4"
>
<slot />
</div>
<slot v-else-if="enableEmptyState && disabled" name="empty">
<slot
v-else-if="enableEmptyState && disabled"
name="empty"
>
<div>
{{ $t('g.empty') }}
</div>

View File

@@ -131,8 +131,8 @@ defineExpose({
:tooltip
>
<template #label>
<div class="flex items-center gap-2 flex-1 min-w-0">
<span class="flex-1 flex items-center gap-2 min-w-0">
<div class="flex min-w-0 flex-1 items-center gap-2">
<span class="flex min-w-0 flex-1 items-center gap-2">
<span class="truncate">
<slot name="label">
{{ displayLabel }}
@@ -140,7 +140,7 @@ defineExpose({
</span>
<span
v-if="parentGroup"
class="text-xs text-muted-foreground truncate flex-1 text-right min-w-11"
class="min-w-11 flex-1 truncate text-right text-xs text-muted-foreground"
:title="parentGroup.title"
>
{{ parentGroup.title }}
@@ -150,7 +150,7 @@ defineExpose({
v-if="canShowLocateButton"
variant="textonly"
size="icon-sm"
class="subbutton shrink-0 mr-3 size-8 cursor-pointer text-muted-foreground hover:text-base-foreground"
class="subbutton mr-3 size-8 shrink-0 cursor-pointer text-muted-foreground hover:text-base-foreground"
:title="t('rightSidePanel.locateNode')"
:aria-label="t('rightSidePanel.locateNode')"
@click.stop="handleLocateNode"
@@ -160,11 +160,13 @@ defineExpose({
</div>
</template>
<template #empty><slot name="empty" /></template>
<template #empty>
<slot name="empty" />
</template>
<div
ref="widgetsContainer"
class="space-y-2 rounded-lg px-4 pt-1 relative"
class="relative space-y-2 rounded-lg px-4 pt-1"
>
<TransitionGroup name="list-scale">
<WidgetItem

View File

@@ -111,7 +111,7 @@ onBeforeUnmount(() => {
</script>
<template>
<div class="px-4 pt-1 pb-4 flex gap-2 border-b border-interface-stroke">
<div class="flex gap-2 border-b border-interface-stroke px-4 pt-1 pb-4">
<FormSearchInput
v-model="searchQuery"
:searcher
@@ -130,7 +130,7 @@ onBeforeUnmount(() => {
@update:collapse="nextTick(setDraggableState)"
>
<template #empty>
<div class="text-sm text-muted-foreground px-4 text-center py-10">
<div class="px-4 py-10 text-center text-sm text-muted-foreground">
{{
isSearching
? t('rightSidePanel.noneSearchDesc')

View File

@@ -50,17 +50,21 @@ async function searcher(query: string) {
</script>
<template>
<div class="px-4 pt-1 pb-4 flex gap-2 border-b border-interface-stroke">
<div class="flex gap-2 border-b border-interface-stroke px-4 pt-1 pb-4">
<FormSearchInput
v-model="searchQuery"
:searcher
:update-key="widgetsSectionDataList"
/>
</div>
<TransitionGroup tag="div" name="list-scale" class="relative">
<TransitionGroup
tag="div"
name="list-scale"
class="relative"
>
<div
v-if="isSearching && searchedWidgetsSectionDataList.length === 0"
class="text-sm text-muted-foreground px-4 text-center pt-5 pb-15"
class="px-4 pt-5 pb-15 text-center text-sm text-muted-foreground"
>
{{ $t('rightSidePanel.noneSearchDesc') }}
</div>

View File

@@ -59,17 +59,21 @@ const label = computed(() => {
</script>
<template>
<div class="px-4 pt-1 pb-4 flex gap-2 border-b border-interface-stroke">
<div class="flex gap-2 border-b border-interface-stroke px-4 pt-1 pb-4">
<FormSearchInput
v-model="searchQuery"
:searcher
:update-key="widgetsSectionDataList"
/>
</div>
<TransitionGroup tag="div" name="list-scale" class="relative">
<TransitionGroup
tag="div"
name="list-scale"
class="relative"
>
<div
v-if="searchedWidgetsSectionDataList.length === 0"
class="text-sm text-muted-foreground px-4 py-10 text-center"
class="px-4 py-10 text-center text-sm text-muted-foreground"
>
{{
isSearching

View File

@@ -202,7 +202,7 @@ const label = computed(() => {
</script>
<template>
<div class="px-4 pt-1 pb-4 flex gap-2 border-b border-interface-stroke">
<div class="flex gap-2 border-b border-interface-stroke px-4 pt-1 pb-4">
<FormSearchInput
v-model="searchQuery"
:searcher
@@ -226,7 +226,7 @@ const label = computed(() => {
@update:collapse="nextTick(setDraggableState)"
>
<template #empty>
<div class="text-sm text-muted-foreground px-4 text-center pt-5 pb-15">
<div class="px-4 pt-5 pb-15 text-center text-sm text-muted-foreground">
{{ t('rightSidePanel.noneSearchDesc') }}
</div>
</template>

View File

@@ -99,7 +99,7 @@ function handleToggleFavorite() {
const buttonClasses = cn([
'border-none bg-transparent',
'w-full flex items-center gap-2 rounded px-3 py-2 text-sm',
'flex w-full items-center gap-2 rounded px-3 py-2 text-sm',
'cursor-pointer transition-all hover:bg-secondary-background-hover active:scale-95'
])
</script>
@@ -107,7 +107,7 @@ const buttonClasses = cn([
<template>
<MoreButton
is-vertical
class="text-muted-foreground bg-transparent hover:text-base-foreground hover:bg-secondary-background-hover active:scale-95 transition-all"
class="bg-transparent text-muted-foreground transition-all hover:bg-secondary-background-hover hover:text-base-foreground active:scale-95"
>
<template #default="{ close }">
<button

View File

@@ -106,9 +106,9 @@ const displayLabel = customRef((track, trigger) => {
<div
:class="
cn(
'widget-item col-span-full grid grid-cols-subgrid rounded-lg group',
'widget-item group col-span-full grid grid-cols-subgrid rounded-lg',
isDraggable &&
'draggable-item !will-change-auto drag-handle cursor-grab bg-comfy-menu-bg [&.is-draggable]:cursor-grabbing outline-comfy-menu-bg [&.is-draggable]:outline-4 [&.is-draggable]:outline-offset-0 [&.is-draggable]:opacity-70'
'draggable-item drag-handle cursor-grab bg-comfy-menu-bg outline-comfy-menu-bg !will-change-auto [&.is-draggable]:cursor-grabbing [&.is-draggable]:opacity-70 [&.is-draggable]:outline-4 [&.is-draggable]:outline-offset-0'
)
"
>
@@ -116,7 +116,7 @@ const displayLabel = customRef((track, trigger) => {
<div
:class="
cn(
'min-h-8 flex items-center justify-between gap-1 mb-1.5 min-w-0',
'mb-1.5 flex min-h-8 min-w-0 items-center justify-between gap-1',
isDraggable && 'pointer-events-none'
)
"
@@ -126,7 +126,7 @@ const displayLabel = customRef((track, trigger) => {
:model-value="displayLabel"
:is-editing="isEditing"
:input-attrs="{ placeholder: widget.name }"
class="text-sm leading-8 p-0 m-0 truncate pointer-events-auto cursor-text"
class="pointer-events-auto m-0 cursor-text truncate p-0 text-sm leading-8"
@edit="displayLabel = $event"
@cancel="isEditing = false"
@click="isEditing = true"
@@ -134,11 +134,11 @@ const displayLabel = customRef((track, trigger) => {
<span
v-if="(showNodeName || hasParents) && sourceNodeName"
class="text-xs text-muted-foreground flex-1 p-0 my-0 mx-1 truncate text-right min-w-10"
class="mx-1 my-0 min-w-10 flex-1 truncate p-0 text-right text-xs text-muted-foreground"
>
{{ sourceNodeName }}
</span>
<div class="flex items-center gap-1 shrink-0 pointer-events-auto">
<div class="pointer-events-auto flex shrink-0 items-center gap-1">
<WidgetActions
v-model:label="displayLabel"
:widget="widget"
@@ -152,12 +152,12 @@ const displayLabel = customRef((track, trigger) => {
<div
v-if="
!hiddenFavoriteIndicator &&
favoritedWidgetsStore.isFavorited(favoriteNode, widget.name)
favoritedWidgetsStore.isFavorited(favoriteNode, widget.name)
"
class="relative z-2 pointer-events-none"
class="pointer-events-none relative z-2"
>
<i
class="absolute -right-1 -top-1 pi pi-star-fill text-xs text-muted-foreground pointer-events-none"
class="pi pi-star-fill pointer-events-none absolute -top-1 -right-1 text-xs text-muted-foreground"
/>
</div>
<!-- widget content -->
@@ -173,7 +173,7 @@ const displayLabel = customRef((track, trigger) => {
<div
:class="
cn(
'pointer-events-none mt-1.5 mx-auto max-w-40 w-1/2 h-1 rounded-lg bg-transparent transition-colors duration-150',
'pointer-events-none mx-auto mt-1.5 h-1 w-1/2 max-w-40 rounded-lg bg-transparent transition-colors duration-150',
'group-hover:bg-interface-stroke group-[.is-draggable]:bg-component-node-widget-background-highlighted',
!isDraggable && 'opacity-0'
)

View File

@@ -18,14 +18,14 @@ defineProps<{
v-tooltip.left="
tooltip
? {
value: tooltip,
showDelay: 300
}
value: tooltip,
showDelay: 300
}
: null
"
:class="
cn(
'text-sm text-muted-foreground truncate',
'truncate text-sm text-muted-foreground',
tooltip ? 'cursor-help' : '',
singleline ? 'flex-1' : ''
)

View File

@@ -107,14 +107,14 @@ const nodeColor = computed<NodeColorOption['name'] | null>({
<template>
<LayoutField :label="t('rightSidePanel.color')">
<div
class="bg-secondary-background border-none rounded-lg p-1 grid grid-cols-5 gap-1 justify-items-center"
class="grid grid-cols-5 justify-items-center gap-1 rounded-lg border-none bg-secondary-background p-1"
>
<button
v-for="option of colorOptions"
:key="option.name"
:class="
cn(
'size-8 rounded-lg bg-transparent border-0 outline-0 ring-0 text-left flex justify-center items-center cursor-pointer',
'flex size-8 cursor-pointer items-center justify-center rounded-lg border-0 bg-transparent text-left ring-0 outline-0',
option.name === nodeColor
? 'bg-interface-menu-component-surface-selected'
: 'hover:bg-interface-menu-component-surface-selected'

View File

@@ -125,7 +125,7 @@ function openFullSettings() {
<LayoutField :label="t('rightSidePanel.globalSettings.gridSpacing')">
<div
:class="
cn(WidgetInputBaseClass, 'flex items-center gap-2 pl-3 pr-2')
cn(WidgetInputBaseClass, 'flex items-center gap-2 pr-2 pl-3')
"
>
<Slider
@@ -189,7 +189,7 @@ function openFullSettings() {
<!-- View all settings button -->
<div
class="flex items-center justify-center p-4 border-b border-interface-stroke"
class="flex items-center justify-center border-b border-interface-stroke p-4"
>
<Button
variant="muted-textonly"

View File

@@ -239,8 +239,11 @@ onBeforeUnmount(() => {
</script>
<template>
<div v-if="activeNode" class="subgraph-edit-section flex h-full flex-col">
<div class="px-4 pb-4 pt-1 flex gap-2 border-b border-interface-stroke">
<div
v-if="activeNode"
class="subgraph-edit-section flex h-full flex-col"
>
<div class="flex gap-2 border-b border-interface-stroke px-4 pt-1 pb-4">
<FormSearchInput v-model="searchQuery" />
</div>
@@ -248,10 +251,10 @@ onBeforeUnmount(() => {
<div
v-if="
searchQuery &&
filteredActive.length === 0 &&
filteredCandidates.length === 0
filteredActive.length === 0 &&
filteredCandidates.length === 0
"
class="text-sm text-muted-foreground px-4 py-10 text-center"
class="px-4 py-10 text-center text-sm text-muted-foreground"
>
{{ $t('rightSidePanel.noneSearchDesc') }}
</div>
@@ -261,19 +264,21 @@ onBeforeUnmount(() => {
class="flex flex-col border-b border-interface-stroke"
>
<div
class="sticky top-0 z-10 flex items-center justify-between backdrop-blur-xl min-h-12 px-4"
class="sticky top-0 z-10 flex min-h-12 items-center justify-between px-4 backdrop-blur-xl"
>
<div class="text-sm font-semibold uppercase line-clamp-1">
<div class="line-clamp-1 text-sm font-semibold uppercase">
{{ $t('subgraphStore.shown') }}
</div>
<a
class="cursor-pointer text-right text-xs font-normal text-text-secondary hover:text-azure-600 whitespace-nowrap"
class="cursor-pointer text-right text-xs font-normal whitespace-nowrap text-text-secondary hover:text-azure-600"
@click.stop="hideAll"
>
{{ $t('subgraphStore.hideAll') }}</a
>
{{ $t('subgraphStore.hideAll') }}</a>
</div>
<div ref="draggableItems" class="pb-2 px-2 space-y-0.5 mt-0.5">
<div
ref="draggableItems"
class="mt-0.5 space-y-0.5 px-2 pb-2"
>
<SubgraphNodeWidget
v-for="[node, widget] in filteredActive"
:key="toKey([node, widget])"
@@ -293,19 +298,18 @@ onBeforeUnmount(() => {
class="flex flex-col border-b border-interface-stroke"
>
<div
class="sticky top-0 z-10 flex items-center justify-between backdrop-blur-xl min-h-12 px-4"
class="sticky top-0 z-10 flex min-h-12 items-center justify-between px-4 backdrop-blur-xl"
>
<div class="text-sm font-semibold uppercase line-clamp-1">
<div class="line-clamp-1 text-sm font-semibold uppercase">
{{ $t('subgraphStore.hidden') }}
</div>
<a
class="cursor-pointer text-right text-xs font-normal text-text-secondary hover:text-azure-600 whitespace-nowrap"
class="cursor-pointer text-right text-xs font-normal whitespace-nowrap text-text-secondary hover:text-azure-600"
@click.stop="showAll"
>
{{ $t('subgraphStore.showAll') }}</a
>
{{ $t('subgraphStore.showAll') }}</a>
</div>
<div class="pb-2 px-2 space-y-0.5 mt-0.5">
<div class="mt-0.5 space-y-0.5 px-2 pb-2">
<SubgraphNodeWidget
v-for="[node, widget] in filteredCandidates"
:key="toKey([node, widget])"

View File

@@ -27,19 +27,21 @@ function getIcon() {
<div
:class="
cn(
'flex py-1 px-2 break-all rounded items-center gap-1',
'flex items-center gap-1 rounded px-2 py-1 break-all',
'bg-node-component-surface',
props.isDraggable &&
'draggable-item drag-handle cursor-grab [&.is-draggable]:cursor-grabbing hover:ring-1 ring-accent-background',
'draggable-item drag-handle cursor-grab ring-accent-background hover:ring-1 [&.is-draggable]:cursor-grabbing',
props.class
)
"
>
<div class="pointer-events-none flex-1">
<div class="text-xs text-text-secondary line-clamp-1">
<div class="line-clamp-1 text-xs text-text-secondary">
{{ nodeTitle }}
</div>
<div class="text-sm line-clamp-1 leading-8">{{ widgetName }}</div>
<div class="line-clamp-1 text-sm leading-8">
{{ widgetName }}
</div>
</div>
<Button
variant="muted-textonly"
@@ -51,7 +53,7 @@ function getIcon() {
</Button>
<div
v-if="isDraggable"
class="size-4 pointer-events-none icon-[lucide--grip-vertical]"
class="pointer-events-none icon-[lucide--grip-vertical] size-4"
/>
</div>
</template>

View File

@@ -17,7 +17,12 @@
/>
</div>
<div class="_footer">
<Button type="button" @click="submit">{{ $t('g.add') }}</Button>
<Button
type="button"
@click="submit"
>
{{ $t('g.add') }}
</Button>
</div>
</template>
@@ -76,6 +81,6 @@ const submit = () => {
}
._footer {
@apply flex flex-col pt-4 items-end;
@apply flex flex-col items-end pt-4;
}
</style>

View File

@@ -7,7 +7,7 @@ import { useCommandStore } from '@/stores/commandStore'
const canvasStore = useCanvasStore()
</script>
<template>
<div class="p-1 bg-secondary-background rounded-lg w-10">
<div class="w-10 rounded-lg bg-secondary-background p-1">
<Button
size="icon"
:title="t('linearMode.linearMode')"

View File

@@ -14,10 +14,13 @@
:class="
isOverflowing
? 'side-tool-bar-container overflow-y-auto'
: 'flex flex-col h-full'
: 'flex h-full flex-col'
"
>
<div ref="topToolbarRef" :class="groupClasses">
<div
ref="topToolbarRef"
:class="groupClasses"
>
<ComfyMenuButton />
<SidebarIcon
v-for="tab in tabs"
@@ -35,12 +38,19 @@
<SidebarTemplatesButton />
</div>
<div ref="bottomToolbarRef" class="mt-auto" :class="groupClasses">
<div
ref="bottomToolbarRef"
class="mt-auto"
:class="groupClasses"
>
<SidebarLogoutIcon
v-if="userStore.isMultiUserServer"
:is-small="isSmall"
/>
<SidebarHelpCenterIcon v-if="!isIntegratedTabBar" :is-small="isSmall" />
<SidebarHelpCenterIcon
v-if="!isIntegratedTabBar"
:is-small="isSmall"
/>
<SidebarBottomPanelToggleButton :is-small="isSmall" />
<SidebarShortcutsToggleButton :is-small="isSmall" />
<SidebarSettingsButton :is-small="isSmall" />
@@ -158,8 +168,8 @@ const getTabTooltipSuffix = (tab: SidebarTabExtension) => {
const isOverflowing = ref(false)
const groupClasses = computed(() =>
cn(
'sidebar-item-group flex flex-col items-center overflow-hidden flex-shrink-0',
!isConnected.value && 'rounded-lg shadow-interface pointer-events-auto'
'sidebar-item-group flex flex-shrink-0 flex-col items-center overflow-hidden',
!isConnected.value && 'pointer-events-auto rounded-lg shadow-interface'
)
)

View File

@@ -17,12 +17,19 @@
>
<div class="side-bar-button-content">
<slot name="icon">
<OverlayBadge v-if="shouldShowBadge" :value="overlayValue">
<OverlayBadge
v-if="shouldShowBadge"
:value="overlayValue"
>
<i
v-if="typeof icon === 'string'"
:class="icon + ' side-bar-button-icon'"
/>
<component :is="icon" v-else class="side-bar-button-icon" />
<component
:is="icon"
v-else
class="side-bar-button-icon"
/>
</OverlayBadge>
<i
v-else-if="typeof icon === 'string'"
@@ -34,7 +41,10 @@
class="side-bar-button-icon"
/>
</slot>
<span v-if="label && !isSmall" class="side-bar-button-label">{{
<span
v-if="label && !isSmall"
class="side-bar-button-label"
>{{
t(label)
}}</span>
</div>
@@ -117,7 +127,7 @@ const computedTooltip = computed(() => t(tooltip) + tooltipSuffix)
}
.side-bar-button-label {
@apply text-[10px] text-center;
@apply text-center text-[10px];
line-height: 1;
}

View File

@@ -25,7 +25,10 @@
@mouseleave="onJobLeave(job.id)"
@click.stop
>
<template v-if="hoveredJobId === job.id" #actions>
<template
v-if="hoveredJobId === job.id"
#actions
>
<Button
v-if="canCancelJob"
:variant="cancelAction.variant"
@@ -33,7 +36,10 @@
:aria-label="cancelAction.label"
@click.stop="runCancelJob()"
>
<i :class="cancelAction.icon" class="size-4" />
<i
:class="cancelAction.icon"
class="size-4"
/>
</Button>
</template>
</AssetsListItem>
@@ -44,7 +50,7 @@
:class="cn('px-2', activeJobItems.length && 'mt-2')"
>
<div
class="flex items-center py-2 text-sm font-normal leading-normal text-muted-foreground font-inter"
class="flex items-center py-2 font-inter text-sm leading-normal font-normal text-muted-foreground"
>
{{ t('sideToolbar.generatedAssetsHeader') }}
</div>
@@ -79,7 +85,10 @@
@contextmenu.prevent.stop="emit('context-menu', $event, item.asset)"
@click.stop="emit('select-asset', item.asset)"
>
<template v-if="hoveredAssetId === item.asset.id" #actions>
<template
v-if="hoveredAssetId === item.asset.id"
#actions
>
<Button
variant="secondary"
size="icon"
@@ -194,7 +203,7 @@ function getAssetCardClass(selected: boolean): string {
'w-full text-text-primary transition-colors hover:bg-secondary-background-hover',
'cursor-pointer',
selected &&
'bg-secondary-background-hover ring-1 ring-inset ring-modal-card-border-highlighted'
'bg-secondary-background-hover ring-1 ring-modal-card-border-highlighted ring-inset'
)
}

View File

@@ -15,7 +15,7 @@
role="button"
@click="copyJobId"
>
<i class="icon-[lucide--copy] text-sm"></i>
<i class="icon-[lucide--copy] text-sm" />
</button>
</div>
<div>
@@ -25,19 +25,39 @@
</template>
<template #tool-buttons>
<!-- Normal Tab View -->
<TabList v-if="!isInFolderView" v-model="activeTab">
<Tab class="font-inter" value="output">{{
$t('sideToolbar.labels.generated')
}}</Tab>
<Tab class="font-inter" value="input">{{
$t('sideToolbar.labels.imported')
}}</Tab>
<TabList
v-if="!isInFolderView"
v-model="activeTab"
>
<Tab
class="font-inter"
value="output"
>
{{
$t('sideToolbar.labels.generated')
}}
</Tab>
<Tab
class="font-inter"
value="input"
>
{{
$t('sideToolbar.labels.imported')
}}
</Tab>
</TabList>
</template>
<template #header>
<!-- Job Detail View Header -->
<div v-if="isInFolderView" class="px-2 2xl:px-4">
<Button variant="secondary" size="lg" @click="exitFolderView">
<div
v-if="isInFolderView"
class="px-2 2xl:px-4"
>
<Button
variant="secondary"
size="lg"
@click="exitFolderView"
>
<i class="icon-[lucide--arrow-left] size-4" />
<span>{{ $t('sideToolbar.backToAssets') }}</span>
</Button>
@@ -49,7 +69,7 @@
v-model:sort-by="sortBy"
v-model:view-mode="viewMode"
v-model:media-type-filters="mediaTypeFilters"
class="pb-1 px-2 2xl:px-4"
class="px-2 pb-1 2xl:px-4"
:show-generation-time-sort="activeTab === 'output'"
/>
<div
@@ -76,7 +96,11 @@
</Button>
</div>
</div>
<Divider v-else type="dashed" class="my-2" />
<Divider
v-else
type="dashed"
class="my-2"
/>
</template>
<template #body>
<div v-if="showLoadingState">
@@ -95,7 +119,11 @@
:message="$t('sideToolbar.noFilesFoundMessage')"
/>
</div>
<div v-else class="relative size-full" @click="handleEmptySpaceClick">
<div
v-else
class="relative size-full"
@click="handleEmptySpaceClick"
>
<AssetsSidebarListView
v-if="isListView"
:assets="displayAssets"
@@ -134,10 +162,13 @@
<div
v-if="hasSelection"
ref="footerRef"
class="flex gap-1 h-18 w-full items-center justify-between"
class="flex h-18 w-full items-center justify-between gap-1"
>
<div class="flex-1 pl-4">
<div ref="selectionCountButtonRef" class="inline-flex w-48">
<div
ref="selectionCountButtonRef"
class="inline-flex w-48"
>
<Button
variant="secondary"
:class="cn(isCompact && 'text-left')"
@@ -147,13 +178,13 @@
isHoveringSelectionCount
? $t('mediaAsset.selection.deselectAll')
: $t('mediaAsset.selection.selectedCount', {
count: totalOutputCount
})
count: totalOutputCount
})
}}
</Button>
</div>
</div>
<div class="flex shrink gap-2 pr-4 items-center-safe justify-end-safe">
<div class="flex shrink items-center-safe justify-end-safe gap-2 pr-4">
<template v-if="isCompact">
<!-- Compact mode: Icon only -->
<Button
@@ -163,7 +194,10 @@
>
<i class="icon-[lucide--trash-2] size-4" />
</Button>
<Button size="icon" @click="handleDownloadSelected">
<Button
size="icon"
@click="handleDownloadSelected"
>
<i class="icon-[lucide--download] size-4" />
</Button>
</template>
@@ -177,7 +211,10 @@
<span>{{ $t('mediaAsset.selection.deleteSelected') }}</span>
<i class="icon-[lucide--trash-2] size-4" />
</Button>
<Button variant="secondary" @click="handleDownloadSelected">
<Button
variant="secondary"
@click="handleDownloadSelected"
>
<span>{{ $t('mediaAsset.selection.downloadSelected') }}</span>
<i class="icon-[lucide--download] size-4" />
</Button>

View File

@@ -5,17 +5,20 @@
>
<div class="comfy-vue-side-bar-header flex flex-col gap-2">
<Toolbar
class="min-h-16 bg-transparent rounded-none border-x-0 border-t-0 px-2 2xl:px-4"
class="min-h-16 rounded-none border-x-0 border-t-0 bg-transparent px-2 2xl:px-4"
>
<template #start>
<span class="truncate font-bold" :title="props.title">
<span
class="truncate font-bold"
:title="props.title"
>
{{ props.title }}
</span>
<slot name="alt-title" />
</template>
<template #end>
<div
class="touch:w-auto touch:opacity-100 flex flex-row overflow-hidden transition-all duration-200 motion-safe:w-0 motion-safe:opacity-0 motion-safe:group-focus-within/sidebar-tab:w-auto motion-safe:group-focus-within/sidebar-tab:opacity-100 motion-safe:group-hover/sidebar-tab:w-auto motion-safe:group-hover/sidebar-tab:opacity-100"
class="flex flex-row overflow-hidden transition-all duration-200 motion-safe:w-0 motion-safe:opacity-0 motion-safe:group-focus-within/sidebar-tab:w-auto motion-safe:group-focus-within/sidebar-tab:opacity-100 motion-safe:group-hover/sidebar-tab:w-auto motion-safe:group-hover/sidebar-tab:opacity-100 touch:w-auto touch:opacity-100"
>
<slot name="tool-buttons" />
</div>

View File

@@ -32,12 +32,12 @@ const isActive = computed(() => currentValue?.value === value)
const tabClasses = computed(() => {
return cn(
// Base styles from TextButton
'flex items-center justify-center shrink-0',
'px-2.5 py-2 text-sm rounded-lg cursor-pointer transition-all duration-200',
'outline-hidden border-none',
'flex shrink-0 items-center justify-center',
'cursor-pointer rounded-lg px-2.5 py-2 text-sm transition-all duration-200',
'border-none outline-hidden',
// State styles with semantic tokens
isActive.value
? 'bg-interface-menu-component-surface-hovered text-text-primary text-bold'
? 'text-bold bg-interface-menu-component-surface-hovered text-text-primary'
: 'bg-transparent text-text-secondary hover:bg-button-hover-surface focus:bg-button-hover-surface'
)
})

View File

@@ -11,14 +11,20 @@
<div
:class="
cn(
'flex items-center gap-1 rounded-full hover:bg-interface-button-hover-surface justify-center',
compact && 'size-full aspect-square'
'flex items-center justify-center gap-1 rounded-full hover:bg-interface-button-hover-surface',
compact && 'aspect-square size-full'
)
"
>
<UserAvatar :photo-url="photoURL" :class="compact && 'size-full'" />
<UserAvatar
:photo-url="photoURL"
:class="compact && 'size-full'"
/>
<i v-if="showArrow" class="icon-[lucide--chevron-down] size-3 px-1" />
<i
v-if="showArrow"
class="icon-[lucide--chevron-down] size-3 px-1"
/>
</div>
</Button>

View File

@@ -1,10 +1,10 @@
<!-- A popover that shows current user information and actions -->
<template>
<div
class="current-user-popover w-80 -m-3 p-2 rounded-lg border border-border-default bg-base-background shadow-[1px_1px_8px_0_rgba(0,0,0,0.4)]"
class="current-user-popover -m-3 w-80 rounded-lg border border-border-default bg-base-background p-2 shadow-[1px_1px_8px_0_rgba(0,0,0,0.4)]"
>
<!-- User Info Section -->
<div class="flex flex-col items-center px-0 py-3 mb-4">
<div class="mb-4 flex flex-col items-center px-0 py-3">
<UserAvatar
class="mb-1"
:photo-url="userPhotoUrl"
@@ -18,32 +18,41 @@
<h3 class="my-0 mb-1 truncate text-base font-bold text-base-foreground">
{{ userDisplayName || $t('g.user') }}
</h3>
<p v-if="userEmail" class="my-0 truncate text-sm text-muted">
<p
v-if="userEmail"
class="my-0 truncate text-sm text-muted"
>
{{ userEmail }}
</p>
<span
v-if="subscriptionTierName"
class="my-0 text-xs text-foreground bg-secondary-background-hover rounded-full uppercase px-2 py-0.5 font-bold mt-2"
class="text-foreground my-0 mt-2 rounded-full bg-secondary-background-hover px-2 py-0.5 text-xs font-bold uppercase"
>
{{ subscriptionTierName }}
</span>
</div>
<!-- Credits Section -->
<div v-if="isActiveSubscription" class="flex items-center gap-2 px-4 py-2">
<i class="icon-[lucide--component] text-amber-400 text-sm" />
<div
v-if="isActiveSubscription"
class="flex items-center gap-2 px-4 py-2"
>
<i class="icon-[lucide--component] text-sm text-amber-400" />
<Skeleton
v-if="authStore.isFetchingBalance"
width="4rem"
height="1.25rem"
class="w-full"
/>
<span v-else class="text-base font-semibold text-base-foreground">{{
<span
v-else
class="text-base font-semibold text-base-foreground"
>{{
formattedBalance
}}</span>
<i
v-tooltip="{ value: $t('credits.unified.tooltip'), showDelay: 300 }"
class="icon-[lucide--circle-help] cursor-help text-base text-muted-foreground mr-auto"
class="mr-auto icon-[lucide--circle-help] cursor-help text-base text-muted-foreground"
/>
<Button
variant="secondary"
@@ -56,7 +65,10 @@
</Button>
</div>
<div v-else class="flex justify-center px-4">
<div
v-else
class="flex justify-center px-4"
>
<SubscribeButton
:fluid="false"
:label="$t('subscription.subscribeToComfyCloud')"
@@ -66,32 +78,32 @@
/>
</div>
<Divider class="my-2 mx-0" />
<Divider class="mx-0 my-2" />
<div
v-if="isActiveSubscription"
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
class="flex cursor-pointer items-center gap-2 px-4 py-2 hover:bg-secondary-background-hover"
data-testid="partner-nodes-menu-item"
@click="handleOpenPartnerNodesInfo"
>
<i class="icon-[lucide--tag] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
<i class="icon-[lucide--tag] text-sm text-muted-foreground" />
<span class="flex-1 text-sm text-base-foreground">{{
$t('subscription.partnerNodesCredits')
}}</span>
</div>
<div
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
class="flex cursor-pointer items-center gap-2 px-4 py-2 hover:bg-secondary-background-hover"
data-testid="plans-pricing-menu-item"
@click="handleOpenPlansAndPricing"
>
<i class="icon-[lucide--receipt-text] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
<i class="icon-[lucide--receipt-text] text-sm text-muted-foreground" />
<span class="flex-1 text-sm text-base-foreground">{{
$t('subscription.plansAndPricing')
}}</span>
<span
v-if="canUpgrade"
class="text-xs font-bold text-base-background bg-base-foreground px-1.5 py-0.5 rounded-full"
class="rounded-full bg-base-foreground px-1.5 py-0.5 text-xs font-bold text-base-background"
>
{{ $t('subscription.upgrade') }}
</span>
@@ -99,36 +111,36 @@
<div
v-if="isActiveSubscription"
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
class="flex cursor-pointer items-center gap-2 px-4 py-2 hover:bg-secondary-background-hover"
data-testid="manage-plan-menu-item"
@click="handleOpenPlanAndCreditsSettings"
>
<i class="icon-[lucide--file-text] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
<i class="icon-[lucide--file-text] text-sm text-muted-foreground" />
<span class="flex-1 text-sm text-base-foreground">{{
$t('subscription.managePlan')
}}</span>
</div>
<div
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
class="flex cursor-pointer items-center gap-2 px-4 py-2 hover:bg-secondary-background-hover"
data-testid="user-settings-menu-item"
@click="handleOpenUserSettings"
>
<i class="icon-[lucide--settings-2] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
<i class="icon-[lucide--settings-2] text-sm text-muted-foreground" />
<span class="flex-1 text-sm text-base-foreground">{{
$t('userSettings.accountSettings')
}}</span>
</div>
<Divider class="my-2 mx-0" />
<Divider class="mx-0 my-2" />
<div
class="flex items-center gap-2 px-4 py-2 cursor-pointer hover:bg-secondary-background-hover"
class="flex cursor-pointer items-center gap-2 px-4 py-2 hover:bg-secondary-background-hover"
data-testid="logout-menu-item"
@click="handleLogout"
>
<i class="icon-[lucide--log-out] text-muted-foreground text-sm" />
<span class="text-sm text-base-foreground flex-1">{{
<i class="icon-[lucide--log-out] text-sm text-muted-foreground" />
<span class="flex-1 text-sm text-base-foreground">{{
$t('auth.signOut.signOut')
}}</span>
</div>

View File

@@ -3,7 +3,7 @@
v-if="!isLoggedIn"
variant="textonly"
size="icon"
:class="cn('group rounded-full text-base-foreground p-0', className)"
:class="cn('group rounded-full p-0 text-base-foreground', className)"
:aria-label="t('g.login')"
@click="handleSignIn()"
@mouseenter="showPopover"
@@ -22,13 +22,14 @@
@mouseover="cancelHidePopover"
>
<div>
<div class="mb-1">{{ t('auth.loginButton.tooltipHelp') }}</div>
<div class="mb-1">
{{ t('auth.loginButton.tooltipHelp') }}
</div>
<a
:href="apiNodesOverviewUrl"
target="_blank"
class="text-neutral-500 hover:text-primary"
>{{ t('auth.loginButton.tooltipLearnMore') }}</a
>
>{{ t('auth.loginButton.tooltipLearnMore') }}</a>
</div>
</Popover>
</template>

View File

@@ -4,8 +4,10 @@
variant="textonly"
@click="toggleHelpCenter"
>
<div class="not-md:hidden">{{ $t('menu.helpAndFeedback') }}</div>
<i class="icon-[lucide--circle-help] ml-0.5" />
<div class="not-md:hidden">
{{ $t('menu.helpAndFeedback') }}
</div>
<i class="ml-0.5 icon-[lucide--circle-help]" />
<span
v-if="shouldShowRedDot"
class="absolute top-[7px] right-[7px] size-1.5 rounded-full bg-[#ff3b30]"

View File

@@ -18,7 +18,11 @@
>
{{ badge.label }}
</div>
<div v-else class="size-2 shrink-0 rounded-full" :class="dotClasses" />
<div
v-else
class="size-2 shrink-0 rounded-full"
:class="dotClasses"
/>
<Popover
ref="popover"
append-to="body"
@@ -37,8 +41,13 @@
>
{{ badge.label }}
</div>
<div class="text-sm font-inter">{{ badge.text }}</div>
<div v-if="badge.tooltip" class="text-xs">
<div class="font-inter text-sm">
{{ badge.text }}
</div>
<div
v-if="badge.tooltip"
class="text-xs"
>
{{ badge.tooltip }}
</div>
</div>
@@ -90,8 +99,13 @@
>
{{ badge.label }}
</div>
<div class="text-sm font-inter">{{ badge.text }}</div>
<div v-if="badge.tooltip" class="text-xs">
<div class="font-inter text-sm">
{{ badge.text }}
</div>
<div
v-if="badge.tooltip"
class="text-xs"
>
{{ badge.tooltip }}
</div>
</div>
@@ -117,7 +131,10 @@
>
{{ badge.label }}
</div>
<div class="font-inter text-sm" :class="textClasses">
<div
class="font-inter text-sm"
:class="textClasses"
>
{{ badge.text }}
</div>
</div>

View File

@@ -2,7 +2,7 @@
<div>
<Button
v-tooltip="{ value: $t('g.moreWorkflows'), showDelay: 300 }"
class="rounded-none h-full w-auto aspect-square"
class="aspect-square h-full w-auto rounded-none"
variant="muted-textonly"
size="icon"
:aria-label="$t('g.moreWorkflows')"

View File

@@ -2,7 +2,7 @@
<div
ref="positionRef"
class="absolute bottom-0 left-1/2 -translate-x-1/2"
></div>
/>
<Popover
ref="popoverRef"
append-to="body"
@@ -24,7 +24,7 @@
:src="thumbnailUrl"
class="block h-[200px] rounded-lg object-cover p-2"
:style="{ width: `${POPOVER_WIDTH}px` }"
/>
>
</div>
<div class="workflow-preview-footer">
<span class="workflow-preview-name">{{ workflowFilename }}</span>
@@ -143,7 +143,7 @@ defineExpose({
@reference '../../assets/css/style.css';
.workflow-preview-content {
@apply flex flex-col rounded-xl overflow-hidden;
@apply flex flex-col overflow-hidden rounded-xl;
max-width: var(--popover-width);
background-color: var(--comfy-menu-bg);
color: var(--fg-color);
@@ -163,11 +163,11 @@ defineExpose({
}
.workflow-preview-footer {
@apply pt-1 pb-2 px-3;
@apply px-3 pt-1 pb-2;
}
.workflow-preview-name {
@apply block text-sm font-medium overflow-hidden text-ellipsis whitespace-nowrap;
@apply block overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap;
color: var(--fg-color);
}
</style>
@@ -178,7 +178,7 @@ defineExpose({
.workflow-popover-fade {
--p-popover-background: transparent;
--p-popover-content-padding: 0;
@apply bg-transparent rounded-xl shadow-lg;
@apply rounded-xl bg-transparent shadow-lg;
transition: opacity 0.15s ease-out !important;
}

View File

@@ -8,7 +8,7 @@
v-if="showOverflowArrows"
variant="muted-textonly"
size="icon"
class="overflow-arrow overflow-arrow-left h-full w-auto aspect-square"
class="overflow-arrow overflow-arrow-left aspect-square h-full w-auto"
:aria-label="$t('g.scrollLeft')"
:disabled="!leftArrowEnabled"
@mousedown="whileMouseDown($event, () => scroll(-1))"
@@ -45,7 +45,7 @@
v-if="showOverflowArrows"
variant="muted-textonly"
size="icon"
class="overflow-arrow overflow-arrow-right h-full w-auto aspect-square"
class="overflow-arrow overflow-arrow-right aspect-square h-full w-auto"
:aria-label="$t('g.scrollRight')"
:disabled="!rightArrowEnabled"
@mousedown="whileMouseDown($event, () => scroll(1))"
@@ -59,7 +59,7 @@
/>
<Button
v-tooltip="{ value: $t('sideToolbar.newBlankWorkflow'), showDelay: 300 }"
class="new-blank-workflow-button no-drag shrink-0 rounded-none h-full w-auto aspect-square"
class="new-blank-workflow-button no-drag aspect-square h-full w-auto shrink-0 rounded-none"
variant="muted-textonly"
size="icon"
:aria-label="$t('sideToolbar.newBlankWorkflow')"
@@ -78,15 +78,30 @@
compact
class="shrink-0 p-1"
/>
<LoginButton v-else-if="isDesktop" class="p-1" />
<LoginButton
v-else-if="isDesktop"
class="p-1"
/>
</div>
<ContextMenu ref="menu" :model="contextMenuItems">
<ContextMenu
ref="menu"
:model="contextMenuItems"
>
<template #itemicon="{ item }">
<OverlayIcon v-if="item.overlayIcon" v-bind="item.overlayIcon" />
<i v-else-if="item.icon" :class="item.icon" />
<OverlayIcon
v-if="item.overlayIcon"
v-bind="item.overlayIcon"
/>
<i
v-else-if="item.icon"
:class="item.icon"
/>
</template>
</ContextMenu>
<div v-if="isDesktop" class="window-actions-spacer app-drag shrink-0" />
<div
v-if="isDesktop"
class="window-actions-spacer app-drag shrink-0"
/>
</div>
</template>
@@ -383,13 +398,13 @@ onUpdated(() => {
}
:deep(.p-togglebutton) {
@apply p-0 bg-transparent rounded-none shrink relative border-0 border-r border-solid;
@apply relative shrink rounded-none border-0 border-r border-solid bg-transparent p-0;
border-right-color: var(--border-color);
min-width: 90px;
}
.overflow-arrow {
@apply px-2 rounded-none;
@apply rounded-none px-2;
}
.overflow-arrow[disabled] {
@@ -418,7 +433,7 @@ onUpdated(() => {
}
:deep(.p-togglebutton.p-togglebutton-checked) {
@apply border-b border-solid h-full;
@apply h-full border-b border-solid;
border-bottom-color: var(--p-button-text-primary-color);
}
@@ -446,7 +461,7 @@ onUpdated(() => {
}
:deep(.p-selectbutton) {
@apply rounded-none h-full;
@apply h-full rounded-none;
}
.workflow-tabs-container-desktop {

View File

@@ -36,21 +36,21 @@ defineProps<{
:side-offset="5"
:collision-padding="10"
v-bind="$attrs"
class="rounded-lg p-2 bg-base-background shadow-sm border border-border-subtle will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
class="data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade rounded-lg border border-border-subtle bg-base-background p-2 shadow-sm will-change-[transform,opacity]"
>
<slot>
<div class="flex flex-col p-1">
<section
v-for="(entryGroup, index) in entries ?? []"
:key="index"
class="flex flex-col border-b-2 last:border-none border-border-subtle"
class="flex flex-col border-b-2 border-border-subtle last:border-none"
>
<div
v-for="{ label, action, icon } in entryGroup"
:key="label"
:class="
cn(
'flex flex-row gap-4 p-2 rounded-sm my-1',
'my-1 flex flex-row gap-4 rounded-sm p-2',
action &&
'cursor-pointer hover:bg-secondary-background-hover'
)
@@ -63,7 +63,10 @@ defineProps<{
}
"
>
<i v-if="icon" :class="icon" />
<i
v-if="icon"
:class="icon"
/>
{{ label }}
</div>
</section>

View File

@@ -20,10 +20,17 @@ whenever(feedbackRef, () => {
<template>
<Popover>
<template #button>
<Button variant="inverted" class="rounded-full size-12">
<Button
variant="inverted"
class="size-12 rounded-full"
>
<i class="icon-[lucide--circle-question-mark] size-6" />
</Button>
</template>
<div ref="feedbackRef" data-tf-auto-resize :data-tf-widget />
<div
ref="feedbackRef"
data-tf-auto-resize
:data-tf-widget
/>
</Popover>
</template>

View File

@@ -47,7 +47,7 @@ const transform = computed(() => {
<template>
<div
ref="zoomPane"
class="contain-size place-content-center"
class="place-content-center contain-size"
@wheel="handleWheel"
@pointerdown.prevent="handleDown"
@pointermove="handleMove"

View File

@@ -2,11 +2,11 @@ import type { VariantProps } from 'cva'
import { cva } from 'cva'
export const buttonVariants = cva({
base: 'relative inline-flex items-center justify-center gap-2 cursor-pointer whitespace-nowrap appearance-none border-none rounded-md text-sm font-medium font-inter transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
base: 'focus-visible:ring-ring relative inline-flex cursor-pointer appearance-none items-center justify-center gap-2 rounded-md border-none font-inter text-sm font-medium whitespace-nowrap transition-colors focus-visible:ring-1 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
variants: {
variant: {
secondary:
'bg-secondary-background text-secondary-foreground hover:bg-secondary-background-hover',
'text-secondary-foreground bg-secondary-background hover:bg-secondary-background-hover',
primary:
'bg-primary-background text-base-foreground hover:bg-primary-background-hover',
inverted:
@@ -14,11 +14,11 @@ export const buttonVariants = cva({
destructive:
'bg-destructive-background text-base-foreground hover:bg-destructive-background-hover',
textonly:
'text-base-foreground bg-transparent hover:bg-secondary-background-hover',
'bg-transparent text-base-foreground hover:bg-secondary-background-hover',
'muted-textonly':
'text-muted-foreground bg-transparent hover:bg-secondary-background-hover',
'bg-transparent text-muted-foreground hover:bg-secondary-background-hover',
'destructive-textonly':
'text-destructive-background bg-transparent hover:bg-destructive-background/10',
'bg-transparent text-destructive-background hover:bg-destructive-background/10',
'overlay-white': 'bg-white text-gray-600 hover:bg-white/90'
},
size: {

View File

@@ -50,7 +50,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
data-slot="slider-track"
:class="
cn(
'bg-node-stroke relative grow overflow-hidden rounded-full',
'relative grow overflow-hidden rounded-full bg-node-stroke',
'cursor-pointer overflow-visible',
`before:absolute before:-inset-2 before:block before:bg-transparent`,
'data-[orientation=horizontal]:h-0.5 data-[orientation=horizontal]:w-full',
@@ -70,9 +70,9 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
data-slot="slider-thumb"
:class="
cn(
'bg-node-component-surface-highlight ring-node-component-surface-selected block size-3.5 shrink-0 rounded-full shadow-sm transition-[color,box-shadow]',
'block size-3.5 shrink-0 rounded-full bg-node-component-surface-highlight shadow-sm ring-node-component-surface-selected transition-[color,box-shadow]',
'cursor-grab',
'before:absolute before:-inset-1 before:block before:bg-transparent before:rounded-full',
'before:absolute before:-inset-1 before:block before:rounded-full before:bg-transparent',
'hover:ring-2 focus-visible:ring-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50',
{ 'cursor-grabbing': pressed }
)

View File

@@ -3,14 +3,14 @@
:for="inputId"
:class="
cn(
'flex h-10 cursor-text items-center rounded-lg bg-secondary-background text-secondary-foreground hover:bg-secondary-background-hover focus-within:ring-1 focus-within:ring-secondary-foreground',
disabled && 'opacity-50 pointer-events-none'
'text-secondary-foreground focus-within:ring-secondary-foreground flex h-10 cursor-text items-center rounded-lg bg-secondary-background focus-within:ring-1 hover:bg-secondary-background-hover',
disabled && 'pointer-events-none opacity-50'
)
"
>
<button
type="button"
class="flex h-full w-8 cursor-pointer items-center justify-center rounded-l-lg border-none bg-transparent text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-secondary-foreground disabled:opacity-30"
class="focus-visible:ring-secondary-foreground flex h-full w-8 cursor-pointer items-center justify-center rounded-l-lg border-none bg-transparent text-muted-foreground transition-colors hover:text-base-foreground focus-visible:ring-1 focus-visible:outline-none focus-visible:ring-inset disabled:opacity-30"
:disabled="disabled || modelValue <= min"
:aria-label="$t('g.decrement')"
@click="handleStep(-1)"
@@ -28,17 +28,17 @@
type="text"
inputmode="numeric"
:style="{ width: `${inputWidth}ch` }"
class="min-w-0 rounded border-none bg-transparent text-center text-base-foreground font-medium text-lg focus-visible:outline-none"
class="min-w-0 rounded border-none bg-transparent text-center text-lg font-medium text-base-foreground focus-visible:outline-none"
:disabled="disabled"
@input="handleInputChange"
@blur="handleInputBlur"
@focus="handleInputFocus"
/>
>
<slot name="suffix" />
</div>
<button
type="button"
class="flex h-full w-8 cursor-pointer items-center justify-center rounded-r-lg border-none bg-transparent text-muted-foreground transition-colors hover:text-base-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-secondary-foreground disabled:opacity-30"
class="focus-visible:ring-secondary-foreground flex h-full w-8 cursor-pointer items-center justify-center rounded-r-lg border-none bg-transparent text-muted-foreground transition-colors hover:text-base-foreground focus-visible:ring-1 focus-visible:outline-none focus-visible:ring-inset disabled:opacity-30"
:disabled="disabled || modelValue >= max"
:aria-label="$t('g.increment')"
@click="handleStep(1)"

View File

@@ -60,7 +60,7 @@ onClickOutside(rootEl, () => {
cn(
'group relative flex flex-wrap items-center gap-2 rounded-lg bg-transparent p-2 text-xs text-base-foreground',
!internalDisabled &&
'hover:bg-modal-card-background-hovered focus-within:bg-modal-card-background-hovered',
'focus-within:bg-modal-card-background-hovered hover:bg-modal-card-background-hovered',
!disabled && !isEditing && 'cursor-pointer',
className
)
@@ -71,7 +71,7 @@ onClickOutside(rootEl, () => {
<i
v-if="!disabled && !isEditing"
aria-hidden="true"
class="icon-[lucide--square-pen] absolute bottom-2 right-2 size-4 text-muted-foreground"
class="absolute right-2 bottom-2 icon-[lucide--square-pen] size-4 text-muted-foreground"
/>
</TagsInputRoot>
</template>

View File

@@ -39,7 +39,7 @@ onUnmounted(() => {
v-bind="forwardedProps"
:class="
cn(
'min-h-6 flex-1 bg-transparent text-xs text-muted-foreground placeholder:text-muted-foreground focus:outline-none appearance-none border-none',
'min-h-6 flex-1 appearance-none border-none bg-transparent text-xs text-muted-foreground placeholder:text-muted-foreground focus:outline-none',
!isEditing && 'pointer-events-none',
className
)

View File

@@ -17,7 +17,7 @@ const forwardedProps = useForwardProps(restProps)
v-bind="forwardedProps"
:class="
cn(
'flex h-6 items-center gap-1 rounded-sm bg-modal-card-tag-background py-1 pl-2 pr-1 text-modal-card-tag-foreground backdrop-blur-sm ring-offset-base-background data-[state=active]:ring-2 data-[state=active]:ring-base-foreground data-[state=active]:ring-offset-1',
'flex h-6 items-center gap-1 rounded-sm bg-modal-card-tag-background py-1 pr-1 pl-2 text-modal-card-tag-foreground ring-offset-base-background backdrop-blur-sm data-[state=active]:ring-2 data-[state=active]:ring-base-foreground data-[state=active]:ring-offset-1',
className
)
"

View File

@@ -24,7 +24,7 @@ const { t } = useI18n()
:aria-label="t('g.removeTag')"
:class="
cn(
'opacity-60 hover:bg-transparent hover:opacity-100 transition-[opacity,width] duration-150 w-4 data-[disabled]:w-0 data-[disabled]:opacity-0 data-[disabled]:pointer-events-none overflow-hidden',
'w-4 overflow-hidden opacity-60 transition-[opacity,width] duration-150 hover:bg-transparent hover:opacity-100 data-[disabled]:pointer-events-none data-[disabled]:w-0 data-[disabled]:opacity-0',
className
)
"

View File

@@ -1,11 +1,11 @@
<template>
<div class="base-widget-layout rounded-2xl overflow-hidden relative">
<div class="base-widget-layout relative overflow-hidden rounded-2xl">
<Button
v-show="!isRightPanelOpen && hasRightPanel"
size="lg"
:class="
cn('absolute top-4 right-18 z-10', 'transition-opacity duration-200', {
'opacity-0 pointer-events-none': isRightPanelOpen || !hasRightPanel
'pointer-events-none opacity-0': isRightPanelOpen || !hasRightPanel
})
"
@click="toggleRightPanel"
@@ -14,7 +14,7 @@
</Button>
<Button
size="lg"
class="absolute top-4 right-6 z-10 transition-opacity duration-200 w-10"
class="absolute top-4 right-6 z-10 w-10 transition-opacity duration-200"
@click="closeDialog"
>
<i class="pi pi-times" />
@@ -29,18 +29,22 @@
PANEL_SIZES.maxWidth
]"
>
<slot name="leftPanel"></slot>
<slot name="leftPanel" />
</nav>
</Transition>
<div class="flex-1 flex bg-base-background">
<div class="flex flex-1 bg-base-background">
<div class="flex h-full w-full flex-col">
<header
v-if="$slots.header"
class="w-full h-18 px-6 flex items-center justify-between gap-2"
class="flex h-18 w-full items-center justify-between gap-2 px-6"
>
<div class="flex flex-1 shrink-0 gap-2">
<Button v-if="!notMobile" size="icon" @click="toggleLeftPanel">
<Button
v-if="!notMobile"
size="icon"
@click="toggleLeftPanel"
>
<i
:class="
cn(
@@ -51,13 +55,13 @@
"
/>
</Button>
<slot name="header"></slot>
<slot name="header" />
</div>
<slot name="header-right-area"></slot>
<slot name="header-right-area" />
<div
:class="
cn(
'flex justify-end gap-2 w-0',
'flex w-0 justify-end gap-2',
hasRightPanel && !isRightPanelOpen ? 'min-w-22' : 'min-w-10'
)
"
@@ -74,7 +78,7 @@
<main class="flex min-h-0 flex-1 flex-col">
<!-- Fallback title bar when no leftPanel is provided -->
<slot name="contentFilter"></slot>
<slot name="contentFilter" />
<h2
v-if="!$slots.leftPanel"
class="text-xxl m-0 px-6 pt-2 pb-6 capitalize"
@@ -82,17 +86,17 @@
{{ contentTitle }}
</h2>
<div
class="min-h-0 flex-1 px-6 pt-0 pb-10 overflow-y-auto scrollbar-custom"
class="scrollbar-custom min-h-0 flex-1 overflow-y-auto px-6 pt-0 pb-10"
>
<slot name="content"></slot>
<slot name="content" />
</div>
</main>
</div>
<aside
v-if="hasRightPanel && isRightPanelOpen"
class="w-1/4 min-w-40 max-w-80 pt-16 pb-8"
class="w-1/4 max-w-80 min-w-40 pt-16 pb-8"
>
<slot name="rightPanel"></slot>
<slot name="rightPanel" />
</aside>
</div>
</div>

View File

@@ -1,5 +1,8 @@
<template>
<i :class="icon" class="text-neutral text-sm shrink-0" />
<i
:class="icon"
class="text-neutral shrink-0 text-sm"
/>
</template>
<script setup lang="ts">

View File

@@ -5,7 +5,7 @@
disabled: !isOverflowing,
pt: { text: { class: 'whitespace-nowrap' } }
}"
class="flex cursor-pointer items-start gap-2 rounded-md px-4 py-3 text-sm transition-colors text-base-foreground"
class="flex cursor-pointer items-start gap-2 rounded-md px-4 py-3 text-sm text-base-foreground transition-colors"
:class="
active
? 'bg-interface-menu-component-surface-selected'
@@ -15,12 +15,21 @@
@mouseenter="checkOverflow"
@click="onClick"
>
<div v-if="icon" class="pt-0.5">
<div
v-if="icon"
class="pt-0.5"
>
<NavIcon :icon="icon" />
</div>
<i v-else class="text-neutral icon-[lucide--folder] text-xs shrink-0" />
<span ref="textRef" class="min-w-0 truncate">
<slot></slot>
<i
v-else
class="text-neutral icon-[lucide--folder] shrink-0 text-xs"
/>
<span
ref="textRef"
class="min-w-0 truncate"
>
<slot />
</span>
</div>
</template>

View File

@@ -2,7 +2,7 @@
<div
:class="
cn(
'flex items-center justify-between m-0 px-3 py-0 pt-5',
'm-0 flex items-center justify-between px-3 py-0 pt-5',
collapsible && 'cursor-pointer select-none'
)
"
@@ -15,7 +15,7 @@
v-if="collapsible"
:class="
cn(
'pi transition-transform duration-200 text-xs text-text-secondary ',
'pi text-xs text-text-secondary transition-transform duration-200',
isCollapsed ? 'pi-chevron-right' : 'pi-chevron-down'
)
"

View File

@@ -1,173 +1,173 @@
# @ComfyOrg/litegraph
This is the litegraph version used in [ComfyUI_frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
It is a fork of the original `litegraph.js`. Some APIs may by unchanged, however it is largely incompatible with the original.
Some early highlights:
- Accumulated comfyUI custom changes (2024-01 ~ 2024-05) (https://github.com/Comfy-Org/litegraph.js/pull/1)
- Type schema change for ComfyUI_frontend TS migration (https://github.com/Comfy-Org/litegraph.js/pull/3)
- Zoom fix (https://github.com/Comfy-Org/litegraph.js/pull/7)
- Emit search box triggering custom events (<https://github.com/Comfy-Org/litegraph.js/pull/10>)
- Truncate overflowing combo widget text (<https://github.com/Comfy-Org/litegraph.js/pull/17>)
- Sort node based on ID on graph serialization (<https://github.com/Comfy-Org/litegraph.js/pull/21>)
- Fix empty input not used when connecting links (<https://github.com/Comfy-Org/litegraph.js/pull/24>)
- Batch output connection move/disconnect (<https://github.com/Comfy-Org/litegraph.js/pull/39>)
- And now with hundreds more...
# Usage
This library is included as a git subtree in the ComfyUI frontend project at `src/lib/litegraph`.
# litegraph.js
A TypeScript library to create graphs in the browser similar to Unreal Blueprints.
<details>
<summary>Description of the original litegraph.js</summary>
A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs.
It can be integrated easily in any existing web applications and graphs can be run without the need of the editor.
</details>
![Node Graph](imgs/node_graph_example.png 'Node graph example')
## Features
- Renders on Canvas2D (zoom in/out and panning, easy to render complex interfaces, can be used inside a WebGLTexture)
- Easy to use editor (searchbox, keyboard shortcuts, multiple selection, context menu, ...)
- Optimized to support hundreds of nodes per graph (on editor but also on execution)
- Customizable theme (colors, shapes, background)
- Callbacks to personalize every action/drawing/event of nodes
- Graphs can be executed in NodeJS
- Highly customizable nodes (color, shape, widgets, custom rendering)
- Easy to integrate in any JS application (one single file, no dependencies)
- Typescript support
## Integration
This library is integrated as a git subtree in the ComfyUI frontend project. To use it in your code:
```typescript
import { LGraph, LGraphNode, LiteGraph } from '@/lib/litegraph'
```
## How to code a new Node type
Here is an example of how to build a node that sums two inputs:
```ts
import { LiteGraph, LGraphNode } from './litegraph'
class MyAddNode extends LGraphNode {
// Name to show
title = 'Sum'
constructor() {
this.addInput('A', 'number')
this.addInput('B', 'number')
this.addOutput('A+B', 'number')
this.properties.precision = 1
}
// Function to call when the node is executed
onExecute() {
var A = this.getInputData(0)
if (A === undefined) A = 0
var B = this.getInputData(1)
if (B === undefined) B = 0
this.setOutputData(0, A + B)
}
}
// Register the node type
LiteGraph.registerNodeType('basic/sum', MyAddNode)
```
## Server side
It also works server-side using NodeJS although some nodes do not work in server (audio, graphics, input, etc).
```ts
import { LiteGraph, LGraph } from './litegraph.js'
const graph = new LGraph()
const firstNode = LiteGraph.createNode('basic/sum')
graph.add(firstNode)
const secondNode = LiteGraph.createNode('basic/sum')
graph.add(secondNode)
firstNode.connect(0, secondNode, 1)
graph.start()
```
## Projects using it
### [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
![ComfyUI default workflow](https://github.com/comfyanonymous/ComfyUI/blob/6efe561c2a7321501b1b27f47039c7616dda1860/comfyui_screenshot.png 'ComfyUI default workflow')
### Projects using the original litegraph.js
<details>
<summary>Click to expand</summary>
### [webglstudio.org](http://webglstudio.org)
![WebGLStudio](imgs/webglstudio.gif 'WebGLStudio')
### [MOI Elephant](http://moiscript.weebly.com/elephant-systegraveme-nodal.html)
![MOI Elephant](imgs/elephant.gif 'MOI Elephant')
### Mynodes
![MyNodes](imgs/mynodes.png 'MyNodes')
</details>
## Feedback
Please [open an issue](https://github.com/Comfy-Org/litegraph.js/issues/) on the GitHub repo.
# Development
Litegraph has no runtime dependencies. The build tooling has been tested on Node.JS 20.18.x
## Releasing
Use GitHub actions to release normal versions.
1. Run the `Release a New Version` action, selecting the version increment type
1. Merge the resolution PR
1. A GitHub release is automatically published on merge
### Pre-release
The action directly translates `Version increment type` to the pnpm version command. `Pre-release ID (suffix)` is the option for the `--preid` argument.
e.g. Use `prerelease` increment type to automatically bump the patch version and create a pre-release version. Subsequent runs of prerelease will update the prerelease version only.
Use `patch` when ready to remove the pre-release suffix.
## Contributors
You can find the [current list of contributors](https://github.com/Comfy-Org/litegraph.js/graphs/contributors) on GitHub.
### Contributors (pre-fork)
- atlasan
- kriffe
- rappestad
- InventivetalentDev
- NateScarlet
- coderofsalvation
- ilyabesk
- gausszhou
# @ComfyOrg/litegraph
This is the litegraph version used in [ComfyUI_frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
It is a fork of the original `litegraph.js`. Some APIs may by unchanged, however it is largely incompatible with the original.
Some early highlights:
- Accumulated comfyUI custom changes (2024-01 ~ 2024-05) (https://github.com/Comfy-Org/litegraph.js/pull/1)
- Type schema change for ComfyUI_frontend TS migration (https://github.com/Comfy-Org/litegraph.js/pull/3)
- Zoom fix (https://github.com/Comfy-Org/litegraph.js/pull/7)
- Emit search box triggering custom events (<https://github.com/Comfy-Org/litegraph.js/pull/10>)
- Truncate overflowing combo widget text (<https://github.com/Comfy-Org/litegraph.js/pull/17>)
- Sort node based on ID on graph serialization (<https://github.com/Comfy-Org/litegraph.js/pull/21>)
- Fix empty input not used when connecting links (<https://github.com/Comfy-Org/litegraph.js/pull/24>)
- Batch output connection move/disconnect (<https://github.com/Comfy-Org/litegraph.js/pull/39>)
- And now with hundreds more...
# Usage
This library is included as a git subtree in the ComfyUI frontend project at `src/lib/litegraph`.
# litegraph.js
A TypeScript library to create graphs in the browser similar to Unreal Blueprints.
<details>
<summary>Description of the original litegraph.js</summary>
A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs.
It can be integrated easily in any existing web applications and graphs can be run without the need of the editor.
</details>
![Node Graph](imgs/node_graph_example.png 'Node graph example')
## Features
- Renders on Canvas2D (zoom in/out and panning, easy to render complex interfaces, can be used inside a WebGLTexture)
- Easy to use editor (searchbox, keyboard shortcuts, multiple selection, context menu, ...)
- Optimized to support hundreds of nodes per graph (on editor but also on execution)
- Customizable theme (colors, shapes, background)
- Callbacks to personalize every action/drawing/event of nodes
- Graphs can be executed in NodeJS
- Highly customizable nodes (color, shape, widgets, custom rendering)
- Easy to integrate in any JS application (one single file, no dependencies)
- Typescript support
## Integration
This library is integrated as a git subtree in the ComfyUI frontend project. To use it in your code:
```typescript
import { LGraph, LGraphNode, LiteGraph } from '@/lib/litegraph'
```
## How to code a new Node type
Here is an example of how to build a node that sums two inputs:
```ts
import { LiteGraph, LGraphNode } from './litegraph'
class MyAddNode extends LGraphNode {
// Name to show
title = 'Sum'
constructor() {
this.addInput('A', 'number')
this.addInput('B', 'number')
this.addOutput('A+B', 'number')
this.properties.precision = 1
}
// Function to call when the node is executed
onExecute() {
var A = this.getInputData(0)
if (A === undefined) A = 0
var B = this.getInputData(1)
if (B === undefined) B = 0
this.setOutputData(0, A + B)
}
}
// Register the node type
LiteGraph.registerNodeType('basic/sum', MyAddNode)
```
## Server side
It also works server-side using NodeJS although some nodes do not work in server (audio, graphics, input, etc).
```ts
import { LiteGraph, LGraph } from './litegraph.js'
const graph = new LGraph()
const firstNode = LiteGraph.createNode('basic/sum')
graph.add(firstNode)
const secondNode = LiteGraph.createNode('basic/sum')
graph.add(secondNode)
firstNode.connect(0, secondNode, 1)
graph.start()
```
## Projects using it
### [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
![ComfyUI default workflow](https://github.com/comfyanonymous/ComfyUI/blob/6efe561c2a7321501b1b27f47039c7616dda1860/comfyui_screenshot.png 'ComfyUI default workflow')
### Projects using the original litegraph.js
<details>
<summary>Click to expand</summary>
### [webglstudio.org](http://webglstudio.org)
![WebGLStudio](imgs/webglstudio.gif 'WebGLStudio')
### [MOI Elephant](http://moiscript.weebly.com/elephant-systegraveme-nodal.html)
![MOI Elephant](imgs/elephant.gif 'MOI Elephant')
### Mynodes
![MyNodes](imgs/mynodes.png 'MyNodes')
</details>
## Feedback
Please [open an issue](https://github.com/Comfy-Org/litegraph.js/issues/) on the GitHub repo.
# Development
Litegraph has no runtime dependencies. The build tooling has been tested on Node.JS 20.18.x
## Releasing
Use GitHub actions to release normal versions.
1. Run the `Release a New Version` action, selecting the version increment type
1. Merge the resolution PR
1. A GitHub release is automatically published on merge
### Pre-release
The action directly translates `Version increment type` to the pnpm version command. `Pre-release ID (suffix)` is the option for the `--preid` argument.
e.g. Use `prerelease` increment type to automatically bump the patch version and create a pre-release version. Subsequent runs of prerelease will update the prerelease version only.
Use `patch` when ready to remove the pre-release suffix.
## Contributors
You can find the [current list of contributors](https://github.com/Comfy-Org/litegraph.js/graphs/contributors) on GitHub.
### Contributors (pre-fork)
- atlasan
- kriffe
- rappestad
- InventivetalentDev
- NateScarlet
- coderofsalvation
- ilyabesk
- gausszhou

View File

@@ -1,11 +1,11 @@
<template>
<div class="absolute left-2 bottom-2 flex flex-wrap justify-start gap-1">
<div class="absolute bottom-2 left-2 flex flex-wrap justify-start gap-1">
<span
v-for="badge in badges"
:key="badge.label"
:class="
cn(
'px-2 py-1 rounded text-xs font-bold uppercase tracking-wider text-modal-card-tag-foreground bg-modal-card-tag-background break-all'
'rounded bg-modal-card-tag-background px-2 py-1 text-xs font-bold tracking-wider break-all text-modal-card-tag-foreground uppercase'
)
"
>

View File

@@ -7,9 +7,9 @@
:tabindex="interactive ? 0 : -1"
:class="
cn(
'rounded-2xl overflow-hidden transition-all duration-200 bg-modal-card-background p-2 gap-2 flex flex-col h-full',
'flex h-full flex-col gap-2 overflow-hidden rounded-2xl bg-modal-card-background p-2 transition-all duration-200',
interactive &&
'group appearance-none bg-transparent m-0 outline-none text-left hover:bg-secondary-background focus:bg-secondary-background border-none focus:outline-solid outline-base-foreground outline-4'
'group m-0 appearance-none border-none bg-transparent text-left outline-4 outline-base-foreground outline-none hover:bg-secondary-background focus:bg-secondary-background focus:outline-solid'
)
"
@keydown.enter.self="interactive && $emit('select', asset)"
@@ -25,22 +25,25 @@
v-else
:src="asset.preview_url"
:alt="displayName"
class="size-full object-cover cursor-pointer"
class="size-full cursor-pointer object-cover"
role="button"
@click.self="interactive && $emit('select', asset)"
/>
>
<AssetBadgeGroup :badges="asset.badges" />
<IconGroup
v-if="showAssetOptions"
:class="
cn(
'absolute top-2 right-2 invisible group-hover:visible',
'invisible absolute top-2 right-2 group-hover:visible',
dropdownMenuButton?.isOpen && 'visible'
)
"
>
<MoreButton ref="dropdown-menu-button" size="sm">
<MoreButton
ref="dropdown-menu-button"
size="sm"
>
<template #default>
<Button
v-if="flags.assetRenameEnabled"
@@ -66,13 +69,13 @@
</MoreButton>
</IconGroup>
</div>
<div class="max-h-32 flex flex-col gap-2 justify-between flex-auto">
<div class="flex max-h-32 flex-auto flex-col justify-between gap-2">
<h3
:id="titleId"
v-tooltip.top="{ value: displayName, showDelay: tooltipDelay }"
:class="
cn(
'mb-2 m-0 text-base font-semibold line-clamp-2 wrap-anywhere',
'm-0 mb-2 line-clamp-2 text-base font-semibold wrap-anywhere',
'text-base-foreground'
)
"
@@ -90,22 +93,31 @@
v-tooltip.top="{ value: asset.description, showDelay: tooltipDelay }"
:class="
cn(
'm-0 text-sm leading-6 overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [display:-webkit-box] text-muted-foreground'
'm-0 [display:-webkit-box] overflow-hidden text-sm leading-6 text-muted-foreground [-webkit-box-orient:vertical] [-webkit-line-clamp:2]'
)
"
>
{{ asset.description }}
</p>
<div class="flex gap-4 text-xs text-muted-foreground mt-auto">
<span v-if="asset.stats.stars" class="flex items-center gap-1">
<div class="mt-auto flex gap-4 text-xs text-muted-foreground">
<span
v-if="asset.stats.stars"
class="flex items-center gap-1"
>
<i class="icon-[lucide--star] size-3" />
{{ asset.stats.stars }}
</span>
<span v-if="asset.stats.downloadCount" class="flex items-center gap-1">
<span
v-if="asset.stats.downloadCount"
class="flex items-center gap-1"
>
<i class="icon-[lucide--download] size-3" />
{{ asset.stats.downloadCount }}
</span>
<span v-if="asset.stats.formattedDate" class="flex items-center gap-1">
<span
v-if="asset.stats.formattedDate"
class="flex items-center gap-1"
>
<i class="icon-[lucide--clock] size-3" />
{{ asset.stats.formattedDate }}
</span>
@@ -192,7 +204,7 @@ function confirmDeletion() {
confirmText: t('g.delete'),
// TODO: These need to be put into the new Button Variants once we have them.
confirmClass: cn(
'bg-danger-200 text-base-foreground hover:bg-danger-200/80 focus:bg-danger-200/80 focus:ring ring-base-foreground'
'bg-danger-200 text-base-foreground ring-base-foreground hover:bg-danger-200/80 focus:bg-danger-200/80 focus:ring'
),
optionsDisabled,
onCancel: () => {

View File

@@ -1,10 +1,10 @@
<template>
<div
class="flex gap-4 items-center justify-between px-6 pt-2 pb-6"
class="flex items-center justify-between gap-4 px-6 pt-2 pb-6"
data-component-id="asset-filter-bar"
>
<div
class="flex gap-4 items-center"
class="flex items-center gap-4"
data-component-id="asset-filter-bar-left"
>
<MultiSelect
@@ -38,7 +38,10 @@
/>
</div>
<div class="flex items-center" data-component-id="asset-filter-bar-right">
<div
class="flex items-center"
data-component-id="asset-filter-bar-right"
>
<SingleSelect
v-model="sortBy"
:label="$t('assetBrowser.sortBy')"

Some files were not shown because too many files have changed in this diff Show More