UI color updates & tweaks (#6381)
## Summary Small updates to the UI to make it more visually distinct from the graph nodes and improving the login button ## Changes - **What**: - Improve theme support with dynamic colors - Standardize surface colors/borders - Add shadows to all floating UI elements - Change side toolbar to be docked by default - Decrease side toolbar width - Change login button to icon only - Update C button to be more distinctive ## Review Focus - Theme tokens, I am not sure what the overall plan for how these tokens will be supported for custom user palettes. I've implemented a method where default variables are chosen that look nice over all existing themes, but they can be overridden. ## Screenshots Dark theme updated color <img width="958" height="615" alt="image" src="https://github.com/user-attachments/assets/a2c540cf-6c05-4ad3-996b-8b7b80df8d2d" /> Themed & updated menu button (active vs hover vs default) <img width="58" height="338" alt="github" src="https://github.com/user-attachments/assets/90244ee2-d5ee-4b26-9d99-f4b8439aa372" /><img width="58" height="338" alt="nord" src="https://github.com/user-attachments/assets/053e8e7b-d639-4b72-92d2-ec7e2167aab8" /><img width="58" height="338" alt="arc" src="https://github.com/user-attachments/assets/3caeb07b-d41b-4d88-83b4-ef8d45d4e5a6" /><img width="58" height="338" alt="solarized" src="https://github.com/user-attachments/assets/6ebf6afb-ec3a-436b-90eb-bda40be1c79f" /><img width="58" height="338" alt="light" src="https://github.com/user-attachments/assets/fbb7f96a-b722-40c5-86fa-a0732e166972" /><img width="58" height="338" alt="dark" src="https://github.com/user-attachments/assets/5459208b-9256-4c55-9373-169e9cd9f09a" /> With labels <img width="58" height="394" alt="labels" src="https://github.com/user-attachments/assets/f97ee354-35cd-42b8-896f-57ac39644c1d" /> Logged out (only visible on desktop) <img width="323" height="48" alt="logged out" src="https://github.com/user-attachments/assets/75b71420-0c1b-446f-8cdd-43c68674d346" /> Logged in <img width="355" height="48" alt="logged in" src="https://github.com/user-attachments/assets/324fd133-a793-49dd-844a-f93dd5d1effb" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6381-UI-color-updates-tweaks-29b6d73d3650816384d2ef5617a776a0) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: github-actions <github-actions@github.com>
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
@@ -241,6 +241,18 @@
|
||||
--border-subtle: var(--color-smoke-400);
|
||||
--muted-background: var(--color-smoke-700);
|
||||
--accent-background: var(--color-smoke-800);
|
||||
|
||||
/* Default UI element color palette variables */
|
||||
--palette-contrast-mix-color: #fff;
|
||||
--palette-interface-panel-surface: var(--comfy-menu-bg);
|
||||
--palette-interface-stroke: color-mix(in srgb, var(--interface-panel-surface) 75.5%, var(--contrast-mix-color));
|
||||
|
||||
--palette-interface-panel-box-shadow: 1px 1px 8px 0 rgb(0 0 0 / 0.4);
|
||||
--palette-interface-panel-drop-shadow: 1px 1px 4px rgb(0 0 0 / 0.4);
|
||||
--palette-interface-panel-hover-surface: color-mix(in srgb, var(--interface-panel-surface) 92.5%, var(--contrast-mix-color));
|
||||
--palette-interface-panel-selected-surface: color-mix(in srgb, var(--interface-panel-surface) 87.5%, var(--contrast-mix-color));
|
||||
--palette-interface-button-hover-surface: color-mix(in srgb, var(--interface-panel-surface) 82%, var(--contrast-mix-color));
|
||||
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
|
||||
@@ -4,6 +4,13 @@ import { addDynamicIconSelectors } from '@iconify/tailwind'
|
||||
import { iconCollection } from './src/iconCollection'
|
||||
|
||||
export default {
|
||||
theme: {
|
||||
extend: {
|
||||
boxShadow: {
|
||||
interface: 'var(--interface-panel-box-shadow)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
addDynamicIconSelectors({
|
||||
iconSets: {
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"comfy_base": {
|
||||
"fg-color": "#fff",
|
||||
"bg-color": "#202020",
|
||||
"comfy-menu-bg": "#353535",
|
||||
"comfy-menu-bg": "#11141a",
|
||||
"comfy-menu-secondary-bg": "#303030",
|
||||
"comfy-input-bg": "#222",
|
||||
"input-text": "#ddd",
|
||||
|
||||
@@ -68,7 +68,12 @@
|
||||
"content-fg": "#222",
|
||||
"content-hover-bg": "#adadad",
|
||||
"content-hover-fg": "#222",
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.25) 0 0 0.5rem"
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.25) 0 0 0.5rem",
|
||||
"interface-panel-box-shadow": "1px 1px 8px 0 rgba(0, 0, 0, 0.2)",
|
||||
"interface-panel-drop-shadow": "1px 1px 4px rgba(0, 0, 0, 0.4)",
|
||||
"interface-panel-hover-surface": "var(--color-gray-200)",
|
||||
"interface-panel-selected-surface": "color-mix(in srgb, var(--interface-panel-surface) 78%, var(--contrast-mix-color))",
|
||||
"contrast-mix-color": "#000"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="actionbar-container pointer-events-auto mx-1 flex h-12 items-center rounded-lg px-2 shadow-md"
|
||||
class="actionbar-container pointer-events-auto mx-1 flex h-12 items-center rounded-lg border border-[var(--interface-stroke)] px-2 shadow-interface"
|
||||
>
|
||||
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
||||
<div
|
||||
@@ -48,6 +48,5 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.actionbar-container {
|
||||
background-color: var(--comfy-menu-bg);
|
||||
border: 1px solid var(--p-panel-border-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -268,7 +268,9 @@ const panelClass = computed(() =>
|
||||
cn(
|
||||
'actionbar pointer-events-auto z1000',
|
||||
isDragging.value && 'select-none pointer-events-none',
|
||||
isDocked.value ? 'p-0 static mr-2 border-none bg-transparent' : 'fixed'
|
||||
isDocked.value
|
||||
? 'p-0 static mr-2 border-none bg-transparent'
|
||||
: 'fixed shadow-interface'
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="subgraph-breadcrumb w-auto drop-shadow-md"
|
||||
class="subgraph-breadcrumb w-auto drop-shadow-[var(--interface-panel-drop-shadow)]"
|
||||
:class="{
|
||||
'subgraph-breadcrumb-collapse': collapseTabs,
|
||||
'subgraph-breadcrumb-overflow': overflowingTabs
|
||||
@@ -208,8 +208,8 @@ onUpdated(() => {
|
||||
:deep(.p-breadcrumb-separator),
|
||||
:deep(.p-breadcrumb-item) {
|
||||
@apply h-12;
|
||||
border-top: 1px solid var(--p-panel-border-color);
|
||||
border-bottom: 1px solid var(--p-panel-border-color);
|
||||
border-top: 1px solid var(--interface-stroke);
|
||||
border-bottom: 1px solid var(--interface-stroke);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ onUpdated(() => {
|
||||
@apply rounded-l-lg;
|
||||
/* Then collapse the root workflow */
|
||||
flex-shrink: 5000;
|
||||
border-left: 1px solid var(--p-panel-border-color);
|
||||
border-left: 1px solid var(--interface-stroke);
|
||||
|
||||
.p-breadcrumb-item-link {
|
||||
padding-left: var(--p-breadcrumb-item-padding);
|
||||
@@ -232,7 +232,7 @@ onUpdated(() => {
|
||||
@apply rounded-r-lg;
|
||||
/* Then collapse the active item */
|
||||
flex-shrink: 1;
|
||||
border-right: 1px solid var(--p-panel-border-color);
|
||||
border-right: 1px solid var(--interface-stroke);
|
||||
}
|
||||
|
||||
:deep(.p-breadcrumb-item-link:hover),
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('UserAvatar', () => {
|
||||
const avatar = wrapper.findComponent(Avatar)
|
||||
expect(avatar.exists()).toBe(true)
|
||||
expect(avatar.props('image')).toBeNull()
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('renders with default icon when provided photo Url is null', () => {
|
||||
@@ -67,7 +67,7 @@ describe('UserAvatar', () => {
|
||||
const avatar = wrapper.findComponent(Avatar)
|
||||
expect(avatar.exists()).toBe(true)
|
||||
expect(avatar.props('image')).toBeNull()
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('falls back to icon when image fails to load', async () => {
|
||||
@@ -82,7 +82,7 @@ describe('UserAvatar', () => {
|
||||
avatar.vm.$emit('error')
|
||||
await nextTick()
|
||||
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('uses provided ariaLabel', () => {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<Avatar
|
||||
class="bg-gray-200 dark-theme:bg-[var(--interface-panel-selected-surface)]"
|
||||
:image="photoUrl ?? undefined"
|
||||
:icon="hasAvatar ? undefined : 'pi pi-user'"
|
||||
:icon="hasAvatar ? undefined : 'icon-[lucide--user]'"
|
||||
:pt:icon:class="{ 'size-4': !hasAvatar }"
|
||||
shape="circle"
|
||||
:aria-label="ariaLabel ?? $t('auth.login.userAvatar')"
|
||||
@error="handleImageError"
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
></div>
|
||||
|
||||
<ButtonGroup
|
||||
class="absolute right-0 bottom-0 z-[1200] flex-row gap-1 border-[1px] border-node-border bg-interface-panel-surface p-2"
|
||||
:style="stringifiedMinimapStyles.buttonGroupStyles"
|
||||
class="absolute right-0 bottom-0 z-[1200] flex-row gap-1 border-[1px] border-[var(--interface-stroke)] bg-interface-panel-surface p-2"
|
||||
:style="{
|
||||
...stringifiedMinimapStyles.buttonGroupStyles
|
||||
}"
|
||||
@wheel="canvasInteractions.handleWheel"
|
||||
>
|
||||
<CanvasModeSelector
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.8193 0.600586C15.1248 0.600586 15.3296 0.70893 15.459 0.881836C15.5914 1.05888 15.6471 1.33774 15.5527 1.66895L14.8037 4.30176C14.7063 4.64386 14.4729 4.97024 14.1641 5.21191C13.8544 5.45415 13.496 5.58984 13.1699 5.58984H13.1689L9.5791 5.59668H7.90625C7.52654 5.59668 7.19496 5.84986 7.09082 6.21289L5.69434 11.0889C5.63007 11.3133 5.66134 11.5534 5.77734 11.7529L5.83203 11.8359C5.99177 12.0491 6.24252 12.1758 6.50977 12.1758H6.51074L8.88281 12.1709H11.4971C11.7643 12.171 11.9541 12.254 12.084 12.3906L12.1357 12.4521C12.2685 12.6295 12.3249 12.9089 12.2305 13.2402L11.4805 15.8721C11.383 16.2144 11.1498 16.5415 10.8408 16.7832C10.5314 17.0252 10.1736 17.161 9.84766 17.1611H9.84668L6.25684 17.168H3.64258C3.33762 17.1679 3.13349 17.0588 3.00391 16.8857C2.87135 16.7087 2.81482 16.43 2.90918 16.0986L3.39551 14.3887C3.46841 14.1327 3.41794 13.8576 3.25879 13.6445V13.6436C3.09901 13.4303 2.84745 13.3037 2.58008 13.3037H1.18066C0.875088 13.3037 0.670398 13.1953 0.541016 13.0225C0.408483 12.8451 0.351891 12.5655 0.446289 12.2344L2.11914 6.38965L2.30371 5.74707V5.74609C2.40139 5.40341 2.63456 5.07671 2.94336 4.83496C3.25302 4.59258 3.61143 4.45705 3.9375 4.45703H5.6123C5.94484 4.45703 6.24083 4.26316 6.37891 3.9707L6.42773 3.83984L6.98145 1.89551C7.07894 1.55317 7.31212 1.22614 7.62109 0.984375C7.93074 0.742127 8.2892 0.606445 8.61523 0.606445H8.61621L12.1982 0.600586H14.8193Z"
|
||||
:stroke="color"
|
||||
stroke-width="1"
|
||||
v-bind="attributes"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -22,11 +20,18 @@ interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
class?: string
|
||||
mode?: 'outline' | 'fill'
|
||||
}
|
||||
const {
|
||||
size = 16,
|
||||
color = 'currentColor',
|
||||
mode = 'outline',
|
||||
class: className
|
||||
} = defineProps<Props>()
|
||||
const iconClass = computed(() => className || '')
|
||||
const attributes = computed(() => ({
|
||||
stroke: mode === 'outline' ? color : undefined,
|
||||
strokeWidth: mode === 'outline' ? 1 : undefined,
|
||||
fill: mode === 'fill' ? color : 'none'
|
||||
}))
|
||||
</script>
|
||||
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="comfy-menu-button-wrapper flex shrink-0 cursor-pointer flex-col items-center justify-center rounded-t-md p-2 transition-colors"
|
||||
v-tooltip="{
|
||||
value: t('sideToolbar.labels.menu'),
|
||||
showDelay: 300,
|
||||
hideDelay: 300
|
||||
}"
|
||||
class="comfy-menu-button-wrapper flex shrink-0 cursor-pointer flex-col items-center justify-center p-2 transition-colors"
|
||||
:class="{
|
||||
'comfy-menu-button-active': menuRef?.visible
|
||||
}"
|
||||
@click="onLogoMenuClick($event)"
|
||||
>
|
||||
<ComfyLogoTransparent
|
||||
alt="ComfyUI Logo"
|
||||
class="comfyui-logo h-[18px] w-[18px]"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="!isSmall"
|
||||
class="side-bar-button-label mt-1 text-center text-[10px]"
|
||||
>{{ t('sideToolbar.labels.menu') }}</span
|
||||
>
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-black">
|
||||
<ComfyLogo
|
||||
alt="ComfyUI Logo"
|
||||
class="comfyui-logo h-[18px] w-[18px] text-white"
|
||||
mode="fill"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TieredMenu
|
||||
@@ -75,7 +77,7 @@ import { computed, nextTick, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import ComfyLogoTransparent from '@/components/icons/ComfyLogoTransparent.vue'
|
||||
import ComfyLogo from '@/components/icons/ComfyLogo.vue'
|
||||
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
|
||||
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
@@ -97,10 +99,6 @@ const colorPaletteService = useColorPaletteService()
|
||||
const dialogStore = useDialogStore()
|
||||
const managerState = useManagerState()
|
||||
|
||||
const { isSmall = false } = defineProps<{
|
||||
isSmall?: boolean
|
||||
}>()
|
||||
|
||||
const menuRef = ref<
|
||||
({ dirty: boolean } & TieredMenuMethods & TieredMenuState) | null
|
||||
>(null)
|
||||
@@ -292,12 +290,12 @@ const hasActiveStateSiblings = (item: MenuItem): boolean => {
|
||||
}
|
||||
|
||||
.comfy-menu-button-wrapper:hover {
|
||||
background: var(--p-button-text-secondary-hover-background);
|
||||
background: var(--interface-panel-hover-surface);
|
||||
}
|
||||
|
||||
.comfy-menu-button-active,
|
||||
.comfy-menu-button-active:hover {
|
||||
background-color: var(--content-hover-bg);
|
||||
background: var(--interface-panel-selected-surface);
|
||||
}
|
||||
|
||||
.keybinding-tag {
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
'small-sidebar': isSmall,
|
||||
'connected-sidebar': isConnected,
|
||||
'floating-sidebar': !isConnected,
|
||||
'overflowing-sidebar': isOverflowing
|
||||
'overflowing-sidebar': isOverflowing,
|
||||
'border-r border-[var(--interface-stroke)] shadow-interface': isConnected
|
||||
}"
|
||||
>
|
||||
<div
|
||||
@@ -18,7 +19,7 @@
|
||||
"
|
||||
>
|
||||
<div ref="topToolbarRef" :class="groupClasses">
|
||||
<ComfyMenuButton :is-small="isSmall" />
|
||||
<ComfyMenuButton />
|
||||
<SidebarIcon
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@@ -145,7 +146,7 @@ const isOverflowing = ref(false)
|
||||
const groupClasses = computed(() =>
|
||||
cn(
|
||||
'sidebar-item-group pointer-events-auto flex flex-col items-center overflow-hidden flex-shrink-0' +
|
||||
(isConnected.value ? '' : ' rounded-lg shadow-md')
|
||||
(isConnected.value ? '' : ' rounded-lg shadow-interface')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -214,7 +215,7 @@ onMounted(() => {
|
||||
--sidebar-padding: 4px;
|
||||
--sidebar-icon-size: 1rem;
|
||||
|
||||
--sidebar-default-floating-width: 56px;
|
||||
--sidebar-default-floating-width: 48px;
|
||||
--sidebar-default-connected-width: calc(
|
||||
var(--sidebar-default-floating-width) + var(--sidebar-padding) * 2
|
||||
);
|
||||
|
||||
@@ -86,7 +86,11 @@ const computedTooltip = computed(() => t(tooltip) + tooltipSuffix)
|
||||
}
|
||||
|
||||
.side-bar-button-selected {
|
||||
background-color: var(--content-hover-bg);
|
||||
background-color: var(--interface-panel-selected-surface);
|
||||
color: var(--content-hover-fg);
|
||||
}
|
||||
.side-bar-button:hover {
|
||||
background-color: var(--interface-panel-hover-surface);
|
||||
color: var(--content-hover-fg);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
<div>
|
||||
<Button
|
||||
v-if="isLoggedIn"
|
||||
class="user-profile-button p-1"
|
||||
class="user-profile-button p-1 hover:bg-transparent"
|
||||
severity="secondary"
|
||||
text
|
||||
:aria-label="$t('g.currentUser')"
|
||||
@click="popover?.toggle($event)"
|
||||
>
|
||||
<div class="flex items-center rounded-full bg-(--p-content-background)">
|
||||
<div
|
||||
class="flex items-center gap-1 rounded-full hover:bg-[var(--interface-button-hover-surface)]"
|
||||
>
|
||||
<UserAvatar :photo-url="photoURL" />
|
||||
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.5rem' }" />
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.6rem' }" />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<template>
|
||||
<Button
|
||||
v-if="!isLoggedIn"
|
||||
:label="t('auth.login.loginButton')"
|
||||
outlined
|
||||
rounded
|
||||
severity="secondary"
|
||||
class="text-neutral border-black/50 px-4 capitalize dark-theme:border-white/50 dark-theme:text-white"
|
||||
class="size-8 border-black/50 bg-transparent text-black hover:bg-[var(--interface-panel-hover-surface)] dark-theme:border-white/50 dark-theme:text-white"
|
||||
@click="handleSignIn()"
|
||||
@mouseenter="showPopover"
|
||||
@mouseleave="hidePopover"
|
||||
/>
|
||||
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--user] size-4" />
|
||||
</template>
|
||||
</Button>
|
||||
<Popover
|
||||
ref="popoverRef"
|
||||
class="p-2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="workflow-tabs-container flex h-full max-w-full flex-auto flex-row overflow-hidden"
|
||||
class="workflow-tabs-container flex h-full max-w-full flex-auto flex-row overflow-hidden border-b border-[var(--interface-stroke)] shadow-interface"
|
||||
:class="{ 'workflow-tabs-container-desktop': isDesktop }"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -122,7 +122,7 @@ export const CORE_SETTINGS: SettingParams[] = [
|
||||
name: 'Sidebar style',
|
||||
type: 'combo',
|
||||
options: ['floating', 'connected'],
|
||||
defaultValue: 'floating'
|
||||
defaultValue: 'connected'
|
||||
},
|
||||
{
|
||||
id: 'Comfy.TextareaWidget.FontSize',
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="litegraph-minimap relative bg-interface-panel-surface"
|
||||
class="litegraph-minimap relative border border-[var(--interface-stroke)] bg-interface-panel-surface shadow-interface"
|
||||
:style="containerStyles"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="minimap-panel mr-2 flex flex-col gap-2 bg-interface-panel-surface p-3 text-sm"
|
||||
class="minimap-panel mr-2 flex flex-col gap-2 bg-interface-panel-surface p-3 text-sm shadow-interface"
|
||||
:style="panelStyles"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
@@ -57,7 +57,7 @@ const litegraphBaseSchema = z.object({
|
||||
BADGE_BG_COLOR: z.string()
|
||||
})
|
||||
|
||||
const comfyBaseSchema = z.object({
|
||||
export const comfyBaseSchema = z.object({
|
||||
['fg-color']: z.string(),
|
||||
['bg-color']: z.string(),
|
||||
['bg-img']: z.string().optional(),
|
||||
@@ -75,7 +75,15 @@ const comfyBaseSchema = z.object({
|
||||
['content-fg']: z.string(),
|
||||
['content-hover-bg']: z.string(),
|
||||
['content-hover-fg']: z.string(),
|
||||
['bar-shadow']: z.string()
|
||||
['bar-shadow']: z.string(),
|
||||
['contrast-mix-color']: z.string().optional(),
|
||||
['interface-stroke']: z.string().optional(),
|
||||
['interface-panel-surface']: z.string().optional(),
|
||||
['interface-panel-box-shadow']: z.string().optional(),
|
||||
['interface-panel-drop-shadow']: z.string().optional(),
|
||||
['interface-panel-hover-surface']: z.string().optional(),
|
||||
['interface-panel-selected-surface']: z.string().optional(),
|
||||
['interface-button-hover-surface']: z.string().optional()
|
||||
})
|
||||
|
||||
const colorsSchema = z.object({
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { toRaw } from 'vue'
|
||||
import { z } from 'zod'
|
||||
import { fromZodError } from 'zod-validation-error'
|
||||
|
||||
import { downloadBlob } from '@/base/common/downloadUtil'
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { paletteSchema } from '@/schemas/colorPaletteSchema'
|
||||
import { paletteSchema, comfyBaseSchema } from '@/schemas/colorPaletteSchema'
|
||||
import type { Colors, Palette } from '@/schemas/colorPaletteSchema'
|
||||
import { app } from '@/scripts/app'
|
||||
import { uploadFile } from '@/scripts/utils'
|
||||
@@ -158,6 +159,26 @@ export const useColorPaletteService = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets optional keys from a Zod schema object.
|
||||
*
|
||||
* @param schema - The Zod schema object to analyze.
|
||||
* @returns Array of optional key names.
|
||||
*/
|
||||
const getOptionalKeys = (schema: z.ZodObject<any, any>) => {
|
||||
const optionalKeys: string[] = []
|
||||
const shape = schema.shape
|
||||
|
||||
for (const [key, value] of Object.entries(shape)) {
|
||||
if (value instanceof z.ZodOptional || value instanceof z.ZodDefault) {
|
||||
optionalKeys.push(key)
|
||||
}
|
||||
}
|
||||
|
||||
return optionalKeys
|
||||
}
|
||||
const optionalComfyBaseKeys = getOptionalKeys(comfyBaseSchema)
|
||||
|
||||
/**
|
||||
* Loads the Comfy color palette.
|
||||
*
|
||||
@@ -169,6 +190,16 @@ export const useColorPaletteService = () => {
|
||||
for (const [key, value] of Object.entries(comfyColorPalette)) {
|
||||
rootStyle.setProperty('--' + key, value)
|
||||
}
|
||||
|
||||
for (const optionalKey of optionalComfyBaseKeys) {
|
||||
if (!(optionalKey in comfyColorPalette)) {
|
||||
rootStyle.setProperty(
|
||||
'--' + optionalKey,
|
||||
`var(--palette-${optionalKey})`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const backgroundImage = settingStore.get('Comfy.Canvas.BackgroundImage')
|
||||
if (backgroundImage) {
|
||||
rootStyle.setProperty(
|
||||
|
||||