mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-18 01:38:03 +00:00
Compare commits
36 Commits
codex/part
...
drjkl/upda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
751979bb23 | ||
|
|
2e46923ac8 | ||
|
|
4621969f02 | ||
|
|
ebb995eada | ||
|
|
5ffdaf4b46 | ||
|
|
d28394e62b | ||
|
|
8f94684c64 | ||
|
|
6ba4801663 | ||
|
|
c9b5647773 | ||
|
|
0cea758dfa | ||
|
|
7fd38c9144 | ||
|
|
b19d8d968f | ||
|
|
04c14d98c5 | ||
|
|
ba2d7c5774 | ||
|
|
5e2ce2099d | ||
|
|
a0d3379b98 | ||
|
|
31fa8fe952 | ||
|
|
62ffad0c3c | ||
|
|
a0be6ac430 | ||
|
|
d15ee851f0 | ||
|
|
99faedfa81 | ||
|
|
5e7629560b | ||
|
|
2182166da0 | ||
|
|
66e5d8c5f0 | ||
|
|
ecfd1f3816 | ||
|
|
f79f0e6066 | ||
|
|
c924d62f0c | ||
|
|
d833ffcc89 | ||
|
|
797fd0c78c | ||
|
|
f6811a5f57 | ||
|
|
5e1f0ddd03 | ||
|
|
4adf9bdda5 | ||
|
|
0e360677cf | ||
|
|
3f656030f7 | ||
|
|
08c34f2680 | ||
|
|
935142428d |
2
.github/workflows/ci-perf-report.yaml
vendored
2
.github/workflows/ci-perf-report.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
container:
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.17
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.21
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/ci-tests-e2e.yaml
vendored
4
.github/workflows/ci-tests-e2e.yaml
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
container:
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.17
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.21
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.17
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.21
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/ci-website-e2e.yaml
vendored
2
.github/workflows/ci-website-e2e.yaml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
if: ${{ needs.changes.outputs.app-website-changes == 'true' || needs.changes.outputs.packages-changes == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
image: mcr.microsoft.com/playwright:v1.61.1-noble
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.17
|
||||
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.21
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
update-screenshots:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.58.1-noble
|
||||
image: mcr.microsoft.com/playwright:v1.61.1-noble
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -128,6 +128,9 @@ jobs:
|
||||
|
||||
- name: Remove label
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
# Label cleanup is best-effort; the restricted pull_request token may
|
||||
# lack permission to remove labels and must not fail the run.
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.pr-info.outputs.pr-number }}
|
||||
|
||||
@@ -6,15 +6,19 @@
|
||||
"customSyntax": "postcss-html"
|
||||
}
|
||||
],
|
||||
"languageOptions": {
|
||||
"syntax": {
|
||||
"types": {
|
||||
"radial-gradient()": "| <any-value>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"import-notation": "string",
|
||||
"font-family-no-missing-generic-family-keyword": true,
|
||||
"declaration-property-value-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"typesSyntax": {
|
||||
"radial-gradient()": "| <any-value>"
|
||||
},
|
||||
"ignoreProperties": {
|
||||
"speak": ["none"],
|
||||
"app-region": ["drag", "no-drag"],
|
||||
|
||||
@@ -17,9 +17,7 @@ import { mockSystemStats } from '../fixtures/data/systemStats'
|
||||
|
||||
// Extend the base set with test-specific nodes
|
||||
const nodeDefs = createMockNodeDefinitions({
|
||||
MyCustomNode: {
|
||||
/* ... */
|
||||
}
|
||||
MyCustomNode: {/* ... */}
|
||||
})
|
||||
|
||||
await page.route('**/api/object_info', (route) =>
|
||||
|
||||
@@ -31,9 +31,7 @@ export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
/* props */
|
||||
}
|
||||
args: {/* props */}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -37,10 +37,8 @@ const config: KnipConfig = {
|
||||
entry: ['src/scripts/**/*.ts']
|
||||
}
|
||||
},
|
||||
ignoreBinaries: ['python3'],
|
||||
ignoreDependencies: [
|
||||
// Weird importmap things
|
||||
'@iconify-json/lucide',
|
||||
'@iconify/json',
|
||||
'@primeuix/forms',
|
||||
'@primeuix/styled',
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
"@vee-validate/zod": "catalog:",
|
||||
"@vueuse/core": "catalog:",
|
||||
"@vueuse/integrations": "catalog:",
|
||||
"@vueuse/router": "^14.2.0",
|
||||
"@vueuse/router": "^14.3.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-serialize": "^0.13.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
@@ -111,7 +111,7 @@
|
||||
"extendable-media-recorder": "^9.2.27",
|
||||
"extendable-media-recorder-wav-encoder": "^7.0.129",
|
||||
"firebase": "catalog:",
|
||||
"fuse.js": "^7.0.0",
|
||||
"fuse.js": "7.0.0",
|
||||
"glob": "catalog:",
|
||||
"jsonata": "catalog:",
|
||||
"loglevel": "^1.9.2",
|
||||
@@ -219,5 +219,5 @@
|
||||
"node": ">=25 <26",
|
||||
"pnpm": ">=11.3"
|
||||
},
|
||||
"packageManager": "pnpm@11.3.0"
|
||||
"packageManager": "pnpm@11.13.1"
|
||||
}
|
||||
|
||||
@@ -78,9 +78,9 @@
|
||||
--color-interface-panel-job-progress-border: var(--base-foreground);
|
||||
|
||||
--color-blue-selection: rgb(from var(--color-azure-600) r g b / 0.3);
|
||||
--color-node-hover-100: rgb(from var(--color-charcoal-800) r g b/ 0.15);
|
||||
--color-node-hover-200: rgb(from var(--color-charcoal-800) r g b/ 0.1);
|
||||
--color-modal-tag: rgb(from var(--color-smoke-400) r g b/ 0.4);
|
||||
--color-node-hover-100: rgb(from var(--color-charcoal-800) r g b / 0.15);
|
||||
--color-node-hover-200: rgb(from var(--color-charcoal-800) r g b / 0.1);
|
||||
--color-modal-tag: rgb(from var(--color-smoke-400) r g b / 0.4);
|
||||
--color-alpha-charcoal-600-30: color-mix(
|
||||
in srgb,
|
||||
var(--color-charcoal-600) 30%,
|
||||
@@ -107,7 +107,7 @@
|
||||
--color-highlight: var(--p-primary-color);
|
||||
|
||||
/* Special Colors (temporary) */
|
||||
--color-dark-elevation-1.5: rgba(from white r g b/ 0.015);
|
||||
--color-dark-elevation-1-5: rgba(from white r g b / 0.015);
|
||||
--color-dark-elevation-2: rgba(from white r g b / 0.03);
|
||||
}
|
||||
|
||||
@@ -1873,9 +1873,9 @@ audio.comfy-audio.empty-audio-widget {
|
||||
/* Shadow Cover TOP */
|
||||
linear-gradient(--value(--color-*) 30%, transparent) center top,
|
||||
/* Shadow Cover BOTTOM */
|
||||
linear-gradient(transparent, --value(--color-*) 70%) center bottom,
|
||||
linear-gradient(transparent, --value(--color-*) 70%) center bottom,
|
||||
/* Shadow TOP */
|
||||
radial-gradient(
|
||||
radial-gradient(
|
||||
farthest-side at 50% 0,
|
||||
color-mix(in oklab, --value(--color-*), #777777 35%),
|
||||
60%,
|
||||
@@ -1883,7 +1883,7 @@ audio.comfy-audio.empty-audio-widget {
|
||||
)
|
||||
center top,
|
||||
/* Shadow BOTTOM */
|
||||
radial-gradient(
|
||||
radial-gradient(
|
||||
farthest-side at 50% 100%,
|
||||
color-mix(in oklab, --value(--color-*), #777777 35%),
|
||||
60%,
|
||||
|
||||
10291
pnpm-lock.yaml
generated
10291
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -24,26 +24,26 @@ catalog:
|
||||
'@iconify/tailwind4': ^1.2.3
|
||||
'@iconify/tools': ^5.0.3
|
||||
'@iconify/utils': ^3.1.0
|
||||
'@intlify/eslint-plugin-vue-i18n': ^4.5.0
|
||||
'@intlify/eslint-plugin-vue-i18n': ^4.5.1
|
||||
'@lobehub/i18n-cli': ^1.26.1
|
||||
'@lucide/vue': ^1.17.0
|
||||
'@pinia/testing': ^1.0.3
|
||||
'@playwright/test': ^1.58.1
|
||||
'@pinia/testing': ^2.0.1
|
||||
'@playwright/test': ^1.61.1
|
||||
'@primeuix/forms': 0.0.2
|
||||
'@primeuix/styled': 0.3.2
|
||||
'@primeuix/utils': ^0.3.2
|
||||
'@primevue/core': ^4.2.5
|
||||
'@primevue/forms': ^4.2.5
|
||||
'@primevue/core': 4.2.5
|
||||
'@primevue/forms': 4.2.5
|
||||
'@primevue/icons': 4.2.5
|
||||
'@primevue/themes': ^4.2.5
|
||||
'@primevue/themes': 4.2.5
|
||||
'@sentry/vite-plugin': ^4.6.0
|
||||
'@sentry/vue': ^10.32.1
|
||||
'@sparkjsdev/spark': ^2.1.0
|
||||
'@storybook/addon-docs': ^10.2.10
|
||||
'@storybook/addon-docs': ^10.5.0
|
||||
'@storybook/addon-mcp': 0.1.6
|
||||
'@storybook/vue3': ^10.2.10
|
||||
'@storybook/vue3-vite': ^10.2.10
|
||||
'@tailwindcss/vite': ^4.3.0
|
||||
'@storybook/vue3': ^10.5.0
|
||||
'@storybook/vue3-vite': ^10.5.0
|
||||
'@tailwindcss/vite': ^4.3.2
|
||||
'@tanstack/vue-virtual': ^3.13.12
|
||||
'@testing-library/jest-dom': ^6.9.1
|
||||
'@testing-library/user-event': ^14.6.1
|
||||
@@ -59,16 +59,16 @@ catalog:
|
||||
'@total-typescript/shoehorn': ^0.1.2
|
||||
'@types/fs-extra': ^11.0.4
|
||||
'@types/jsdom': ^21.1.7
|
||||
'@types/node': ^24.1.0
|
||||
'@types/node': ^25.9.5
|
||||
'@types/semver': ^7.7.0
|
||||
'@types/three': ^0.184.1
|
||||
'@vee-validate/zod': ^4.15.1
|
||||
'@vercel/analytics': ^2.0.1
|
||||
'@vitejs/plugin-vue': ^6.0.0
|
||||
'@vitest/coverage-v8': ^4.0.16
|
||||
'@vitest/ui': ^4.0.16
|
||||
'@vueuse/core': ^14.2.0
|
||||
'@vueuse/integrations': ^14.2.0
|
||||
'@vitejs/plugin-vue': ^6.0.8
|
||||
'@vitest/coverage-v8': ^4.1.10
|
||||
'@vitest/ui': ^4.1.10
|
||||
'@vueuse/core': ^14.3.0
|
||||
'@vueuse/integrations': ^14.3.0
|
||||
'@webgpu/types': ^0.1.66
|
||||
algoliasearch: ^5.21.0
|
||||
astro: ^6.4.2
|
||||
@@ -76,75 +76,75 @@ catalog:
|
||||
class-variance-authority: ^0.7.1
|
||||
cross-env: ^10.1.0
|
||||
cva: 1.0.0-beta.4
|
||||
dompurify: ^3.4.5
|
||||
dompurify: 3.4.7
|
||||
dotenv: ^16.4.5
|
||||
eslint: ^10.4.0
|
||||
eslint: ^10.7.0
|
||||
eslint-config-prettier: ^10.1.8
|
||||
eslint-import-resolver-typescript: ^4.4.4
|
||||
eslint-plugin-better-tailwindcss: ^4.3.1
|
||||
eslint-plugin-import-x: ^4.16.2
|
||||
eslint-plugin-oxlint: 1.69.0
|
||||
eslint-plugin-playwright: ^2.10.1
|
||||
eslint-plugin-storybook: ^10.2.10
|
||||
eslint-plugin-testing-library: ^7.16.1
|
||||
eslint-import-resolver-typescript: ^4.4.5
|
||||
eslint-plugin-better-tailwindcss: ^4.6.1
|
||||
eslint-plugin-import-x: ^4.17.1
|
||||
eslint-plugin-oxlint: 1.73.0
|
||||
eslint-plugin-playwright: ^2.10.5
|
||||
eslint-plugin-storybook: ^10.5.0
|
||||
eslint-plugin-testing-library: ^7.16.2
|
||||
eslint-plugin-unused-imports: ^4.4.1
|
||||
eslint-plugin-vue: ^10.9.1
|
||||
eslint-plugin-vue: ^10.9.2
|
||||
fallow: ^2.102.0
|
||||
fast-check: ^4.5.3
|
||||
firebase: ^11.6.0
|
||||
glob: ^13.0.6
|
||||
globals: ^16.5.0
|
||||
globals: ^17.7.0
|
||||
gsap: ^3.14.2
|
||||
happy-dom: ^20.8.9
|
||||
happy-dom: 20.9.0
|
||||
husky: ^9.1.7
|
||||
jiti: 2.6.1
|
||||
jsdom: ^27.4.0
|
||||
jiti: 2.7.0
|
||||
jsdom: ^29.1.1
|
||||
jsonata: ^2.1.0
|
||||
knip: ^6.15.0
|
||||
knip: ^6.27.0
|
||||
lenis: ^1.3.21
|
||||
lint-staged: ^16.2.7
|
||||
markdown-table: ^3.0.4
|
||||
minisearch: ^7.2.0
|
||||
mixpanel-browser: ^2.71.0
|
||||
monocart-coverage-reports: ^2.12.9
|
||||
oxfmt: ^0.54.0
|
||||
oxlint: ^1.69.0
|
||||
oxlint-tsgolint: ^0.23.0
|
||||
oxfmt: ^0.59.0
|
||||
oxlint: ^1.74.0
|
||||
oxlint-tsgolint: ^0.24.0
|
||||
picocolors: ^1.1.1
|
||||
pinia: ^3.0.4
|
||||
pinia: ^4.0.2
|
||||
postcss-html: ^1.8.0
|
||||
posthog-js: ^1.358.1
|
||||
pretty-bytes: ^7.1.0
|
||||
primeicons: ^7.0.0
|
||||
primevue: ^4.2.5
|
||||
primevue: 4.2.5
|
||||
reka-ui: 2.5.0
|
||||
rollup-plugin-visualizer: ^6.0.4
|
||||
storybook: ^10.2.10
|
||||
stylelint: ^16.26.1
|
||||
tailwindcss: ^4.3.0
|
||||
storybook: ^10.5.0
|
||||
stylelint: ^17.14.0
|
||||
tailwindcss: ^4.3.2
|
||||
tailwindcss-primeui: ^0.6.1
|
||||
three: ^0.184.0
|
||||
tsx: ^4.15.6
|
||||
tw-animate-css: ^1.3.8
|
||||
typegpu: ^0.8.2
|
||||
typescript: ^5.9.3
|
||||
typescript-eslint: ^8.60.0
|
||||
typescript: ^6.0.3
|
||||
typescript-eslint: ^8.64.0
|
||||
unplugin-icons: ^22.5.0
|
||||
unplugin-typegpu: 0.8.0
|
||||
unplugin-vue-components: ^30.0.0
|
||||
uuid: ^11.1.1
|
||||
vee-validate: ^4.15.1
|
||||
vite: ^8.0.13
|
||||
vite: ^8.1.4
|
||||
vite-plugin-dts: ^4.5.4
|
||||
vite-plugin-html: ^3.2.2
|
||||
vite-plugin-vue-devtools: ^8.0.0
|
||||
vitest: ^4.1.0
|
||||
vue: ^3.5.34
|
||||
vue-component-type-helpers: ^3.2.1
|
||||
vue-eslint-parser: ^10.4.0
|
||||
vue-i18n: ^9.14.5
|
||||
vue-router: ^4.4.3
|
||||
vue-tsc: ^3.2.5
|
||||
vite-plugin-vue-devtools: ^8.1.5
|
||||
vitest: ^4.1.10
|
||||
vue: ^3.5.39
|
||||
vue-component-type-helpers: ^3.3.7
|
||||
vue-eslint-parser: ^10.4.1
|
||||
vue-i18n: ^11.4.6
|
||||
vue-router: ^5.2.0
|
||||
vue-tsc: ^3.3.7
|
||||
vuefire: ^3.2.1
|
||||
wwobjloader2: ^6.2.1
|
||||
yjs: ^13.6.27
|
||||
|
||||
@@ -32,11 +32,11 @@ let _runWhenIdle: (
|
||||
* Falls back to setTimeout-based scheduling in browsers without native support.
|
||||
*/
|
||||
export let runWhenGlobalIdle: (
|
||||
callback: (idle: IdleDeadline) => void,
|
||||
timeout?: number
|
||||
) => IDisposable
|
||||
callback: (idle: IdleDeadline) => void,
|
||||
timeout?: number
|
||||
) => IDisposable
|
||||
|
||||
// Self-invoking function to set up the idle callback implementation
|
||||
// Self-invoking function to set up the idle callback implementation
|
||||
;(function () {
|
||||
const safeGlobal: GlobalWindow = globalThis as GlobalWindow
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ const commandsData = computed<ICommandData[]>(() => {
|
||||
id: command.id,
|
||||
label: t(
|
||||
`commands.${normalizeI18nKey(command.id)}.label`,
|
||||
command.label ?? ''
|
||||
command.label ?? command.id
|
||||
),
|
||||
keybindings: keybindingStore.getKeybindingsByCommandId(command.id),
|
||||
source: command.source,
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
PointElement,
|
||||
Tooltip
|
||||
} from 'chart.js'
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
@@ -129,7 +129,7 @@ export function useChart(
|
||||
data: Ref<ChartData>,
|
||||
options?: Ref<ChartOptions | undefined>
|
||||
) {
|
||||
const chartInstance = ref<Chart | null>(null)
|
||||
const chartInstance = shallowRef<Chart | null>(null)
|
||||
|
||||
function createChart() {
|
||||
if (!canvasRef.value) return
|
||||
|
||||
@@ -207,7 +207,8 @@ export function useGPUResources() {
|
||||
async function initTypeGPU(): Promise<void> {
|
||||
if (store.tgpuRoot) {
|
||||
/* c8 ignore start */
|
||||
device = store.tgpuRoot.device
|
||||
// typegpu vendors its own WebGPU types; align with @webgpu/types
|
||||
device = store.tgpuRoot.device as GPUDevice
|
||||
return
|
||||
/* c8 ignore stop */
|
||||
}
|
||||
@@ -215,7 +216,7 @@ export function useGPUResources() {
|
||||
/* c8 ignore start — requires functional WebGPU hardware */
|
||||
const root = await tgpu.init()
|
||||
store.tgpuRoot = root
|
||||
device = root.device
|
||||
device = root.device as GPUDevice
|
||||
console.warn('✅ TypeGPU initialized! Root:', root)
|
||||
console.warn('Device info:', root.device.limits)
|
||||
/* c8 ignore stop */
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const mockCopy = vi.fn()
|
||||
const mockWriteText = vi.fn()
|
||||
const mockToastAdd = vi.fn()
|
||||
|
||||
vi.mock('@vueuse/core', () => ({
|
||||
useClipboard: vi.fn(() => ({
|
||||
copy: mockCopy,
|
||||
copied: ref(false),
|
||||
isSupported: computed(() => true)
|
||||
}))
|
||||
}))
|
||||
|
||||
vi.mock('primevue/usetoast', () => ({
|
||||
useToast: vi.fn(() => ({
|
||||
add: mockToastAdd
|
||||
@@ -22,34 +13,31 @@ vi.mock('@/i18n', () => ({
|
||||
t: (key: string) => key
|
||||
}))
|
||||
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||
|
||||
describe('useCopyToClipboard', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
vi.mocked(useClipboard).mockReturnValue({
|
||||
copy: mockCopy,
|
||||
copied: ref(false),
|
||||
isSupported: computed(() => true),
|
||||
text: ref('')
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: { writeText: mockWriteText }
|
||||
})
|
||||
})
|
||||
|
||||
it('shows success toast when modern clipboard succeeds', async () => {
|
||||
mockCopy.mockResolvedValue(undefined)
|
||||
mockWriteText.mockResolvedValue(undefined)
|
||||
|
||||
const { copyToClipboard } = useCopyToClipboard()
|
||||
await copyToClipboard('hello')
|
||||
|
||||
expect(mockCopy).toHaveBeenCalledWith('hello')
|
||||
expect(mockWriteText).toHaveBeenCalledWith('hello')
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ severity: 'success' })
|
||||
)
|
||||
})
|
||||
|
||||
it('falls back to legacy when modern clipboard fails', async () => {
|
||||
mockCopy.mockRejectedValue(new Error('Not allowed'))
|
||||
mockWriteText.mockRejectedValue(new Error('Not allowed'))
|
||||
document.execCommand = vi.fn(() => true)
|
||||
|
||||
const { copyToClipboard } = useCopyToClipboard()
|
||||
@@ -62,7 +50,7 @@ describe('useCopyToClipboard', () => {
|
||||
})
|
||||
|
||||
it('shows error toast when both modern and legacy fail', async () => {
|
||||
mockCopy.mockRejectedValue(new Error('Not allowed'))
|
||||
mockWriteText.mockRejectedValue(new Error('Not allowed'))
|
||||
document.execCommand = vi.fn(() => false)
|
||||
|
||||
const { copyToClipboard } = useCopyToClipboard()
|
||||
@@ -73,19 +61,17 @@ describe('useCopyToClipboard', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('falls through to legacy when isSupported is false', async () => {
|
||||
vi.mocked(useClipboard).mockReturnValue({
|
||||
copy: mockCopy,
|
||||
copied: ref(false),
|
||||
isSupported: computed(() => false),
|
||||
text: ref('')
|
||||
it('falls through to legacy when clipboard API is unavailable', async () => {
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: undefined
|
||||
})
|
||||
document.execCommand = vi.fn(() => true)
|
||||
|
||||
const { copyToClipboard } = useCopyToClipboard()
|
||||
await copyToClipboard('hello')
|
||||
|
||||
expect(mockCopy).not.toHaveBeenCalled()
|
||||
expect(mockWriteText).not.toHaveBeenCalled()
|
||||
expect(document.execCommand).toHaveBeenCalledWith('copy')
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ severity: 'success' })
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
|
||||
import { t } from '@/i18n'
|
||||
@@ -20,15 +19,14 @@ function legacyCopy(text: string): boolean {
|
||||
}
|
||||
|
||||
export function useCopyToClipboard() {
|
||||
const { copy, isSupported } = useClipboard()
|
||||
const toast = useToast()
|
||||
|
||||
async function copyToClipboard(text: string) {
|
||||
let success = false
|
||||
|
||||
try {
|
||||
if (isSupported.value) {
|
||||
await copy(text)
|
||||
if (navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
success = true
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -260,7 +260,7 @@ describe('useCoreCommands', () => {
|
||||
function createMockSettingStore(
|
||||
getReturnValue: boolean
|
||||
): ReturnType<typeof useSettingStore> {
|
||||
return {
|
||||
return fromPartial<ReturnType<typeof useSettingStore>>({
|
||||
get: vi.fn().mockReturnValue(getReturnValue),
|
||||
addSetting: vi.fn(),
|
||||
load: vi.fn(),
|
||||
@@ -287,7 +287,7 @@ describe('useCoreCommands', () => {
|
||||
$onAction: vi.fn(),
|
||||
$dispose: vi.fn(),
|
||||
_customProperties: new Set()
|
||||
} satisfies ReturnType<typeof useSettingStore>
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -7,6 +7,7 @@ let loadLocale: typeof I18nModule.loadLocale
|
||||
let mergeCustomNodesI18n: typeof I18nModule.mergeCustomNodesI18n
|
||||
let resolveSupportedLocale: typeof I18nModule.resolveSupportedLocale
|
||||
let setActiveLocale: typeof I18nModule.setActiveLocale
|
||||
let te: typeof I18nModule.te
|
||||
|
||||
async function importI18nModule() {
|
||||
const i18nModule = await import('./i18n')
|
||||
@@ -15,10 +16,13 @@ async function importI18nModule() {
|
||||
mergeCustomNodesI18n = i18nModule.mergeCustomNodesI18n
|
||||
resolveSupportedLocale = i18nModule.resolveSupportedLocale
|
||||
setActiveLocale = i18nModule.setActiveLocale
|
||||
te = i18nModule.te
|
||||
}
|
||||
|
||||
// Mock the JSON imports before importing i18n module
|
||||
vi.mock('./locales/en/main.json', () => ({ default: { welcome: 'Welcome' } }))
|
||||
vi.mock('./locales/en/main.json', () => ({
|
||||
default: { welcome: 'Welcome', enOnly: 'English only' }
|
||||
}))
|
||||
vi.mock('./locales/en/nodeDefs.json', () => ({
|
||||
default: { testNode: 'Test Node' }
|
||||
}))
|
||||
@@ -228,6 +232,16 @@ describe('i18n', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('te', () => {
|
||||
it('checks only the active locale, never the fallback locale', async () => {
|
||||
expect(te('enOnly')).toBe(true)
|
||||
|
||||
await setActiveLocale('zh')
|
||||
expect(te('welcome')).toBe(true)
|
||||
expect(te('enOnly')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveSupportedLocale', () => {
|
||||
it('returns the canonical tag when the input is shipped', () => {
|
||||
expect(resolveSupportedLocale('en')).toBe('en')
|
||||
|
||||
@@ -155,7 +155,10 @@ export const i18n = createI18n({
|
||||
|
||||
/** Convenience shorthand: i18n.global */
|
||||
export const t: (typeof i18n.global)['t'] = i18n.global.t
|
||||
export const te: (typeof i18n.global)['te'] = i18n.global.te
|
||||
// vue-i18n 11's te() consults the fallback locale; default to the active
|
||||
// locale to preserve the v9 behavior our fallback paths rely on.
|
||||
export const te: (typeof i18n.global)['te'] = (key, locale) =>
|
||||
i18n.global.te(key, locale ?? i18n.global.locale.value)
|
||||
export const d: (typeof i18n.global)['d'] = i18n.global.d
|
||||
const tm = i18n.global.tm
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ export const hasNonEmptyValue = (
|
||||
return true
|
||||
}
|
||||
|
||||
export const isOtherValue = (current: string | string[] | undefined): boolean =>
|
||||
export const isOtherValue = (
|
||||
current: string | string[] | undefined
|
||||
): boolean =>
|
||||
Array.isArray(current) ? current.includes('other') : current === 'other'
|
||||
|
||||
const conditionMatches = (
|
||||
|
||||
@@ -231,6 +231,7 @@ defineExpose({
|
||||
|
||||
/* What's new title - targets h2 or strong text after h1 */
|
||||
.content-text :deep(h2),
|
||||
/* stylelint-disable-next-line selector-max-type -- generated markdown offers no class hooks */
|
||||
.content-text :deep(h1 + p strong) {
|
||||
color: var(--text-primary);
|
||||
font-family: Inter, sans-serif;
|
||||
|
||||
@@ -224,9 +224,10 @@ function resolveValues(values: unknown): string[] {
|
||||
}
|
||||
|
||||
const modelValue = defineModel<string | undefined>({
|
||||
default(modelProps: Props) {
|
||||
default(modelProps: Record<string, unknown>) {
|
||||
try {
|
||||
const values = modelProps.widget.options?.values
|
||||
const modelWidget = modelProps.widget as Props['widget'] | undefined
|
||||
const values = modelWidget?.options?.values
|
||||
const resolved = typeof values === 'function' ? values() : values
|
||||
const firstValue = Array.isArray(resolved)
|
||||
? resolved.find((value) => value !== null && value !== undefined)
|
||||
|
||||
@@ -41,8 +41,9 @@ provide(
|
||||
)
|
||||
|
||||
const modelValue = defineModel<string | undefined>({
|
||||
default(props: Props) {
|
||||
const values = props.widget.options?.values
|
||||
default(modelProps: Record<string, unknown>) {
|
||||
const modelWidget = modelProps.widget as Props['widget'] | undefined
|
||||
const values = modelWidget?.options?.values
|
||||
return (Array.isArray(values) ? values[0] : undefined) ?? ''
|
||||
}
|
||||
})
|
||||
|
||||
@@ -124,7 +124,7 @@ const findElementsInTag = (
|
||||
end: number,
|
||||
id: Uint8Array
|
||||
): EbmlElementRange | null => {
|
||||
for (let pos = start; pos < end - 1; ) {
|
||||
for (let pos = start; pos < end - 1;) {
|
||||
if (matchesId(data, pos, id)) {
|
||||
const size = readVint(data, pos + 2)
|
||||
if (size && size.value > 0) {
|
||||
@@ -307,7 +307,7 @@ const ebmlToString = (
|
||||
const parseMetadata = (data: Uint8Array): ComfyMetadata => {
|
||||
const meta: ComfyMetadata = {}
|
||||
|
||||
for (let pos = 0; pos < data.length - 2; ) {
|
||||
for (let pos = 0; pos < data.length - 2;) {
|
||||
const tagInfo = findNextTag(data, pos)
|
||||
if (!tagInfo) {
|
||||
pos++
|
||||
|
||||
@@ -306,13 +306,9 @@ export function useNodeFactory() {
|
||||
// Create node based on type and configuration
|
||||
switch (type) {
|
||||
case 'basic':
|
||||
return {
|
||||
/* basic node implementation */
|
||||
}
|
||||
return {/* basic node implementation */}
|
||||
case 'complex':
|
||||
return {
|
||||
/* complex node implementation */
|
||||
}
|
||||
return {/* complex node implementation */}
|
||||
default:
|
||||
throw new Error(`Unknown node type: ${type}`)
|
||||
}
|
||||
@@ -347,9 +343,7 @@ Services in ComfyUI can be tested effectively using different approaches dependi
|
||||
|
||||
```typescript
|
||||
// Manual instantiation required
|
||||
const mockData = [
|
||||
/* test data */
|
||||
]
|
||||
const mockData = [/* test data */]
|
||||
const service = new NodeSearchService(mockData)
|
||||
```
|
||||
|
||||
|
||||
@@ -147,9 +147,7 @@ describe('useAuthStore', () => {
|
||||
let authStateCallback: (user: User | null) => void
|
||||
let idTokenCallback: (user: User | null) => void
|
||||
|
||||
const mockAuth: MockAuth = {
|
||||
/* mock Auth object */
|
||||
}
|
||||
const mockAuth: MockAuth = {/* mock Auth object */}
|
||||
|
||||
const mockUser: MockUser = {
|
||||
uid: 'test-user-id',
|
||||
|
||||
@@ -132,9 +132,9 @@ export function fixBadLinks(
|
||||
// We can set to null (delete), so undefined means we haven't set it at all.
|
||||
if (patchedNode['inputs']![slot] !== undefined) {
|
||||
logger.log(
|
||||
` > Already set ${node.id}.inputs[${slot}] to ${patchedNode[
|
||||
'inputs'
|
||||
]![slot]!} Skipping.`
|
||||
` > Already set ${node.id}.inputs[${slot}] to ${
|
||||
patchedNode['inputs']![slot]!
|
||||
} Skipping.`
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user