mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Component: Button migration 1: TextButton (#7537)
## Summary Setup the variants and migrate existing uses of TextButton/TextIconButton/IconButton to a single Button component. Still a work in progress. ## Changes - **What**: Add a new Button - **What**: Migrate old buttons - **What**: Delete old buttons - **Dependencies**: CVA, upgrade Storybook ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7537-WIP-Component-Button-migration-2cb6d73d36508156a81bfc7bbddb36e9) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
@@ -161,6 +161,7 @@
|
|||||||
"algoliasearch": "catalog:",
|
"algoliasearch": "catalog:",
|
||||||
"axios": "catalog:",
|
"axios": "catalog:",
|
||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
|
"cva": "catalog:",
|
||||||
"dompurify": "^3.2.5",
|
"dompurify": "^3.2.5",
|
||||||
"dotenv": "catalog:",
|
"dotenv": "catalog:",
|
||||||
"es-toolkit": "^1.39.9",
|
"es-toolkit": "^1.39.9",
|
||||||
|
|||||||
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
@@ -138,6 +138,9 @@ catalogs:
|
|||||||
cross-env:
|
cross-env:
|
||||||
specifier: ^10.1.0
|
specifier: ^10.1.0
|
||||||
version: 10.1.0
|
version: 10.1.0
|
||||||
|
cva:
|
||||||
|
specifier: 1.0.0-beta.4
|
||||||
|
version: 1.0.0-beta.4
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.6.1
|
version: 16.6.1
|
||||||
@@ -419,6 +422,9 @@ importers:
|
|||||||
chart.js:
|
chart.js:
|
||||||
specifier: ^4.5.0
|
specifier: ^4.5.0
|
||||||
version: 4.5.0
|
version: 4.5.0
|
||||||
|
cva:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 1.0.0-beta.4(typescript@5.9.2)
|
||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.5
|
version: 3.2.5
|
||||||
@@ -4669,6 +4675,14 @@ packages:
|
|||||||
csstype@3.2.3:
|
csstype@3.2.3:
|
||||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||||
|
|
||||||
|
cva@1.0.0-beta.4:
|
||||||
|
resolution: {integrity: sha512-F/JS9hScapq4DBVQXcK85l9U91M6ePeXoBMSp7vypzShoefUBxjQTo3g3935PUHgQd+IW77DjbPRIxugy4/GCQ==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '>= 4.5.5'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -12808,6 +12822,12 @@ snapshots:
|
|||||||
|
|
||||||
csstype@3.2.3: {}
|
csstype@3.2.3: {}
|
||||||
|
|
||||||
|
cva@1.0.0-beta.4(typescript@5.9.2):
|
||||||
|
dependencies:
|
||||||
|
clsx: 2.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.9.2
|
||||||
|
|
||||||
data-urls@5.0.0:
|
data-urls@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
whatwg-mimetype: 4.0.0
|
whatwg-mimetype: 4.0.0
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ catalog:
|
|||||||
algoliasearch: ^5.21.0
|
algoliasearch: ^5.21.0
|
||||||
axios: ^1.8.2
|
axios: ^1.8.2
|
||||||
cross-env: ^10.1.0
|
cross-env: ^10.1.0
|
||||||
|
cva: 1.0.0-beta.4
|
||||||
dotenv: ^16.4.5
|
dotenv: ^16.4.5
|
||||||
eslint: ^9.39.1
|
eslint: ^9.39.1
|
||||||
eslint-config-prettier: ^10.1.8
|
eslint-config-prettier: ^10.1.8
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
||||||
|
|
||||||
import TextButton from './TextButton.vue'
|
|
||||||
|
|
||||||
const meta: Meta<typeof TextButton> = {
|
|
||||||
title: 'Components/Button/TextButton',
|
|
||||||
component: TextButton,
|
|
||||||
tags: ['autodocs'],
|
|
||||||
argTypes: {
|
|
||||||
label: {
|
|
||||||
control: 'text',
|
|
||||||
defaultValue: 'Click me'
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
control: { type: 'select' },
|
|
||||||
options: ['sm', 'md'],
|
|
||||||
defaultValue: 'md'
|
|
||||||
},
|
|
||||||
border: {
|
|
||||||
control: 'boolean',
|
|
||||||
description: 'Toggle border attribute'
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
control: 'boolean',
|
|
||||||
description: 'Toggle disable status'
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
control: { type: 'select' },
|
|
||||||
options: ['primary', 'secondary', 'transparent'],
|
|
||||||
defaultValue: 'primary'
|
|
||||||
},
|
|
||||||
onClick: { action: 'clicked' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default meta
|
|
||||||
type Story = StoryObj<typeof meta>
|
|
||||||
|
|
||||||
export const Primary: Story = {
|
|
||||||
args: {
|
|
||||||
label: 'Primary Button',
|
|
||||||
type: 'primary',
|
|
||||||
size: 'md'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Secondary: Story = {
|
|
||||||
args: {
|
|
||||||
label: 'Secondary Button',
|
|
||||||
type: 'secondary',
|
|
||||||
size: 'md'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Transparent: Story = {
|
|
||||||
args: {
|
|
||||||
label: 'Transparent Button',
|
|
||||||
type: 'transparent',
|
|
||||||
size: 'md'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Small: Story = {
|
|
||||||
args: {
|
|
||||||
label: 'Small Button',
|
|
||||||
type: 'primary',
|
|
||||||
size: 'sm'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AllVariants: Story = {
|
|
||||||
render: () => ({
|
|
||||||
components: { TextButton },
|
|
||||||
template: `
|
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<TextButton label="Primary Small" type="primary" size="sm" @click="() => {}" />
|
|
||||||
<TextButton label="Primary Medium" type="primary" size="md" @click="() => {}" />
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<TextButton label="Secondary Small" type="secondary" size="sm" @click="() => {}" />
|
|
||||||
<TextButton label="Secondary Medium" type="secondary" size="md" @click="() => {}" />
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<TextButton label="Transparent Small" type="transparent" size="sm" @click="() => {}" />
|
|
||||||
<TextButton label="Transparent Medium" type="transparent" size="md" @click="() => {}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Button
|
|
||||||
v-bind="$attrs"
|
|
||||||
unstyled
|
|
||||||
:class="buttonStyle"
|
|
||||||
:disabled="disabled"
|
|
||||||
@click="onClick"
|
|
||||||
>
|
|
||||||
<span>{{ label }}</span>
|
|
||||||
</Button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import Button from 'primevue/button'
|
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
import type { BaseButtonProps } from '@/types/buttonTypes'
|
|
||||||
import {
|
|
||||||
getBaseButtonClasses,
|
|
||||||
getBorderButtonTypeClasses,
|
|
||||||
getButtonSizeClasses,
|
|
||||||
getButtonTypeClasses
|
|
||||||
} from '@/types/buttonTypes'
|
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
|
||||||
|
|
||||||
interface TextButtonProps extends BaseButtonProps {
|
|
||||||
label: string
|
|
||||||
onClick: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
inheritAttrs: false
|
|
||||||
})
|
|
||||||
|
|
||||||
const {
|
|
||||||
size = 'md',
|
|
||||||
type = 'primary',
|
|
||||||
border = false,
|
|
||||||
disabled = false,
|
|
||||||
class: className,
|
|
||||||
label,
|
|
||||||
onClick
|
|
||||||
} = defineProps<TextButtonProps>()
|
|
||||||
|
|
||||||
const buttonStyle = computed(() => {
|
|
||||||
const baseClasses = getBaseButtonClasses()
|
|
||||||
const sizeClasses = getButtonSizeClasses(size)
|
|
||||||
const typeClasses = border
|
|
||||||
? getBorderButtonTypeClasses(type)
|
|
||||||
: getButtonTypeClasses(type)
|
|
||||||
|
|
||||||
return cn(baseClasses, sizeClasses, typeClasses, className)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="w-full flex gap-2 justify-end px-2 pb-2">
|
<section class="w-full flex gap-2 justify-end px-2 pb-2">
|
||||||
<TextButton
|
<Button :disabled variant="textonly" autofocus @click="$emit('cancel')">
|
||||||
:label="cancelTextX"
|
{{ cancelTextX }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
:disabled
|
:disabled
|
||||||
type="transparent"
|
variant="textonly"
|
||||||
autofocus
|
|
||||||
@click="$emit('cancel')"
|
|
||||||
/>
|
|
||||||
<TextButton
|
|
||||||
:label="confirmTextX"
|
|
||||||
:disabled
|
|
||||||
type="transparent"
|
|
||||||
:class="confirmClass"
|
:class="confirmClass"
|
||||||
@click="$emit('confirm')"
|
@click="$emit('confirm')"
|
||||||
/>
|
>
|
||||||
|
{{ confirmTextX }}
|
||||||
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -21,7 +18,7 @@ import { computed, toValue } from 'vue'
|
|||||||
import type { MaybeRefOrGetter } from 'vue'
|
import type { MaybeRefOrGetter } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
|||||||
@@ -18,22 +18,16 @@
|
|||||||
<i class="icon-[lucide--info]"></i>
|
<i class="icon-[lucide--info]"></i>
|
||||||
</template>
|
</template>
|
||||||
</IconTextButton>
|
</IconTextButton>
|
||||||
<TextButton
|
<Button variant="secondary" size="md" @click="handleGotItClick">{{
|
||||||
:label="$t('missingNodes.cloud.gotIt')"
|
$t('missingNodes.cloud.gotIt')
|
||||||
type="secondary"
|
}}</Button>
|
||||||
size="md"
|
|
||||||
@click="handleGotItClick"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- OSS mode: Open Manager + Install All buttons -->
|
<!-- OSS mode: Open Manager + Install All buttons -->
|
||||||
<div v-else-if="showManagerButtons" class="flex justify-end gap-1 py-2 px-4">
|
<div v-else-if="showManagerButtons" class="flex justify-end gap-1 py-2 px-4">
|
||||||
<TextButton
|
<Button variant="textonly" size="sm" @click="openManager">{{
|
||||||
:label="$t('g.openManager')"
|
$t('g.openManager')
|
||||||
type="transparent"
|
}}</Button>
|
||||||
size="sm"
|
|
||||||
@click="openManager"
|
|
||||||
/>
|
|
||||||
<PackInstallButton
|
<PackInstallButton
|
||||||
v-if="showInstallAllButton"
|
v-if="showInstallAllButton"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
@@ -57,7 +51,7 @@ import { computed, nextTick, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { isCloud } from '@/platform/distribution/types'
|
import { isCloud } from '@/platform/distribution/types'
|
||||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
// Option row hover and focus tone
|
// Option row hover and focus tone
|
||||||
option: ({ context }: MultiSelectPassThroughMethodOptions) => ({
|
option: ({ context }: MultiSelectPassThroughMethodOptions) => ({
|
||||||
class: cn(
|
class: cn(
|
||||||
'flex gap-2 items-center h-10 px-2 rounded-lg',
|
'flex gap-2 items-center h-10 px-2 rounded-lg cursor-pointer',
|
||||||
'hover:bg-secondary-background-hover',
|
'hover:bg-secondary-background-hover',
|
||||||
// Add focus/highlight state for keyboard navigation
|
// Add focus/highlight state for keyboard navigation
|
||||||
context?.focused &&
|
context?.focused &&
|
||||||
@@ -112,14 +112,14 @@
|
|||||||
: $t('g.itemSelected', { selectedCount })
|
: $t('g.itemSelected', { selectedCount })
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
<TextButton
|
<Button
|
||||||
v-if="showClearButton"
|
v-if="showClearButton"
|
||||||
:label="$t('g.clearAll')"
|
variant="textonly"
|
||||||
type="transparent"
|
size="md"
|
||||||
size="fit-content"
|
|
||||||
class="text-sm text-text-primary"
|
|
||||||
@click.stop="selectedItems = []"
|
@click.stop="selectedItems = []"
|
||||||
/>
|
>
|
||||||
|
{{ $t('g.clearAll') }}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-4 h-px bg-border-default"></div>
|
<div class="my-4 h-px bg-border-default"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,9 +145,13 @@
|
|||||||
|
|
||||||
<!-- Custom option row: square checkbox + label (unchanged layout/colors) -->
|
<!-- Custom option row: square checkbox + label (unchanged layout/colors) -->
|
||||||
<template #option="slotProps">
|
<template #option="slotProps">
|
||||||
<div class="flex items-center gap-2" :style="popoverStyle">
|
<div
|
||||||
|
role="button"
|
||||||
|
class="flex items-center gap-2 cursor-pointer"
|
||||||
|
:style="popoverStyle"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="flex h-4 w-4 shrink-0 items-center justify-center rounded p-0.5 transition-all duration-200"
|
class="flex size-4 shrink-0 items-center justify-center rounded p-0.5 transition-all duration-200"
|
||||||
:class="
|
:class="
|
||||||
slotProps.selected
|
slotProps.selected
|
||||||
? 'bg-primary-background'
|
? 'bg-primary-background'
|
||||||
@@ -159,11 +163,9 @@
|
|||||||
class="text-bold icon-[lucide--check] text-xs text-white"
|
class="text-bold icon-[lucide--check] text-xs text-white"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<span>
|
||||||
class="border-none bg-transparent text-left outline-none"
|
{{ slotProps.option.name }}
|
||||||
unstyled
|
</span>
|
||||||
>{{ slotProps.option.name }}</Button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MultiSelect>
|
</MultiSelect>
|
||||||
@@ -172,17 +174,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useFuse } from '@vueuse/integrations/useFuse'
|
import { useFuse } from '@vueuse/integrations/useFuse'
|
||||||
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
import type { UseFuseOptions } from '@vueuse/integrations/useFuse'
|
||||||
import Button from 'primevue/button'
|
|
||||||
import type { MultiSelectPassThroughMethodOptions } from 'primevue/multiselect'
|
import type { MultiSelectPassThroughMethodOptions } from 'primevue/multiselect'
|
||||||
import MultiSelect from 'primevue/multiselect'
|
import MultiSelect from 'primevue/multiselect'
|
||||||
import { computed, useAttrs } from 'vue'
|
import { computed, useAttrs } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import SearchBox from '@/components/common/SearchBox.vue'
|
import SearchBox from '@/components/common/SearchBox.vue'
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { usePopoverSizing } from '@/composables/usePopoverSizing'
|
import { usePopoverSizing } from '@/composables/usePopoverSizing'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
import TextButton from '../button/TextButton.vue'
|
|
||||||
import type { SelectOption } from './types'
|
import type { SelectOption } from './types'
|
||||||
|
|
||||||
type Option = SelectOption
|
type Option = SelectOption
|
||||||
|
|||||||
@@ -80,12 +80,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TextButton
|
<Button
|
||||||
class="h-6 min-w-[120px] flex-1 px-2 py-0 text-[12px]"
|
class="min-w-30 flex-1 px-2 py-0"
|
||||||
type="secondary"
|
variant="secondary"
|
||||||
:label="t('sideToolbar.queueProgressOverlay.viewAllJobs')"
|
size="sm"
|
||||||
@click="$emit('viewAllJobs')"
|
@click="$emit('viewAllJobs')"
|
||||||
/>
|
>
|
||||||
|
{{ t('sideToolbar.queueProgressOverlay.viewAllJobs') }}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -95,7 +97,7 @@ import { computed } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|||||||
@@ -31,20 +31,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="flex items-center justify-end px-4 py-4">
|
<footer class="flex items-center justify-end px-4 py-4">
|
||||||
<div class="flex items-center gap-4 text-[14px] leading-none">
|
<div class="flex items-center gap-4 leading-none">
|
||||||
<TextButton
|
<Button variant="muted-textonly" size="lg" @click="onCancel">
|
||||||
class="min-h-[24px] px-1 py-1 text-[14px] leading-[1] text-text-secondary hover:text-text-primary"
|
{{ t('g.cancel') }}
|
||||||
type="transparent"
|
</Button>
|
||||||
:label="t('g.cancel')"
|
<Button
|
||||||
@click="onCancel"
|
variant="secondary"
|
||||||
/>
|
size="lg"
|
||||||
<TextButton
|
|
||||||
class="min-h-[32px] px-4 py-2 text-[12px] font-normal leading-[1]"
|
|
||||||
type="secondary"
|
|
||||||
:label="t('g.clear')"
|
|
||||||
:disabled="isClearing"
|
:disabled="isClearing"
|
||||||
@click="onConfirm"
|
@click="onConfirm"
|
||||||
/>
|
>{{ t('g.clear') }}</Button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
@@ -55,7 +52,7 @@ import { ref } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { useQueueStore } from '@/stores/queueStore'
|
import { useQueueStore } from '@/stores/queueStore'
|
||||||
|
|||||||
@@ -2,17 +2,16 @@
|
|||||||
<div class="flex items-center justify-between gap-2 px-3">
|
<div class="flex items-center justify-between gap-2 px-3">
|
||||||
<div class="min-w-0 flex-1 overflow-x-auto">
|
<div class="min-w-0 flex-1 overflow-x-auto">
|
||||||
<div class="inline-flex items-center gap-1 whitespace-nowrap">
|
<div class="inline-flex items-center gap-1 whitespace-nowrap">
|
||||||
<TextButton
|
<Button
|
||||||
v-for="tab in visibleJobTabs"
|
v-for="tab in visibleJobTabs"
|
||||||
:key="tab"
|
:key="tab"
|
||||||
class="h-6 px-3 py-1 text-[12px] leading-none hover:opacity-90"
|
:variant="selectedJobTab === tab ? 'secondary' : 'muted-textonly'"
|
||||||
:type="selectedJobTab === tab ? 'secondary' : 'transparent'"
|
size="sm"
|
||||||
:class="[
|
class="px-3"
|
||||||
selectedJobTab === tab ? 'text-text-primary' : 'text-text-secondary'
|
|
||||||
]"
|
|
||||||
:label="tabLabel(tab)"
|
|
||||||
@click="$emit('update:selectedJobTab', tab)"
|
@click="$emit('update:selectedJobTab', tab)"
|
||||||
/>
|
>
|
||||||
|
{{ tabLabel(tab) }}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-2 flex shrink-0 items-center gap-2">
|
<div class="ml-2 flex shrink-0 items-center gap-2">
|
||||||
@@ -155,7 +154,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { jobSortModes, jobTabs } from '@/composables/queue/useJobList'
|
import { jobSortModes, jobTabs } from '@/composables/queue/useJobList'
|
||||||
import type { JobSortMode, JobTab } from '@/composables/queue/useJobList'
|
import type { JobSortMode, JobTab } from '@/composables/queue/useJobList'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
|
|||||||
@@ -154,14 +154,14 @@
|
|||||||
>
|
>
|
||||||
<i class="icon-[lucide--x] size-4" />
|
<i class="icon-[lucide--x] size-4" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<TextButton
|
<Button
|
||||||
v-else-if="props.state === 'completed'"
|
v-else-if="props.state === 'completed'"
|
||||||
class="h-6 transform gap-1 rounded bg-modal-card-button-surface px-2 py-0 text-text-primary transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
class="transform bg-modal-card-button-surface px-2 py-0 transition duration-150 ease-in-out hover:-translate-y-px hover:opacity-95"
|
||||||
type="transparent"
|
variant="textonly"
|
||||||
:label="t('menuLabels.View')"
|
size="sm"
|
||||||
:aria-label="t('menuLabels.View')"
|
|
||||||
@click.stop="emit('view')"
|
@click.stop="emit('view')"
|
||||||
/>
|
>{{ t('menuLabels.View') }}</Button
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
v-if="props.showMenu !== undefined ? props.showMenu : true"
|
v-if="props.showMenu !== undefined ? props.showMenu : true"
|
||||||
v-tooltip.top="moreTooltipConfig"
|
v-tooltip.top="moreTooltipConfig"
|
||||||
@@ -204,9 +204,9 @@ import { computed, nextTick, ref, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
|
||||||
import JobDetailsPopover from '@/components/queue/job/JobDetailsPopover.vue'
|
import JobDetailsPopover from '@/components/queue/job/JobDetailsPopover.vue'
|
||||||
import QueueAssetPreview from '@/components/queue/job/QueueAssetPreview.vue'
|
import QueueAssetPreview from '@/components/queue/job/QueueAssetPreview.vue'
|
||||||
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
import { buildTooltipConfig } from '@/composables/useTooltipConfig'
|
||||||
import type { JobState } from '@/types/queue'
|
import type { JobState } from '@/types/queue'
|
||||||
import { iconForJobState } from '@/utils/queueDisplay'
|
import { iconForJobState } from '@/utils/queueDisplay'
|
||||||
|
|||||||
@@ -112,18 +112,20 @@
|
|||||||
>
|
>
|
||||||
<div class="flex-1 pl-4">
|
<div class="flex-1 pl-4">
|
||||||
<div ref="selectionCountButtonRef" class="inline-flex w-48">
|
<div ref="selectionCountButtonRef" class="inline-flex w-48">
|
||||||
<TextButton
|
<Button
|
||||||
:label="
|
variant="secondary"
|
||||||
|
size="lg"
|
||||||
|
:class="cn(isCompact && 'text-left')"
|
||||||
|
@click="handleDeselectAll"
|
||||||
|
>
|
||||||
|
{{
|
||||||
isHoveringSelectionCount
|
isHoveringSelectionCount
|
||||||
? $t('mediaAsset.selection.deselectAll')
|
? $t('mediaAsset.selection.deselectAll')
|
||||||
: $t('mediaAsset.selection.selectedCount', {
|
: $t('mediaAsset.selection.selectedCount', {
|
||||||
count: totalOutputCount
|
count: totalOutputCount
|
||||||
})
|
})
|
||||||
"
|
}}
|
||||||
type="secondary"
|
</Button>
|
||||||
:class="isCompact ? 'text-left' : ''"
|
|
||||||
@click="handleDeselectAll"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 pr-4">
|
<div class="flex gap-2 pr-4">
|
||||||
@@ -179,10 +181,10 @@ import { Divider } from 'primevue'
|
|||||||
import ProgressSpinner from 'primevue/progressspinner'
|
import ProgressSpinner from 'primevue/progressspinner'
|
||||||
import { useToast } from 'primevue/usetoast'
|
import { useToast } from 'primevue/usetoast'
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
|
||||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||||
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
||||||
import Load3dViewerContent from '@/components/load3d/Load3dViewerContent.vue'
|
import Load3dViewerContent from '@/components/load3d/Load3dViewerContent.vue'
|
||||||
@@ -190,7 +192,7 @@ import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue
|
|||||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||||
import Tab from '@/components/tab/Tab.vue'
|
import Tab from '@/components/tab/Tab.vue'
|
||||||
import TabList from '@/components/tab/TabList.vue'
|
import TabList from '@/components/tab/TabList.vue'
|
||||||
import { t } from '@/i18n'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import MediaAssetCard from '@/platform/assets/components/MediaAssetCard.vue'
|
import MediaAssetCard from '@/platform/assets/components/MediaAssetCard.vue'
|
||||||
import MediaAssetFilterBar from '@/platform/assets/components/MediaAssetFilterBar.vue'
|
import MediaAssetFilterBar from '@/platform/assets/components/MediaAssetFilterBar.vue'
|
||||||
import { useMediaAssets } from '@/platform/assets/composables/media/useMediaAssets'
|
import { useMediaAssets } from '@/platform/assets/composables/media/useMediaAssets'
|
||||||
@@ -203,6 +205,9 @@ import { isCloud } from '@/platform/distribution/types'
|
|||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { ResultItemImpl } from '@/stores/queueStore'
|
import { ResultItemImpl } from '@/stores/queueStore'
|
||||||
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
||||||
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const activeTab = ref<'input' | 'output'>('output')
|
const activeTab = ref<'input' | 'output'>('output')
|
||||||
const folderPromptId = ref<string | null>(null)
|
const folderPromptId = ref<string | null>(null)
|
||||||
|
|||||||
68
src/components/ui/button/Button.stories.ts
Normal file
68
src/components/ui/button/Button.stories.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||||
|
|
||||||
|
import Button from './Button.vue'
|
||||||
|
import { FOR_STORIES } from '@/components/ui/button/button.variants'
|
||||||
|
|
||||||
|
const { variants, sizes } = FOR_STORIES
|
||||||
|
const meta: Meta<typeof Button> = {
|
||||||
|
title: 'Components/Button/Button',
|
||||||
|
component: Button,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
argTypes: {
|
||||||
|
size: {
|
||||||
|
control: { type: 'select' },
|
||||||
|
options: sizes,
|
||||||
|
defaultValue: 'md'
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
control: { type: 'select' },
|
||||||
|
options: variants,
|
||||||
|
defaultValue: 'primary'
|
||||||
|
},
|
||||||
|
as: { defaultValue: 'button' },
|
||||||
|
asChild: { defaultValue: false },
|
||||||
|
default: {
|
||||||
|
defaultValue: 'Button'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
variant: 'secondary',
|
||||||
|
size: 'md',
|
||||||
|
default: 'Button'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default meta
|
||||||
|
type Story = StoryObj<typeof meta>
|
||||||
|
|
||||||
|
export const SingleButton: Story = {
|
||||||
|
args: {
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'lg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateVariants() {
|
||||||
|
const variantButtons: string[] = []
|
||||||
|
for (const variant of variants) {
|
||||||
|
for (const size of sizes) {
|
||||||
|
variantButtons.push(
|
||||||
|
`<Button variant="${variant}" size="${size}">${size === 'icon' ? `<i class="icon-[lucide--settings]" />` : variant}</Button>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return variantButtons
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Keep the number of columns here aligned with the number of sizes above.
|
||||||
|
export const AllVariants: Story = {
|
||||||
|
render: () => ({
|
||||||
|
components: { Button },
|
||||||
|
template: `
|
||||||
|
<div class="grid grid-cols-4 gap-4 place-items-center-safe">
|
||||||
|
${generateVariants().join('\n')}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
}
|
||||||
28
src/components/ui/button/Button.vue
Normal file
28
src/components/ui/button/Button.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PrimitiveProps } from 'reka-ui'
|
||||||
|
import { Primitive } from 'reka-ui'
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
|
import type { ButtonVariants } from './button.variants'
|
||||||
|
import { buttonVariants } from './button.variants'
|
||||||
|
|
||||||
|
interface Props extends PrimitiveProps {
|
||||||
|
variant?: ButtonVariants['variant']
|
||||||
|
size?: ButtonVariants['size']
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}
|
||||||
|
|
||||||
|
const { as = 'button', class: customClass = '' } = defineProps<Props>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Primitive
|
||||||
|
:as
|
||||||
|
:as-child
|
||||||
|
:class="cn(buttonVariants({ variant, size }), customClass)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Primitive>
|
||||||
|
</template>
|
||||||
49
src/components/ui/button/button.variants.ts
Normal file
49
src/components/ui/button/button.variants.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { VariantProps } from 'cva'
|
||||||
|
import { cva } from 'cva'
|
||||||
|
|
||||||
|
export const buttonVariants = cva({
|
||||||
|
base: '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',
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
secondary:
|
||||||
|
'bg-secondary-background text-secondary-foreground hover:bg-secondary-background-hover',
|
||||||
|
primary:
|
||||||
|
'bg-primary-background text-base-foreground hover:bg-primary-background-hover',
|
||||||
|
inverted:
|
||||||
|
'bg-base-foreground text-base-background hover:bg-base-foreground/80',
|
||||||
|
destructive:
|
||||||
|
'bg-destructive-background text-base-foreground hover:bg-destructive-background-hover',
|
||||||
|
textonly:
|
||||||
|
'text-base-foreground bg-transparent hover:bg-secondary-background-hover',
|
||||||
|
'muted-textonly':
|
||||||
|
'text-muted-foreground bg-transparent hover:bg-secondary-background-hover'
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
sm: 'h-6 rounded-sm px-2 py-1 text-xs',
|
||||||
|
md: 'h-8 rounded-lg p-2 text-xs',
|
||||||
|
lg: 'h-10 rounded-lg px-4 py-2 text-sm',
|
||||||
|
icon: 'size-9'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'secondary',
|
||||||
|
size: 'md'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||||
|
|
||||||
|
const variants = [
|
||||||
|
'secondary',
|
||||||
|
'primary',
|
||||||
|
'inverted',
|
||||||
|
'destructive',
|
||||||
|
'textonly',
|
||||||
|
'muted-textonly'
|
||||||
|
] as const satisfies Array<ButtonVariants['variant']>
|
||||||
|
const sizes = ['sm', 'md', 'lg', 'icon'] as const satisfies Array<
|
||||||
|
ButtonVariants['size']
|
||||||
|
>
|
||||||
|
|
||||||
|
export const FOR_STORIES = { variants, sizes } as const
|
||||||
@@ -13,24 +13,26 @@
|
|||||||
<i class="icon-[lucide--circle-question-mark]" />
|
<i class="icon-[lucide--circle-question-mark]" />
|
||||||
</template>
|
</template>
|
||||||
</IconTextButton>
|
</IconTextButton>
|
||||||
<TextButton
|
<Button
|
||||||
v-if="currentStep === 1"
|
v-if="currentStep === 1"
|
||||||
:label="$t('g.cancel')"
|
variant="muted-textonly"
|
||||||
type="transparent"
|
size="lg"
|
||||||
size="md"
|
|
||||||
data-attr="upload-model-step1-cancel-button"
|
data-attr="upload-model-step1-cancel-button"
|
||||||
:disabled="isFetchingMetadata || isUploading"
|
:disabled="isFetchingMetadata || isUploading"
|
||||||
@click="emit('close')"
|
@click="emit('close')"
|
||||||
/>
|
>
|
||||||
<TextButton
|
{{ $t('g.cancel') }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
v-if="currentStep !== 1 && currentStep !== 3"
|
v-if="currentStep !== 1 && currentStep !== 3"
|
||||||
:label="$t('g.back')"
|
variant="muted-textonly"
|
||||||
type="transparent"
|
size="lg"
|
||||||
size="md"
|
|
||||||
:data-attr="`upload-model-step${currentStep}-back-button`"
|
:data-attr="`upload-model-step${currentStep}-back-button`"
|
||||||
:disabled="isFetchingMetadata || isUploading"
|
:disabled="isFetchingMetadata || isUploading"
|
||||||
@click="emit('back')"
|
@click="emit('back')"
|
||||||
/>
|
>
|
||||||
|
{{ $t('g.back') }}
|
||||||
|
</Button>
|
||||||
<span v-else />
|
<span v-else />
|
||||||
|
|
||||||
<IconTextButton
|
<IconTextButton
|
||||||
@@ -65,14 +67,14 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</IconTextButton>
|
</IconTextButton>
|
||||||
<TextButton
|
<Button
|
||||||
v-else-if="currentStep === 3 && uploadStatus === 'success'"
|
v-else-if="currentStep === 3 && uploadStatus === 'success'"
|
||||||
:label="$t('assetBrowser.finish')"
|
variant="secondary"
|
||||||
type="secondary"
|
|
||||||
size="md"
|
|
||||||
data-attr="upload-model-step3-finish-button"
|
data-attr="upload-model-step3-finish-button"
|
||||||
@click="emit('close')"
|
@click="emit('close')"
|
||||||
/>
|
>
|
||||||
|
{{ $t('assetBrowser.finish') }}
|
||||||
|
</Button>
|
||||||
<VideoHelpDialog
|
<VideoHelpDialog
|
||||||
v-model="showVideoHelp"
|
v-model="showVideoHelp"
|
||||||
video-url="https://media.comfy.org/compressed_768/civitai_howto.webm"
|
video-url="https://media.comfy.org/compressed_768/civitai_howto.webm"
|
||||||
@@ -85,7 +87,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import IconTextButton from '@/components/button/IconTextButton.vue'
|
import IconTextButton from '@/components/button/IconTextButton.vue'
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
import VideoHelpDialog from '@/platform/assets/components/VideoHelpDialog.vue'
|
import VideoHelpDialog from '@/platform/assets/components/VideoHelpDialog.vue'
|
||||||
|
|
||||||
const showVideoHelp = ref(false)
|
const showVideoHelp = ref(false)
|
||||||
|
|||||||
@@ -9,23 +9,17 @@
|
|||||||
<i class="icon-[lucide--external-link]" />
|
<i class="icon-[lucide--external-link]" />
|
||||||
<span>{{ $t('g.learnMore') }}</span>
|
<span>{{ $t('g.learnMore') }}</span>
|
||||||
</a>
|
</a>
|
||||||
<TextButton
|
<Button variant="textonly" @click="emit('close')">{{
|
||||||
:label="$t('g.close')"
|
$t('g.close')
|
||||||
type="transparent"
|
}}</Button>
|
||||||
size="md"
|
<Button variant="secondary" @click="emit('subscribe')">
|
||||||
@click="emit('close')"
|
{{ $t('subscription.required.subscribe') }}
|
||||||
/>
|
</Button>
|
||||||
<TextButton
|
|
||||||
:label="$t('subscription.required.subscribe')"
|
|
||||||
type="secondary"
|
|
||||||
size="md"
|
|
||||||
@click="emit('subscribe')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import TextButton from '@/components/button/TextButton.vue'
|
import Button from '@/components/ui/button/Button.vue'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: []
|
close: []
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const ANALYZE_BUNDLE = process.env.ANALYZE_BUNDLE === 'true'
|
|||||||
const VITE_REMOTE_DEV = process.env.VITE_REMOTE_DEV === 'true'
|
const VITE_REMOTE_DEV = process.env.VITE_REMOTE_DEV === 'true'
|
||||||
const DISABLE_TEMPLATES_PROXY = process.env.DISABLE_TEMPLATES_PROXY === 'true'
|
const DISABLE_TEMPLATES_PROXY = process.env.DISABLE_TEMPLATES_PROXY === 'true'
|
||||||
const GENERATE_SOURCEMAP = process.env.GENERATE_SOURCEMAP !== 'false'
|
const GENERATE_SOURCEMAP = process.env.GENERATE_SOURCEMAP !== 'false'
|
||||||
|
const IS_STORYBOOK = process.env.npm_lifecycle_event === 'storybook'
|
||||||
|
|
||||||
// Open Graph / Twitter Meta Tags Constants
|
// Open Graph / Twitter Meta Tags Constants
|
||||||
const VITE_OG_URL = 'https://cloud.comfy.org'
|
const VITE_OG_URL = 'https://cloud.comfy.org'
|
||||||
@@ -53,7 +54,8 @@ const DISTRIBUTION: 'desktop' | 'localhost' | 'cloud' =
|
|||||||
// Disable Vue DevTools for production cloud distribution
|
// Disable Vue DevTools for production cloud distribution
|
||||||
const DISABLE_VUE_PLUGINS =
|
const DISABLE_VUE_PLUGINS =
|
||||||
process.env.DISABLE_VUE_PLUGINS === 'true' ||
|
process.env.DISABLE_VUE_PLUGINS === 'true' ||
|
||||||
(DISTRIBUTION === 'cloud' && !IS_DEV)
|
(DISTRIBUTION === 'cloud' && !IS_DEV) ||
|
||||||
|
IS_STORYBOOK
|
||||||
|
|
||||||
const DEV_SEVER_FALLBACK_URL =
|
const DEV_SEVER_FALLBACK_URL =
|
||||||
DISTRIBUTION === 'cloud'
|
DISTRIBUTION === 'cloud'
|
||||||
|
|||||||
Reference in New Issue
Block a user