diff --git a/.stylelintrc.json b/.stylelintrc.json index 71a1311ae5..26ce840f1e 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -52,7 +52,8 @@ "reference", "plugin", "custom-variant", - "utility" + "utility", + "source" ] } ], diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 6439071639..9bd03386b4 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -14,6 +14,7 @@ import { ComfyTemplates } from '../helpers/templates' import { ComfyMouse } from './ComfyMouse' import { VueNodeHelpers } from './VueNodeHelpers' import { ComfyNodeSearchBox } from './components/ComfyNodeSearchBox' +import { ComfyNodeSearchBoxV2 } from './components/ComfyNodeSearchBoxV2' import { ContextMenu } from './components/ContextMenu' import { SettingDialog } from './components/SettingDialog' import { BottomPanel } from './components/BottomPanel' @@ -166,6 +167,7 @@ export class ComfyPage { // Components public readonly searchBox: ComfyNodeSearchBox + public readonly searchBoxV2: ComfyNodeSearchBoxV2 public readonly menu: ComfyMenu public readonly actionbar: ComfyActionbar public readonly templates: ComfyTemplates @@ -210,6 +212,7 @@ export class ComfyPage { this.workflowUploadInput = page.locator('#comfy-file-input') this.searchBox = new ComfyNodeSearchBox(page) + this.searchBoxV2 = new ComfyNodeSearchBoxV2(page) this.menu = new ComfyMenu(page) this.actionbar = new ComfyActionbar(page) this.templates = new ComfyTemplates(page) diff --git a/browser_tests/fixtures/components/ComfyNodeSearchBoxV2.ts b/browser_tests/fixtures/components/ComfyNodeSearchBoxV2.ts new file mode 100644 index 0000000000..64f6b9cebb --- /dev/null +++ b/browser_tests/fixtures/components/ComfyNodeSearchBoxV2.ts @@ -0,0 +1,29 @@ +import type { Locator, Page } from '@playwright/test' + +import type { ComfyPage } from '../ComfyPage' + +export class ComfyNodeSearchBoxV2 { + readonly dialog: Locator + readonly input: Locator + readonly results: Locator + readonly filterOptions: Locator + + constructor(readonly page: Page) { + this.dialog = page.getByRole('search') + this.input = this.dialog.locator('input[type="text"]') + this.results = this.dialog.getByTestId('result-item') + this.filterOptions = this.dialog.getByTestId('filter-option') + } + + categoryButton(categoryId: string): Locator { + return this.dialog.getByTestId(`category-${categoryId}`) + } + + filterBarButton(name: string): Locator { + return this.dialog.getByRole('button', { name }) + } + + async reload(comfyPage: ComfyPage) { + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + } +} diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts index eed31cc354..f70a527e3d 100644 --- a/browser_tests/tests/dialog.spec.ts +++ b/browser_tests/tests/dialog.spec.ts @@ -37,7 +37,7 @@ test.describe('Load workflow warning', { tag: '@ui' }, () => { }) test('Does not report warning on undo/redo', async ({ comfyPage }) => { - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'v1 (legacy)') await comfyPage.workflow.loadWorkflow('missing/missing_nodes') await comfyPage.page diff --git a/browser_tests/tests/groupNode.spec.ts b/browser_tests/tests/groupNode.spec.ts index 6af103c8fb..90234e110d 100644 --- a/browser_tests/tests/groupNode.spec.ts +++ b/browser_tests/tests/groupNode.spec.ts @@ -10,6 +10,7 @@ import type { NodeReference } from '../fixtures/utils/litegraphUtils' test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled') + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'v1 (legacy)') }) test.describe('Group Node', { tag: '@node' }, () => { diff --git a/browser_tests/tests/nodeSearchBox.spec.ts b/browser_tests/tests/nodeSearchBox.spec.ts index 4b50ee3ca3..fc6d3ccb4f 100644 --- a/browser_tests/tests/nodeSearchBox.spec.ts +++ b/browser_tests/tests/nodeSearchBox.spec.ts @@ -18,7 +18,10 @@ test.describe('Node search box', { tag: '@node' }, () => { 'Comfy.LinkRelease.ActionShift', 'search box' ) - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) }) test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => { @@ -45,7 +48,10 @@ test.describe('Node search box', { tag: '@node' }, () => { await comfyPage.setup({ clearStorage: true }) // Simulate new user with 1.24.1+ installed version await comfyPage.settings.setSetting('Comfy.InstalledVersion', '1.24.1') - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) // Don't set LinkRelease settings explicitly to test versioned defaults await comfyPage.canvasOps.disconnectEdge() @@ -285,7 +291,10 @@ test.describe('Release context menu', { tag: '@node' }, () => { 'Comfy.LinkRelease.ActionShift', 'search box' ) - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) }) test( @@ -329,7 +338,10 @@ test.describe('Release context menu', { tag: '@node' }, () => { await comfyPage.setup({ clearStorage: true }) // Simulate existing user with pre-1.24.1 version await comfyPage.settings.setSetting('Comfy.InstalledVersion', '1.23.0') - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) // Don't set LinkRelease settings explicitly to test versioned defaults await comfyPage.canvasOps.disconnectEdge() @@ -350,7 +362,10 @@ test.describe('Release context menu', { tag: '@node' }, () => { 'Comfy.LinkRelease.Action', 'context menu' ) - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) await comfyPage.canvasOps.disconnectEdge() // Context menu should appear due to explicit setting, not search box diff --git a/browser_tests/tests/nodeSearchBoxV2.spec.ts b/browser_tests/tests/nodeSearchBoxV2.spec.ts new file mode 100644 index 0000000000..0c91963eed --- /dev/null +++ b/browser_tests/tests/nodeSearchBoxV2.spec.ts @@ -0,0 +1,149 @@ +import { + comfyExpect as expect, + comfyPageFixture as test +} from '../fixtures/ComfyPage' + +test.describe('Node search box V2', { tag: '@node' }, () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled') + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.LinkRelease.Action', + 'search box' + ) + await comfyPage.settings.setSetting( + 'Comfy.LinkRelease.ActionShift', + 'search box' + ) + await comfyPage.searchBoxV2.reload(comfyPage) + }) + + test('Can open search and add node', async ({ comfyPage }) => { + const { searchBoxV2 } = comfyPage + const initialCount = await comfyPage.nodeOps.getGraphNodesCount() + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + await searchBoxV2.input.fill('KSampler') + await expect(searchBoxV2.results.first()).toBeVisible() + + await comfyPage.page.keyboard.press('Enter') + await expect(searchBoxV2.input).not.toBeVisible() + + const newCount = await comfyPage.nodeOps.getGraphNodesCount() + expect(newCount).toBe(initialCount + 1) + }) + + test('Can add first default result with Enter', async ({ comfyPage }) => { + const { searchBoxV2 } = comfyPage + const initialCount = await comfyPage.nodeOps.getGraphNodesCount() + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + // Default results should be visible without typing + await expect(searchBoxV2.results.first()).toBeVisible() + + // Enter should add the first (selected) result + await comfyPage.page.keyboard.press('Enter') + await expect(searchBoxV2.input).not.toBeVisible() + + const newCount = await comfyPage.nodeOps.getGraphNodesCount() + expect(newCount).toBe(initialCount + 1) + }) + + test.describe('Category navigation', () => { + test('Favorites shows only bookmarked nodes', async ({ comfyPage }) => { + const { searchBoxV2 } = comfyPage + await comfyPage.settings.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [ + 'KSampler' + ]) + await searchBoxV2.reload(comfyPage) + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + await searchBoxV2.categoryButton('favorites').click() + + await expect(searchBoxV2.results).toHaveCount(1) + await expect(searchBoxV2.results.first()).toContainText('KSampler') + }) + + test('Category filters results to matching nodes', async ({ + comfyPage + }) => { + const { searchBoxV2 } = comfyPage + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + await searchBoxV2.categoryButton('sampling').click() + + await expect(searchBoxV2.results.first()).toBeVisible() + const count = await searchBoxV2.results.count() + expect(count).toBeGreaterThan(0) + }) + }) + + test.describe('Filter workflow', () => { + test('Can filter by input type via filter bar', async ({ comfyPage }) => { + const { searchBoxV2 } = comfyPage + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + // Click "Input" filter chip in the filter bar + await searchBoxV2.filterBarButton('Input').click() + + // Filter options should appear + await expect(searchBoxV2.filterOptions.first()).toBeVisible() + + // Type to narrow and select MODEL + await searchBoxV2.input.fill('MODEL') + await searchBoxV2.filterOptions + .filter({ hasText: 'MODEL' }) + .first() + .click() + + // Filter chip should appear and results should be filtered + await expect( + searchBoxV2.dialog.getByText('Input:', { exact: false }).locator('..') + ).toContainText('MODEL') + await expect(searchBoxV2.results.first()).toBeVisible() + }) + }) + + test.describe('Keyboard navigation', () => { + test('Can navigate and select with keyboard', async ({ comfyPage }) => { + const { searchBoxV2 } = comfyPage + const initialCount = await comfyPage.nodeOps.getGraphNodesCount() + + await comfyPage.canvasOps.doubleClick() + await expect(searchBoxV2.input).toBeVisible() + + await searchBoxV2.input.fill('KSampler') + const results = searchBoxV2.results + await expect(results.first()).toBeVisible() + + // First result selected by default + await expect(results.first()).toHaveAttribute('aria-selected', 'true') + + // ArrowDown moves selection + await comfyPage.page.keyboard.press('ArrowDown') + await expect(results.nth(1)).toHaveAttribute('aria-selected', 'true') + await expect(results.first()).toHaveAttribute('aria-selected', 'false') + + // ArrowUp moves back + await comfyPage.page.keyboard.press('ArrowUp') + await expect(results.first()).toHaveAttribute('aria-selected', 'true') + + // Enter selects and adds node + await comfyPage.page.keyboard.press('Enter') + await expect(searchBoxV2.input).not.toBeVisible() + + const newCount = await comfyPage.nodeOps.getGraphNodesCount() + expect(newCount).toBe(initialCount + 1) + }) + }) +}) diff --git a/browser_tests/tests/recordAudio.spec.ts b/browser_tests/tests/recordAudio.spec.ts index 07d9c48081..c46a5168e6 100644 --- a/browser_tests/tests/recordAudio.spec.ts +++ b/browser_tests/tests/recordAudio.spec.ts @@ -4,6 +4,7 @@ import { comfyPageFixture as test } from '../fixtures/ComfyPage' test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled') + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'v1 (legacy)') }) test.describe('Record Audio Node', { tag: '@screenshot' }, () => { diff --git a/browser_tests/tests/remoteWidgets.spec.ts b/browser_tests/tests/remoteWidgets.spec.ts index b09421bc68..e29f46ea2d 100644 --- a/browser_tests/tests/remoteWidgets.spec.ts +++ b/browser_tests/tests/remoteWidgets.spec.ts @@ -53,6 +53,10 @@ test.describe('Remote COMBO Widget', { tag: '@widget' }, () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) }) test.describe('Loading options', () => { diff --git a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-collapsed-badge-chromium-linux.png b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-collapsed-badge-chromium-linux.png index 97144d4827..37ceaa2a90 100644 Binary files a/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-collapsed-badge-chromium-linux.png and b/browser_tests/tests/rightClickMenu.spec.ts-snapshots/right-click-node-collapsed-badge-chromium-linux.png differ diff --git a/browser_tests/tests/subgraph.spec.ts b/browser_tests/tests/subgraph.spec.ts index d31c3bd6a8..d8010c8437 100644 --- a/browser_tests/tests/subgraph.spec.ts +++ b/browser_tests/tests/subgraph.spec.ts @@ -19,6 +19,10 @@ const SELECTORS = { test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Disabled') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) }) // Helper to get subgraph slot count diff --git a/browser_tests/tests/subgraphSearchAliases.spec.ts b/browser_tests/tests/subgraphSearchAliases.spec.ts index a17e56dfd3..ee1bbbb488 100644 --- a/browser_tests/tests/subgraphSearchAliases.spec.ts +++ b/browser_tests/tests/subgraphSearchAliases.spec.ts @@ -54,7 +54,10 @@ async function searchAndExpectResult( test.describe('Subgraph Search Aliases', { tag: ['@subgraph'] }, () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top') - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) }) test('Can set search aliases on subgraph and find via search', async ({ diff --git a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts index ab41c27858..1819edde34 100644 --- a/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts +++ b/browser_tests/tests/vueNodes/interactions/links/linkInteraction.spec.ts @@ -102,6 +102,7 @@ test.describe('Vue Node Link Interaction', { tag: '@screenshot' }, () => { test.beforeEach(async ({ comfyPage }) => { await comfyPage.settings.setSetting('Comfy.UseNewMenu', 'Top') await comfyPage.settings.setSetting('Comfy.VueNodes.Enabled', true) + await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') // await comfyPage.setup() await comfyPage.workflow.loadWorkflow('vueNodes/simple-triple') await comfyPage.vueNodes.waitForNodes() @@ -928,7 +929,10 @@ test.describe('Vue Node Link Interaction', { tag: '@screenshot' }, () => { 'Comfy.LinkRelease.ActionShift', 'context menu' ) - await comfyPage.settings.setSetting('Comfy.NodeSearchBoxImpl', 'default') + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) const samplerNode = ( await comfyPage.nodeOps.getNodeRefsByType('KSampler') @@ -994,6 +998,10 @@ test.describe('Vue Node Link Interaction', { tag: '@screenshot' }, () => { 'Comfy.LinkRelease.ActionShift', 'search box' ) + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) const samplerNode = ( await comfyPage.nodeOps.getNodeRefsByType('KSampler') @@ -1048,6 +1056,11 @@ test.describe('Vue Node Link Interaction', { tag: '@screenshot' }, () => { comfyPage, comfyMouse }) => { + await comfyPage.settings.setSetting( + 'Comfy.NodeSearchBoxImpl', + 'v1 (legacy)' + ) + // Setup workflow with a KSampler node await comfyPage.command.executeCommand('Comfy.NewBlankWorkflow') await comfyPage.nodeOps.waitForGraphNodes(0) diff --git a/packages/design-system/src/css/style.css b/packages/design-system/src/css/style.css index 88487f0a18..08ee3570c7 100644 --- a/packages/design-system/src/css/style.css +++ b/packages/design-system/src/css/style.css @@ -12,6 +12,12 @@ icon-sets: from-folder(comfy, './packages/design-system/src/icons'); } +/* Safelist dynamic comfy icons for node library folders */ +@source inline("icon-[comfy--{ai-model,bfl,bria,bytedance,credits,extensions-blocks,file-output,gemini,grok,hitpaw,ideogram,image-ai-edit,kling,ltxv,luma,magnific,mask,meshy,minimax,moonvalley-marey,node,openai,pin,pixverse,play,recraft,rodin,runway,sora,stability-ai,template,tencent,topaz,tripo,veo,vidu,wan,wavespeed,workflow}]"); + +/* Safelist dynamic comfy icons for essential nodes (kebab-case of node names) */ +@source inline("icon-[comfy--{load-image,save-image,load-video,save-video,load-3-d,save-glb,image-batch,image-crop,image-scale,image-rotate,image-blur,image-invert,canny,recraft-remove-background-node,kling-lip-sync-audio-to-video-node,load-audio,save-audio,stability-text-to-audio,lora-loader,clip-text-encode,get-video-components,tencent-text-to-model-node,tencent-image-to-model-node,open-ai-chat-node,subgraph-blueprint-canny-to-video-ltx-2-0,subgraph-blueprint-pose-to-video-ltx-2-0}]"); + @custom-variant touch (@media (hover: none)); @theme { diff --git a/packages/design-system/src/icons/bfl.svg b/packages/design-system/src/icons/bfl.svg new file mode 100644 index 0000000000..74ce48cc94 --- /dev/null +++ b/packages/design-system/src/icons/bfl.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/bria.svg b/packages/design-system/src/icons/bria.svg new file mode 100644 index 0000000000..20d6e66aa8 --- /dev/null +++ b/packages/design-system/src/icons/bria.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/bytedance.svg b/packages/design-system/src/icons/bytedance.svg new file mode 100644 index 0000000000..54309f7ccd --- /dev/null +++ b/packages/design-system/src/icons/bytedance.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/canny.svg b/packages/design-system/src/icons/canny.svg new file mode 100644 index 0000000000..6bae945a26 --- /dev/null +++ b/packages/design-system/src/icons/canny.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/canny_to_image.svg b/packages/design-system/src/icons/canny_to_image.svg new file mode 100644 index 0000000000..aaeb32d051 --- /dev/null +++ b/packages/design-system/src/icons/canny_to_image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/clip-text-encode.svg b/packages/design-system/src/icons/clip-text-encode.svg new file mode 100644 index 0000000000..ea6fb966b2 --- /dev/null +++ b/packages/design-system/src/icons/clip-text-encode.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/compare_images.svg b/packages/design-system/src/icons/compare_images.svg new file mode 100644 index 0000000000..a1d8ad0e5a --- /dev/null +++ b/packages/design-system/src/icons/compare_images.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/compare_videos.svg b/packages/design-system/src/icons/compare_videos.svg new file mode 100644 index 0000000000..75ca914356 --- /dev/null +++ b/packages/design-system/src/icons/compare_videos.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/credits.svg b/packages/design-system/src/icons/credits.svg new file mode 100644 index 0000000000..f806cddeed --- /dev/null +++ b/packages/design-system/src/icons/credits.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/crop_video.svg b/packages/design-system/src/icons/crop_video.svg new file mode 100644 index 0000000000..d2d5508a01 --- /dev/null +++ b/packages/design-system/src/icons/crop_video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/depth_to_image.svg b/packages/design-system/src/icons/depth_to_image.svg new file mode 100644 index 0000000000..d46ea269db --- /dev/null +++ b/packages/design-system/src/icons/depth_to_image.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/depth_to_video.svg b/packages/design-system/src/icons/depth_to_video.svg new file mode 100644 index 0000000000..9edb63ecc2 --- /dev/null +++ b/packages/design-system/src/icons/depth_to_video.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/edit_image.svg b/packages/design-system/src/icons/edit_image.svg new file mode 100644 index 0000000000..6ccc96753e --- /dev/null +++ b/packages/design-system/src/icons/edit_image.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/edit_video.svg b/packages/design-system/src/icons/edit_video.svg new file mode 100644 index 0000000000..ecb0acb01e --- /dev/null +++ b/packages/design-system/src/icons/edit_video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/enhance.svg b/packages/design-system/src/icons/enhance.svg new file mode 100644 index 0000000000..3a3534c2bd --- /dev/null +++ b/packages/design-system/src/icons/enhance.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/enhance_3dmodel.svg b/packages/design-system/src/icons/enhance_3dmodel.svg new file mode 100644 index 0000000000..e5d03491c3 --- /dev/null +++ b/packages/design-system/src/icons/enhance_3dmodel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/enhance_video.svg b/packages/design-system/src/icons/enhance_video.svg new file mode 100644 index 0000000000..93ccad2cfa --- /dev/null +++ b/packages/design-system/src/icons/enhance_video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/gemini.svg b/packages/design-system/src/icons/gemini.svg new file mode 100644 index 0000000000..d2f0fc7928 --- /dev/null +++ b/packages/design-system/src/icons/gemini.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/get-video-components.svg b/packages/design-system/src/icons/get-video-components.svg new file mode 100644 index 0000000000..7bca20f5cd --- /dev/null +++ b/packages/design-system/src/icons/get-video-components.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/grok.svg b/packages/design-system/src/icons/grok.svg new file mode 100644 index 0000000000..ac0e596fb9 --- /dev/null +++ b/packages/design-system/src/icons/grok.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/hitpaw.svg b/packages/design-system/src/icons/hitpaw.svg new file mode 100644 index 0000000000..7785527403 --- /dev/null +++ b/packages/design-system/src/icons/hitpaw.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/ideogram.svg b/packages/design-system/src/icons/ideogram.svg new file mode 100644 index 0000000000..a4b77cb475 --- /dev/null +++ b/packages/design-system/src/icons/ideogram.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image-batch.svg b/packages/design-system/src/icons/image-batch.svg new file mode 100644 index 0000000000..6c2c3913ba --- /dev/null +++ b/packages/design-system/src/icons/image-batch.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image-blur.svg b/packages/design-system/src/icons/image-blur.svg new file mode 100644 index 0000000000..a59f889931 --- /dev/null +++ b/packages/design-system/src/icons/image-blur.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/design-system/src/icons/image-crop.svg b/packages/design-system/src/icons/image-crop.svg new file mode 100644 index 0000000000..d0cddd2399 --- /dev/null +++ b/packages/design-system/src/icons/image-crop.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image-invert.svg b/packages/design-system/src/icons/image-invert.svg new file mode 100644 index 0000000000..98087cac3d --- /dev/null +++ b/packages/design-system/src/icons/image-invert.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/image-rotate.svg b/packages/design-system/src/icons/image-rotate.svg new file mode 100644 index 0000000000..d565dc6403 --- /dev/null +++ b/packages/design-system/src/icons/image-rotate.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/image-scale.svg b/packages/design-system/src/icons/image-scale.svg new file mode 100644 index 0000000000..09160d715f --- /dev/null +++ b/packages/design-system/src/icons/image-scale.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image_captioning.svg b/packages/design-system/src/icons/image_captioning.svg new file mode 100644 index 0000000000..38892dac13 --- /dev/null +++ b/packages/design-system/src/icons/image_captioning.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image_color_adjust.svg b/packages/design-system/src/icons/image_color_adjust.svg new file mode 100644 index 0000000000..4ba7fa877e --- /dev/null +++ b/packages/design-system/src/icons/image_color_adjust.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image_compositor.svg b/packages/design-system/src/icons/image_compositor.svg new file mode 100644 index 0000000000..8d06c0427b --- /dev/null +++ b/packages/design-system/src/icons/image_compositor.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image_depth.svg b/packages/design-system/src/icons/image_depth.svg new file mode 100644 index 0000000000..076915dd87 --- /dev/null +++ b/packages/design-system/src/icons/image_depth.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/image_iterator.svg b/packages/design-system/src/icons/image_iterator.svg new file mode 100644 index 0000000000..6b53161d64 --- /dev/null +++ b/packages/design-system/src/icons/image_iterator.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/image_normalmap.svg b/packages/design-system/src/icons/image_normalmap.svg new file mode 100644 index 0000000000..177107bdc2 --- /dev/null +++ b/packages/design-system/src/icons/image_normalmap.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/image_pose.svg b/packages/design-system/src/icons/image_pose.svg new file mode 100644 index 0000000000..bdf23df3b0 --- /dev/null +++ b/packages/design-system/src/icons/image_pose.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/image_shader.svg b/packages/design-system/src/icons/image_shader.svg new file mode 100644 index 0000000000..10d623411c --- /dev/null +++ b/packages/design-system/src/icons/image_shader.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/image_to_image.svg b/packages/design-system/src/icons/image_to_image.svg new file mode 100644 index 0000000000..6491f5362b --- /dev/null +++ b/packages/design-system/src/icons/image_to_image.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/image_to_layers.svg b/packages/design-system/src/icons/image_to_layers.svg new file mode 100644 index 0000000000..43d9d2ac02 --- /dev/null +++ b/packages/design-system/src/icons/image_to_layers.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/design-system/src/icons/image_to_video.svg b/packages/design-system/src/icons/image_to_video.svg new file mode 100644 index 0000000000..0052f1d308 --- /dev/null +++ b/packages/design-system/src/icons/image_to_video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/inpaint_image.svg b/packages/design-system/src/icons/inpaint_image.svg new file mode 100644 index 0000000000..b3959fa2b3 --- /dev/null +++ b/packages/design-system/src/icons/inpaint_image.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/inpaint_video.svg b/packages/design-system/src/icons/inpaint_video.svg new file mode 100644 index 0000000000..965bb3fcbd --- /dev/null +++ b/packages/design-system/src/icons/inpaint_video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/kling-lip-sync-audio-to-video-node.svg b/packages/design-system/src/icons/kling-lip-sync-audio-to-video-node.svg new file mode 100644 index 0000000000..4a74ae4845 --- /dev/null +++ b/packages/design-system/src/icons/kling-lip-sync-audio-to-video-node.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/kling.svg b/packages/design-system/src/icons/kling.svg new file mode 100644 index 0000000000..32dc0f806e --- /dev/null +++ b/packages/design-system/src/icons/kling.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/layers_to_image.svg b/packages/design-system/src/icons/layers_to_image.svg new file mode 100644 index 0000000000..3e9dfb3a4b --- /dev/null +++ b/packages/design-system/src/icons/layers_to_image.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/design-system/src/icons/load-3-d.svg b/packages/design-system/src/icons/load-3-d.svg new file mode 100644 index 0000000000..2c6ea830e0 --- /dev/null +++ b/packages/design-system/src/icons/load-3-d.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/load-audio.svg b/packages/design-system/src/icons/load-audio.svg new file mode 100644 index 0000000000..8276d1a80f --- /dev/null +++ b/packages/design-system/src/icons/load-audio.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/load-image.svg b/packages/design-system/src/icons/load-image.svg new file mode 100644 index 0000000000..8da07a860d --- /dev/null +++ b/packages/design-system/src/icons/load-image.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/load-video.svg b/packages/design-system/src/icons/load-video.svg new file mode 100644 index 0000000000..17cfbc6c13 --- /dev/null +++ b/packages/design-system/src/icons/load-video.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/lora-loader.svg b/packages/design-system/src/icons/lora-loader.svg new file mode 100644 index 0000000000..d96f7d7a6e --- /dev/null +++ b/packages/design-system/src/icons/lora-loader.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/ltxv.svg b/packages/design-system/src/icons/ltxv.svg new file mode 100644 index 0000000000..0f2db5f5e1 --- /dev/null +++ b/packages/design-system/src/icons/ltxv.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/luma.svg b/packages/design-system/src/icons/luma.svg new file mode 100644 index 0000000000..a124becca8 --- /dev/null +++ b/packages/design-system/src/icons/luma.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/magnific.svg b/packages/design-system/src/icons/magnific.svg new file mode 100644 index 0000000000..67d06535d6 --- /dev/null +++ b/packages/design-system/src/icons/magnific.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/meshy.svg b/packages/design-system/src/icons/meshy.svg new file mode 100644 index 0000000000..e0bdc68ff1 --- /dev/null +++ b/packages/design-system/src/icons/meshy.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/minimax.svg b/packages/design-system/src/icons/minimax.svg new file mode 100644 index 0000000000..a72b1c52a6 --- /dev/null +++ b/packages/design-system/src/icons/minimax.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/moonvalley-marey.svg b/packages/design-system/src/icons/moonvalley-marey.svg new file mode 100644 index 0000000000..f8201735f8 --- /dev/null +++ b/packages/design-system/src/icons/moonvalley-marey.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/music_generation.svg b/packages/design-system/src/icons/music_generation.svg new file mode 100644 index 0000000000..9df20c85f8 --- /dev/null +++ b/packages/design-system/src/icons/music_generation.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/design-system/src/icons/open-ai-chat-node.svg b/packages/design-system/src/icons/open-ai-chat-node.svg new file mode 100644 index 0000000000..48b44236ab --- /dev/null +++ b/packages/design-system/src/icons/open-ai-chat-node.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/openai.svg b/packages/design-system/src/icons/openai.svg new file mode 100644 index 0000000000..ec77e07300 --- /dev/null +++ b/packages/design-system/src/icons/openai.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/outpaint_image.svg b/packages/design-system/src/icons/outpaint_image.svg new file mode 100644 index 0000000000..03e603f506 --- /dev/null +++ b/packages/design-system/src/icons/outpaint_image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/painter.svg b/packages/design-system/src/icons/painter.svg new file mode 100644 index 0000000000..2557a7d85e --- /dev/null +++ b/packages/design-system/src/icons/painter.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/pixverse.svg b/packages/design-system/src/icons/pixverse.svg new file mode 100644 index 0000000000..5fcad5cd88 --- /dev/null +++ b/packages/design-system/src/icons/pixverse.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/pose_to_image.svg b/packages/design-system/src/icons/pose_to_image.svg new file mode 100644 index 0000000000..05dc681f7d --- /dev/null +++ b/packages/design-system/src/icons/pose_to_image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/prompt_enhance.svg b/packages/design-system/src/icons/prompt_enhance.svg new file mode 100644 index 0000000000..e1265b9347 --- /dev/null +++ b/packages/design-system/src/icons/prompt_enhance.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/recraft-remove-background-node.svg b/packages/design-system/src/icons/recraft-remove-background-node.svg new file mode 100644 index 0000000000..51497b57c9 --- /dev/null +++ b/packages/design-system/src/icons/recraft-remove-background-node.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/recraft.svg b/packages/design-system/src/icons/recraft.svg new file mode 100644 index 0000000000..ff93cb8403 --- /dev/null +++ b/packages/design-system/src/icons/recraft.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/design-system/src/icons/resize_video.svg b/packages/design-system/src/icons/resize_video.svg new file mode 100644 index 0000000000..fe2b45be98 --- /dev/null +++ b/packages/design-system/src/icons/resize_video.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/design-system/src/icons/rodin.svg b/packages/design-system/src/icons/rodin.svg new file mode 100644 index 0000000000..f628f41bfe --- /dev/null +++ b/packages/design-system/src/icons/rodin.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/rotate_video.svg b/packages/design-system/src/icons/rotate_video.svg new file mode 100644 index 0000000000..8c257ac9e2 --- /dev/null +++ b/packages/design-system/src/icons/rotate_video.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/runway.svg b/packages/design-system/src/icons/runway.svg new file mode 100644 index 0000000000..6cc20d7498 --- /dev/null +++ b/packages/design-system/src/icons/runway.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/save-audio.svg b/packages/design-system/src/icons/save-audio.svg new file mode 100644 index 0000000000..07360f8ab5 --- /dev/null +++ b/packages/design-system/src/icons/save-audio.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/save-glb.svg b/packages/design-system/src/icons/save-glb.svg new file mode 100644 index 0000000000..aa25867173 --- /dev/null +++ b/packages/design-system/src/icons/save-glb.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/save-image.svg b/packages/design-system/src/icons/save-image.svg new file mode 100644 index 0000000000..f137b72425 --- /dev/null +++ b/packages/design-system/src/icons/save-image.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/save-video.svg b/packages/design-system/src/icons/save-video.svg new file mode 100644 index 0000000000..edf324add6 --- /dev/null +++ b/packages/design-system/src/icons/save-video.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/select_object.svg b/packages/design-system/src/icons/select_object.svg new file mode 100644 index 0000000000..6f9532bcec --- /dev/null +++ b/packages/design-system/src/icons/select_object.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/sora.svg b/packages/design-system/src/icons/sora.svg new file mode 100644 index 0000000000..5d2c8b4967 --- /dev/null +++ b/packages/design-system/src/icons/sora.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/design-system/src/icons/stability-ai.svg b/packages/design-system/src/icons/stability-ai.svg new file mode 100644 index 0000000000..c84cae68ed --- /dev/null +++ b/packages/design-system/src/icons/stability-ai.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/stability-text-to-audio.svg b/packages/design-system/src/icons/stability-text-to-audio.svg new file mode 100644 index 0000000000..d642ece211 --- /dev/null +++ b/packages/design-system/src/icons/stability-text-to-audio.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/stitch_videos.svg b/packages/design-system/src/icons/stitch_videos.svg new file mode 100644 index 0000000000..0396bb1f6c --- /dev/null +++ b/packages/design-system/src/icons/stitch_videos.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/subgraph-blueprint-canny-to-video-ltx-2-0.svg b/packages/design-system/src/icons/subgraph-blueprint-canny-to-video-ltx-2-0.svg new file mode 100644 index 0000000000..f3f654b75b --- /dev/null +++ b/packages/design-system/src/icons/subgraph-blueprint-canny-to-video-ltx-2-0.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/subgraph-blueprint-pose-to-video-ltx-2-0.svg b/packages/design-system/src/icons/subgraph-blueprint-pose-to-video-ltx-2-0.svg new file mode 100644 index 0000000000..cfdaa8630e --- /dev/null +++ b/packages/design-system/src/icons/subgraph-blueprint-pose-to-video-ltx-2-0.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/tencent-image-to-model-node.svg b/packages/design-system/src/icons/tencent-image-to-model-node.svg new file mode 100644 index 0000000000..d723246bf7 --- /dev/null +++ b/packages/design-system/src/icons/tencent-image-to-model-node.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/tencent-text-to-model-node.svg b/packages/design-system/src/icons/tencent-text-to-model-node.svg new file mode 100644 index 0000000000..43fa9f0b25 --- /dev/null +++ b/packages/design-system/src/icons/tencent-text-to-model-node.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/tencent.svg b/packages/design-system/src/icons/tencent.svg new file mode 100644 index 0000000000..7eaa231218 --- /dev/null +++ b/packages/design-system/src/icons/tencent.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/text_iterator.svg b/packages/design-system/src/icons/text_iterator.svg new file mode 100644 index 0000000000..4b835d17cd --- /dev/null +++ b/packages/design-system/src/icons/text_iterator.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/text_to_image.svg b/packages/design-system/src/icons/text_to_image.svg new file mode 100644 index 0000000000..d4c61c765f --- /dev/null +++ b/packages/design-system/src/icons/text_to_image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/text_to_video.svg b/packages/design-system/src/icons/text_to_video.svg new file mode 100644 index 0000000000..88ccdb9d18 --- /dev/null +++ b/packages/design-system/src/icons/text_to_video.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/topaz.svg b/packages/design-system/src/icons/topaz.svg new file mode 100644 index 0000000000..f83e03ebcd --- /dev/null +++ b/packages/design-system/src/icons/topaz.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/design-system/src/icons/trim_video.svg b/packages/design-system/src/icons/trim_video.svg new file mode 100644 index 0000000000..b8d842251e --- /dev/null +++ b/packages/design-system/src/icons/trim_video.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/design-system/src/icons/tripo.svg b/packages/design-system/src/icons/tripo.svg new file mode 100644 index 0000000000..8de6a38c4f --- /dev/null +++ b/packages/design-system/src/icons/tripo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/vectorize.svg b/packages/design-system/src/icons/vectorize.svg new file mode 100644 index 0000000000..33fe18a847 --- /dev/null +++ b/packages/design-system/src/icons/vectorize.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/veo.svg b/packages/design-system/src/icons/veo.svg new file mode 100644 index 0000000000..c92e4c632b --- /dev/null +++ b/packages/design-system/src/icons/veo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/video_canny.svg b/packages/design-system/src/icons/video_canny.svg new file mode 100644 index 0000000000..4ecc9cc066 --- /dev/null +++ b/packages/design-system/src/icons/video_canny.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/design-system/src/icons/video_captioning.svg b/packages/design-system/src/icons/video_captioning.svg new file mode 100644 index 0000000000..ce63fe0774 --- /dev/null +++ b/packages/design-system/src/icons/video_captioning.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/video_compositor.svg b/packages/design-system/src/icons/video_compositor.svg new file mode 100644 index 0000000000..7b2bc2659b --- /dev/null +++ b/packages/design-system/src/icons/video_compositor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/design-system/src/icons/video_depth.svg b/packages/design-system/src/icons/video_depth.svg new file mode 100644 index 0000000000..c169d21654 --- /dev/null +++ b/packages/design-system/src/icons/video_depth.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/video_normal_map.svg b/packages/design-system/src/icons/video_normal_map.svg new file mode 100644 index 0000000000..5fc4283699 --- /dev/null +++ b/packages/design-system/src/icons/video_normal_map.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/video_shaders.svg b/packages/design-system/src/icons/video_shaders.svg new file mode 100644 index 0000000000..8d8d7bc1de --- /dev/null +++ b/packages/design-system/src/icons/video_shaders.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/design-system/src/icons/vidu.svg b/packages/design-system/src/icons/vidu.svg new file mode 100644 index 0000000000..3000b25bf7 --- /dev/null +++ b/packages/design-system/src/icons/vidu.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/voice_clone.svg b/packages/design-system/src/icons/voice_clone.svg new file mode 100644 index 0000000000..83b8062884 --- /dev/null +++ b/packages/design-system/src/icons/voice_clone.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/design-system/src/icons/wan.svg b/packages/design-system/src/icons/wan.svg new file mode 100644 index 0000000000..ed9b8c6d2a --- /dev/null +++ b/packages/design-system/src/icons/wan.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/design-system/src/icons/wavespeed.svg b/packages/design-system/src/icons/wavespeed.svg new file mode 100644 index 0000000000..d04d8365b5 --- /dev/null +++ b/packages/design-system/src/icons/wavespeed.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/shared-frontend-utils/package.json b/packages/shared-frontend-utils/package.json index 0a9d947abb..aa18c7a940 100644 --- a/packages/shared-frontend-utils/package.json +++ b/packages/shared-frontend-utils/package.json @@ -13,7 +13,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "axios": "catalog:" + "axios": "catalog:", + "dompurify": "catalog:" }, "devDependencies": { "typescript": "catalog:" diff --git a/packages/shared-frontend-utils/src/formatUtil.test.ts b/packages/shared-frontend-utils/src/formatUtil.test.ts index 75d8b8db23..9b793fe910 100644 --- a/packages/shared-frontend-utils/src/formatUtil.test.ts +++ b/packages/shared-frontend-utils/src/formatUtil.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from 'vitest' -import { getMediaTypeFromFilename, truncateFilename } from './formatUtil' +import { + getMediaTypeFromFilename, + highlightQuery, + truncateFilename +} from './formatUtil' describe('formatUtil', () => { describe('truncateFilename', () => { @@ -142,4 +146,42 @@ describe('formatUtil', () => { }) }) }) + + describe('highlightQuery', () => { + it('should return text unchanged when query is empty', () => { + expect(highlightQuery('Hello World', '')).toBe('Hello World') + }) + + it('should wrap matching text in highlight span', () => { + const result = highlightQuery('Hello World', 'World') + expect(result).toBe('Hello World') + }) + + it('should be case-insensitive', () => { + const result = highlightQuery('Hello World', 'hello') + expect(result).toBe('Hello World') + }) + + it('should sanitize text by default', () => { + const result = highlightQuery('', 'alert') + expect(result).not.toContain(' diff --git a/src/components/common/SearchBoxV2.test.ts b/src/components/common/SearchBoxV2.test.ts new file mode 100644 index 0000000000..dfb2feaaf9 --- /dev/null +++ b/src/components/common/SearchBoxV2.test.ts @@ -0,0 +1,90 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, it, vi } from 'vitest' +import { createI18n } from 'vue-i18n' + +import SearchBoxV2 from './SearchBoxV2.vue' + +vi.mock('@vueuse/core', () => ({ + watchDebounced: vi.fn(() => vi.fn()) +})) + +describe('SearchBoxV2', () => { + const i18n = createI18n({ + legacy: false, + locale: 'en', + messages: { + en: { + g: { + clear: 'Clear', + searchPlaceholder: 'Search...' + } + } + } + }) + + function mountComponent(props = {}) { + return mount(SearchBoxV2, { + global: { + plugins: [i18n], + stubs: { + ComboboxRoot: { + template: '
' + }, + ComboboxAnchor: { + template: '
' + }, + ComboboxInput: { + template: + '', + props: ['placeholder', 'modelValue', 'autoFocus'] + } + } + }, + props: { + modelValue: '', + ...props + } + }) + } + + it('uses i18n placeholder when no placeholder prop provided', () => { + const wrapper = mountComponent() + const input = wrapper.find('input') + expect(input.attributes('placeholder')).toBe('Search...') + }) + + it('uses custom placeholder when provided', () => { + const wrapper = mountComponent({ + placeholder: 'Custom placeholder' + }) + const input = wrapper.find('input') + expect(input.attributes('placeholder')).toBe('Custom placeholder') + }) + + it('shows search icon when search term is empty', () => { + const wrapper = mountComponent({ modelValue: '' }) + expect(wrapper.find('i.icon-\\[lucide--search\\]').exists()).toBe(true) + }) + + it('shows clear button when search term is not empty', () => { + const wrapper = mountComponent({ modelValue: 'test' }) + expect(wrapper.find('button').exists()).toBe(true) + }) + + it('clears search term when clear button is clicked', async () => { + const wrapper = mountComponent({ modelValue: 'test' }) + const clearButton = wrapper.find('button') + await clearButton.trigger('click') + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['']) + }) + + it('applies large size classes when size is lg', () => { + const wrapper = mountComponent({ size: 'lg' }) + expect(wrapper.html()).toContain('size-5') + }) + + it('applies medium size classes when size is md', () => { + const wrapper = mountComponent({ size: 'md' }) + expect(wrapper.html()).toContain('size-4') + }) +}) diff --git a/src/components/common/SearchBoxV2.vue b/src/components/common/SearchBoxV2.vue new file mode 100644 index 0000000000..82998501d3 --- /dev/null +++ b/src/components/common/SearchBoxV2.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/components/common/TextTicker.test.ts b/src/components/common/TextTicker.test.ts new file mode 100644 index 0000000000..0d94b84b40 --- /dev/null +++ b/src/components/common/TextTicker.test.ts @@ -0,0 +1,122 @@ +import { mount } from '@vue/test-utils' +import { nextTick } from 'vue' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import TextTicker from './TextTicker.vue' + +function mockScrollWidth(el: HTMLElement, scrollWidth: number) { + Object.defineProperty(el, 'scrollWidth', { + value: scrollWidth, + configurable: true + }) +} + +describe(TextTicker, () => { + let rafCallbacks: ((time: number) => void)[] + let wrapper: ReturnType + + beforeEach(() => { + vi.useFakeTimers() + rafCallbacks = [] + vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { + rafCallbacks.push(cb) + return rafCallbacks.length + }) + vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {}) + }) + + afterEach(() => { + wrapper?.unmount() + vi.useRealTimers() + vi.restoreAllMocks() + }) + + it('renders slot content', () => { + wrapper = mount(TextTicker, { + slots: { default: 'Hello World' } + }) + expect(wrapper.text()).toBe('Hello World') + }) + + it('scrolls on hover after delay', async () => { + wrapper = mount(TextTicker, { + slots: { default: 'Very long text that overflows' }, + props: { speed: 100 } + }) + + const el = wrapper.element as HTMLElement + mockScrollWidth(el, 300) + + await nextTick() + await wrapper.trigger('mouseenter') + await nextTick() + + expect(rafCallbacks.length).toBe(0) + + vi.advanceTimersByTime(350) + await nextTick() + expect(rafCallbacks.length).toBeGreaterThan(0) + + rafCallbacks[0](performance.now() + 500) + expect(el.scrollLeft).toBeGreaterThan(0) + }) + + it('cancels delayed scroll on mouse leave before delay elapses', async () => { + wrapper = mount(TextTicker, { + slots: { default: 'Very long text that overflows' }, + props: { speed: 100 } + }) + + mockScrollWidth(wrapper.element as HTMLElement, 300) + + await nextTick() + await wrapper.trigger('mouseenter') + await nextTick() + + vi.advanceTimersByTime(200) + await wrapper.trigger('mouseleave') + await nextTick() + + vi.advanceTimersByTime(350) + await nextTick() + expect(rafCallbacks.length).toBe(0) + }) + + it('resets scroll position on mouse leave', async () => { + wrapper = mount(TextTicker, { + slots: { default: 'Very long text that overflows' }, + props: { speed: 100 } + }) + + const el = wrapper.element as HTMLElement + mockScrollWidth(el, 300) + + await nextTick() + await wrapper.trigger('mouseenter') + await nextTick() + vi.advanceTimersByTime(350) + await nextTick() + + rafCallbacks[0](performance.now() + 500) + expect(el.scrollLeft).toBeGreaterThan(0) + + await wrapper.trigger('mouseleave') + await nextTick() + + expect(el.scrollLeft).toBe(0) + }) + + it('does not scroll when content fits', async () => { + wrapper = mount(TextTicker, { + slots: { default: 'Short' } + }) + + await nextTick() + await wrapper.trigger('mouseenter') + await nextTick() + vi.advanceTimersByTime(350) + await nextTick() + + expect(rafCallbacks.length).toBe(0) + }) +}) diff --git a/src/components/common/TextTicker.vue b/src/components/common/TextTicker.vue new file mode 100644 index 0000000000..057a910667 --- /dev/null +++ b/src/components/common/TextTicker.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/components/common/TreeExplorerV2.vue b/src/components/common/TreeExplorerV2.vue new file mode 100644 index 0000000000..6350f02b57 --- /dev/null +++ b/src/components/common/TreeExplorerV2.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/common/TreeExplorerV2Node.test.ts b/src/components/common/TreeExplorerV2Node.test.ts new file mode 100644 index 0000000000..3ef9c7bd33 --- /dev/null +++ b/src/components/common/TreeExplorerV2Node.test.ts @@ -0,0 +1,321 @@ +import { mount } from '@vue/test-utils' +import type { FlattenedItem } from 'reka-ui' +import { ref } from 'vue' +import { describe, expect, it, vi } from 'vitest' + +import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore' +import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes' +import { InjectKeyContextMenuNode } from '@/types/treeExplorerTypes' + +import TreeExplorerV2Node from './TreeExplorerV2Node.vue' + +vi.mock('@/platform/settings/settingStore', () => ({ + useSettingStore: () => ({ + get: vi.fn().mockReturnValue('left') + }) +})) + +vi.mock('@/components/node/NodePreviewCard.vue', () => ({ + default: { template: '
' } +})) + +const mockStartDrag = vi.fn() +const mockHandleNativeDrop = vi.fn() + +vi.mock('@/composables/node/useNodeDragToCanvas', () => ({ + useNodeDragToCanvas: () => ({ + startDrag: mockStartDrag, + handleNativeDrop: mockHandleNativeDrop + }) +})) + +describe('TreeExplorerV2Node', () => { + function createMockItem( + type: 'node' | 'folder', + overrides: Record = {} + ): FlattenedItem> { + const value = { + key: 'test-key', + label: 'Test Label', + type, + icon: 'pi pi-folder', + totalLeaves: 5, + ...overrides + } as RenderedTreeExplorerNode + return { + _id: 'test-id', + index: 0, + value, + level: 1, + hasChildren: type === 'folder', + bind: { value, level: 1 } + } + } + + function createTreeItemStub() { + const handleToggle = vi.fn() + const handleSelect = vi.fn() + return { + handleToggle, + handleSelect, + stub: { + template: `
`, + setup() { + return { handleToggle, handleSelect } + } + } + } + } + + function mountComponent( + props: Record = {}, + options: { + provide?: Record + treeItemStub?: ReturnType + } = {} + ) { + const treeItemStub = options.treeItemStub ?? createTreeItemStub() + return { + wrapper: mount(TreeExplorerV2Node, { + global: { + stubs: { + TreeItem: treeItemStub.stub, + ContextMenuTrigger: { + name: 'ContextMenuTrigger', + template: '
' + }, + Teleport: { template: '
' } + }, + provide: { + ...options.provide + } + }, + props: { + item: createMockItem('node'), + ...props + } + }), + treeItemStub + } + } + + describe('handleClick', () => { + it('emits nodeClick event when clicked', async () => { + const { wrapper } = mountComponent({ + item: createMockItem('node') + }) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + await nodeDiv.trigger('click') + + expect(wrapper.emitted('nodeClick')).toBeTruthy() + expect(wrapper.emitted('nodeClick')?.[0]?.[0]).toMatchObject({ + type: 'node', + label: 'Test Label' + }) + }) + + it('calls handleToggle for folder items', async () => { + const treeItemStub = createTreeItemStub() + const { wrapper } = mountComponent( + { item: createMockItem('folder') }, + { treeItemStub } + ) + + const folderDiv = wrapper.find('div.group\\/tree-node') + await folderDiv.trigger('click') + + expect(wrapper.emitted('nodeClick')).toBeTruthy() + expect(treeItemStub.handleToggle).toHaveBeenCalled() + }) + + it('does not call handleToggle for node items', async () => { + const treeItemStub = createTreeItemStub() + const { wrapper } = mountComponent( + { item: createMockItem('node') }, + { treeItemStub } + ) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + await nodeDiv.trigger('click') + + expect(wrapper.emitted('nodeClick')).toBeTruthy() + expect(treeItemStub.handleToggle).not.toHaveBeenCalled() + }) + }) + + describe('context menu', () => { + it('renders ContextMenuTrigger when showContextMenu is true for nodes', () => { + const { wrapper } = mountComponent({ + item: createMockItem('node'), + showContextMenu: true + }) + + expect( + wrapper.find('[data-testid="context-menu-trigger"]').exists() + ).toBe(true) + }) + + it('does not render ContextMenuTrigger for folder items', () => { + const { wrapper } = mountComponent({ + item: createMockItem('folder') + }) + + expect( + wrapper.find('[data-testid="context-menu-trigger"]').exists() + ).toBe(false) + }) + + it('sets contextMenuNode when contextmenu event is triggered', async () => { + const contextMenuNode = ref(null) + const nodeItem = createMockItem('node') + + const { wrapper } = mountComponent( + { + item: nodeItem, + showContextMenu: true + }, + { + provide: { + [InjectKeyContextMenuNode as symbol]: contextMenuNode + } + } + ) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + await nodeDiv.trigger('contextmenu') + + expect(contextMenuNode.value).toEqual(nodeItem.value) + }) + }) + + describe('rendering', () => { + it('renders node icon for node type', () => { + const { wrapper } = mountComponent({ + item: createMockItem('node') + }) + + expect(wrapper.find('i.icon-\\[comfy--node\\]').exists()).toBe(true) + }) + + it('renders folder icon for folder type', () => { + const { wrapper } = mountComponent({ + item: createMockItem('folder', { icon: 'icon-[lucide--folder]' }) + }) + + expect(wrapper.find('i.icon-\\[lucide--folder\\]').exists()).toBe(true) + }) + + it('renders label text', () => { + const { wrapper } = mountComponent({ + item: createMockItem('node', { label: 'My Node' }) + }) + + expect(wrapper.text()).toContain('My Node') + }) + + it('renders chevron for folder with children', () => { + const { wrapper } = mountComponent({ + item: { + ...createMockItem('folder'), + hasChildren: true + } + }) + + expect(wrapper.find('i.icon-\\[lucide--chevron-down\\]').exists()).toBe( + true + ) + }) + }) + + describe('drag and drop', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('sets draggable attribute on node items', () => { + const { wrapper } = mountComponent({ + item: createMockItem('node') + }) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + expect(nodeDiv.attributes('draggable')).toBe('true') + }) + + it('does not set draggable on folder items', () => { + const { wrapper } = mountComponent({ + item: createMockItem('folder') + }) + + const folderDiv = wrapper.find('div.group\\/tree-node') + expect(folderDiv.attributes('draggable')).toBeUndefined() + }) + + it('calls startDrag with native mode on dragstart', async () => { + const mockData = { name: 'TestNode' } + const { wrapper } = mountComponent({ + item: createMockItem('node', { data: mockData }) + }) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + await nodeDiv.trigger('dragstart') + + expect(mockStartDrag).toHaveBeenCalledWith(mockData, 'native') + }) + + it('does not call startDrag for folder items on dragstart', async () => { + const { wrapper } = mountComponent({ + item: createMockItem('folder') + }) + + const folderDiv = wrapper.find('div.group\\/tree-node') + await folderDiv.trigger('dragstart') + + expect(mockStartDrag).not.toHaveBeenCalled() + }) + + it('calls handleNativeDrop on dragend with drop coordinates', async () => { + const mockData = { name: 'TestNode' } + const { wrapper } = mountComponent({ + item: createMockItem('node', { data: mockData }) + }) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + + await nodeDiv.trigger('dragstart') + + const dragEndEvent = new DragEvent('dragend', { bubbles: true }) + Object.defineProperty(dragEndEvent, 'clientX', { value: 100 }) + Object.defineProperty(dragEndEvent, 'clientY', { value: 200 }) + + await nodeDiv.element.dispatchEvent(dragEndEvent) + await wrapper.vm.$nextTick() + + expect(mockHandleNativeDrop).toHaveBeenCalledWith(100, 200) + }) + + it('calls handleNativeDrop regardless of dropEffect', async () => { + const mockData = { name: 'TestNode' } + const { wrapper } = mountComponent({ + item: createMockItem('node', { data: mockData }) + }) + + const nodeDiv = wrapper.find('div.group\\/tree-node') + + await nodeDiv.trigger('dragstart') + mockHandleNativeDrop.mockClear() + + const dragEndEvent = new DragEvent('dragend', { bubbles: true }) + Object.defineProperty(dragEndEvent, 'clientX', { value: 300 }) + Object.defineProperty(dragEndEvent, 'clientY', { value: 400 }) + Object.defineProperty(dragEndEvent, 'dataTransfer', { + value: { dropEffect: 'none' } + }) + + await nodeDiv.element.dispatchEvent(dragEndEvent) + await wrapper.vm.$nextTick() + + expect(mockHandleNativeDrop).toHaveBeenCalledWith(300, 400) + }) + }) +}) diff --git a/src/components/common/TreeExplorerV2Node.vue b/src/components/common/TreeExplorerV2Node.vue new file mode 100644 index 0000000000..6291a4b2f6 --- /dev/null +++ b/src/components/common/TreeExplorerV2Node.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/components/node/NodePreviewCard.vue b/src/components/node/NodePreviewCard.vue new file mode 100644 index 0000000000..437803ab24 --- /dev/null +++ b/src/components/node/NodePreviewCard.vue @@ -0,0 +1,143 @@ + + + diff --git a/src/components/node/NodePricingBadge.vue b/src/components/node/NodePricingBadge.vue new file mode 100644 index 0000000000..74f63499d3 --- /dev/null +++ b/src/components/node/NodePricingBadge.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/components/node/NodeProviderBadge.vue b/src/components/node/NodeProviderBadge.vue new file mode 100644 index 0000000000..fa8fe19894 --- /dev/null +++ b/src/components/node/NodeProviderBadge.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/searchbox/NodeSearchBox.vue b/src/components/searchbox/NodeSearchBox.vue index fcf63b1eb0..747f829cc0 100644 --- a/src/components/searchbox/NodeSearchBox.vue +++ b/src/components/searchbox/NodeSearchBox.vue @@ -5,7 +5,7 @@
{ debouncedTrackSearch(query) } -const emit = defineEmits(['addFilter', 'removeFilter', 'addNode']) +const emit = defineEmits<{ + addFilter: [filter: FuseFilterWithValue] + removeFilter: [filter: FuseFilterWithValue] + addNode: [nodeDef: ComfyNodeDefImpl, dragEvent?: MouseEvent] +}>() // Track node selection and emit addNode event -const onAddNode = (nodeDef: ComfyNodeDefImpl) => { +function onAddNode(nodeDef: ComfyNodeDefImpl, event?: MouseEvent) { telemetry?.trackNodeSearchResultSelected({ node_type: nodeDef.name, last_query: currentQuery.value }) - emit('addNode', nodeDef) + emit('addNode', nodeDef, event) } let inputElement: HTMLInputElement | null = null diff --git a/src/components/searchbox/NodeSearchBoxPopover.vue b/src/components/searchbox/NodeSearchBoxPopover.vue index d043d78364..20f0fa97f7 100644 --- a/src/components/searchbox/NodeSearchBoxPopover.vue +++ b/src/components/searchbox/NodeSearchBoxPopover.vue @@ -6,10 +6,13 @@ :dismissable-mask="dismissable" :pt="{ root: { - class: 'invisible-dialog-root', - role: 'search' + class: useSearchBoxV2 + ? 'w-4/5 min-w-[32rem] max-w-[56rem] border-0 bg-transparent mt-[10vh] max-md:w-[95%] max-md:min-w-0 overflow-visible' + : 'invisible-dialog-root' + }, + mask: { + class: useSearchBoxV2 ? 'items-start' : 'node-search-box-dialog-mask' }, - mask: { class: 'node-search-box-dialog-mask' }, transition: { enterFromClass: 'opacity-0 scale-75', // 100ms is the duration of the transition in the dialog component @@ -21,7 +24,24 @@ @hide="clearFilters" >