From 96fd25de5c2ed7e659f175a7ee1ea4226b3632d6 Mon Sep 17 00:00:00 2001 From: Jin Yi Date: Fri, 6 Mar 2026 17:32:20 +0900 Subject: [PATCH] feat: add Logo C fill and Comfy wave loading indicator components (#9433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Add SVG-based brand loading indicators (LogoCFillLoader, LogoComfyWaveLoader) and use the wave loader as the app loading screen. ## Changes - **What**: New `LogoCFillLoader` (bottom-to-top fill, plays once) and `LogoComfyWaveLoader` (wave water-fill animation) components with `size`, `color`, `bordered`, and `disableAnimation` props. Move all loaders from `components/common/` to `components/loader/`. Use `LogoComfyWaveLoader` in `App.vue` and `WorkspaceAuthGate.vue`. Render loader above BlockUI overlay (z-1200) to prevent dim wash-out. - **Dependencies**: None ## Review Focus - SVG mask-based animation approach using `currentColor` for flexible theming - z-index layering: loader at z-1200 renders above PrimeVue BlockUI's z-1100 modal overlay - `disableAnimation` prop used in WorkspaceAuthGate to show static logo outline during auth loading ## Screenshots (if applicable) [loading_record.webm](https://github.com/user-attachments/assets/b34f7296-9904-4a42-9273-a7d5fda49d15) Storybook stories added for both components under `Components/Loader/`. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9433-feat-add-Logo-C-fill-and-Comfy-wave-loading-indicator-components-31a6d73d3650811cacfdcf867b1f835f) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action --- src/App.vue | 14 +-- .../{common => loader}/Loader.stories.ts | 2 +- src/components/{common => loader}/Loader.vue | 0 .../loader/LogoCFillLoader.stories.ts | 96 +++++++++++++++ src/components/loader/LogoCFillLoader.vue | 100 ++++++++++++++++ .../loader/LogoComfyWaveLoader.stories.ts | 96 +++++++++++++++ src/components/loader/LogoComfyWaveLoader.vue | 111 ++++++++++++++++++ src/components/toast/ProgressToastItem.vue | 2 +- .../components/ActiveMediaAssetCard.vue | 2 +- .../components/AssetExportProgressDialog.vue | 2 +- .../components/ModelImportProgressDialog.vue | 2 +- .../assets/components/UploadModelFooter.vue | 2 +- .../workspace/auth/WorkspaceAuthGate.vue | 4 +- .../extensions/linearMode/LinearControls.vue | 2 +- .../OutputHistoryActiveQueueItem.vue | 2 +- .../widgets/components/WidgetInputText.vue | 2 +- 16 files changed, 421 insertions(+), 18 deletions(-) rename src/components/{common => loader}/Loader.stories.ts (96%) rename src/components/{common => loader}/Loader.vue (100%) create mode 100644 src/components/loader/LogoCFillLoader.stories.ts create mode 100644 src/components/loader/LogoCFillLoader.vue create mode 100644 src/components/loader/LogoComfyWaveLoader.stories.ts create mode 100644 src/components/loader/LogoComfyWaveLoader.vue diff --git a/src/App.vue b/src/App.vue index 90e7e30e76..d8f25a1bf3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,13 +1,13 @@ + + diff --git a/src/components/loader/LogoComfyWaveLoader.stories.ts b/src/components/loader/LogoComfyWaveLoader.stories.ts new file mode 100644 index 0000000000..7057b1a12e --- /dev/null +++ b/src/components/loader/LogoComfyWaveLoader.stories.ts @@ -0,0 +1,96 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' + +import LogoComfyWaveLoader from './LogoComfyWaveLoader.vue' + +const meta: Meta = { + title: 'Components/Loader/LogoComfyWaveLoader', + component: LogoComfyWaveLoader, + tags: ['autodocs'], + parameters: { + layout: 'centered', + backgrounds: { default: 'dark' } + }, + argTypes: { + size: { + control: 'select', + options: ['sm', 'md', 'lg', 'xl'] + }, + color: { + control: 'select', + options: ['yellow', 'blue', 'white', 'black'] + }, + bordered: { + control: 'boolean' + }, + disableAnimation: { + control: 'boolean' + } + } +} + +export default meta +type Story = StoryObj + +export const Default: Story = {} + +export const Small: Story = { + args: { size: 'sm' } +} + +export const Large: Story = { + args: { size: 'lg' } +} + +export const ExtraLarge: Story = { + args: { size: 'xl' } +} + +export const NoBorder: Story = { + args: { bordered: false } +} + +export const Static: Story = { + args: { disableAnimation: true } +} + +export const BrandColors: Story = { + render: () => ({ + components: { LogoComfyWaveLoader }, + template: ` +
+
+ #F0FF41 (Yellow) + +
+
+ #172DD7 (Blue) + +
+
+ White + +
+
+
+ Black + +
+
+
+ ` + }) +} + +export const AllSizes: Story = { + render: () => ({ + components: { LogoComfyWaveLoader }, + template: ` +
+ + + + +
+ ` + }) +} diff --git a/src/components/loader/LogoComfyWaveLoader.vue b/src/components/loader/LogoComfyWaveLoader.vue new file mode 100644 index 0000000000..56f584d2aa --- /dev/null +++ b/src/components/loader/LogoComfyWaveLoader.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/components/toast/ProgressToastItem.vue b/src/components/toast/ProgressToastItem.vue index 89a2bda3d8..2732c5b36f 100644 --- a/src/components/toast/ProgressToastItem.vue +++ b/src/components/toast/ProgressToastItem.vue @@ -2,7 +2,7 @@ import { computed } from 'vue' import { useI18n } from 'vue-i18n' -import Loader from '@/components/common/Loader.vue' +import Loader from '@/components/loader/Loader.vue' import StatusBadge from '@/components/common/StatusBadge.vue' import type { AssetDownload } from '@/stores/assetDownloadStore' import { cn } from '@/utils/tailwindUtil' diff --git a/src/platform/assets/components/ActiveMediaAssetCard.vue b/src/platform/assets/components/ActiveMediaAssetCard.vue index d6ba7c4b61..a87bd9ea55 100644 --- a/src/platform/assets/components/ActiveMediaAssetCard.vue +++ b/src/platform/assets/components/ActiveMediaAssetCard.vue @@ -74,7 +74,7 @@ import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' -import Loader from '@/components/common/Loader.vue' +import Loader from '@/components/loader/Loader.vue' import Button from '@/components/ui/button/Button.vue' import { useJobActions } from '@/composables/queue/useJobActions' import type { JobListItem } from '@/composables/queue/useJobList' diff --git a/src/platform/assets/components/AssetExportProgressDialog.vue b/src/platform/assets/components/AssetExportProgressDialog.vue index 95856fb93c..e28764d730 100644 --- a/src/platform/assets/components/AssetExportProgressDialog.vue +++ b/src/platform/assets/components/AssetExportProgressDialog.vue @@ -2,7 +2,7 @@ import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' -import Loader from '@/components/common/Loader.vue' +import Loader from '@/components/loader/Loader.vue' import HoneyToast from '@/components/honeyToast/HoneyToast.vue' import Button from '@/components/ui/button/Button.vue' import type { AssetExport } from '@/stores/assetExportStore' diff --git a/src/platform/assets/components/ModelImportProgressDialog.vue b/src/platform/assets/components/ModelImportProgressDialog.vue index 80c1716af5..8c02cb8eb4 100644 --- a/src/platform/assets/components/ModelImportProgressDialog.vue +++ b/src/platform/assets/components/ModelImportProgressDialog.vue @@ -4,7 +4,7 @@ import Popover from 'primevue/popover' import { computed, ref } from 'vue' import { useI18n } from 'vue-i18n' -import Loader from '@/components/common/Loader.vue' +import Loader from '@/components/loader/Loader.vue' import HoneyToast from '@/components/honeyToast/HoneyToast.vue' import ProgressToastItem from '@/components/toast/ProgressToastItem.vue' import Button from '@/components/ui/button/Button.vue' diff --git a/src/platform/assets/components/UploadModelFooter.vue b/src/platform/assets/components/UploadModelFooter.vue index 6e8e7741f5..a7d10e76f6 100644 --- a/src/platform/assets/components/UploadModelFooter.vue +++ b/src/platform/assets/components/UploadModelFooter.vue @@ -106,7 +106,7 @@