diff --git a/.claude/commands/setup_repo.md b/.claude/commands/setup_repo.md index d82e22ec61..71dee96a51 100644 --- a/.claude/commands/setup_repo.md +++ b/.claude/commands/setup_repo.md @@ -122,7 +122,7 @@ echo " pnpm build - Build for production" echo " pnpm test:unit - Run unit tests" echo " pnpm typecheck - Run TypeScript checks" echo " pnpm lint - Run ESLint" -echo " pnpm format - Format code with Prettier" +echo " pnpm format - Format code with oxfmt" echo "" echo "Next steps:" echo "1. Run 'pnpm dev' to start developing" diff --git a/.github/workflows/ci-lint-format.yaml b/.github/workflows/ci-lint-format.yaml index 3ce6d6aa9f..c97f6255ca 100644 --- a/.github/workflows/ci-lint-format.yaml +++ b/.github/workflows/ci-lint-format.yaml @@ -42,7 +42,7 @@ jobs: - name: Run Stylelint with auto-fix run: pnpm stylelint:fix - - name: Run Prettier with auto-format + - name: Run oxfmt with auto-format run: pnpm format - name: Check for changes @@ -60,7 +60,7 @@ jobs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add . - git commit -m "[automated] Apply ESLint and Prettier fixes" + git commit -m "[automated] Apply ESLint and Oxfmt fixes" git push - name: Final validation @@ -80,7 +80,7 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Prettier formatting' + body: '## 🔧 Auto-fixes Applied\n\nThis PR has been automatically updated to fix linting and formatting issues.\n\n**⚠️ Important**: Your local branch is now behind. Run `git pull` before making additional changes to avoid conflicts.\n\n### Changes made:\n- ESLint auto-fixes\n- Oxfmt formatting' }) - name: Comment on PR about manual fix needed diff --git a/.i18nrc.cjs b/.i18nrc.cjs index 86ce06eaa3..4369f0a707 100644 --- a/.i18nrc.cjs +++ b/.i18nrc.cjs @@ -1,7 +1,7 @@ // This file is intentionally kept in CommonJS format (.cjs) // to resolve compatibility issues with dependencies that require CommonJS. // Do not convert this file to ESModule format unless all dependencies support it. -const { defineConfig } = require('@lobehub/i18n-cli'); +const { defineConfig } = require('@lobehub/i18n-cli') module.exports = defineConfig({ modelName: 'gpt-4.1', @@ -10,7 +10,19 @@ module.exports = defineConfig({ entry: 'src/locales/en', entryLocale: 'en', output: 'src/locales', - outputLocales: ['zh', 'zh-TW', 'ru', 'ja', 'ko', 'fr', 'es', 'ar', 'tr', 'pt-BR', 'fa'], + outputLocales: [ + 'zh', + 'zh-TW', + 'ru', + 'ja', + 'ko', + 'fr', + 'es', + 'ar', + 'tr', + 'pt-BR', + 'fa' + ], reference: `Special names to keep untranslated: flux, photomaker, clip, vae, cfg, stable audio, stable cascade, stable zero, controlnet, lora, HiDream, Civitai, Hugging Face. 'latent' is the short form of 'latent space'. 'mask' is in the context of image processing. @@ -26,4 +38,4 @@ module.exports = defineConfig({ - Use Arabic-Indic numerals (۰-۹) for numbers where appropriate. - Maintain consistency with terminology used in Persian software and design applications. ` -}); +}) diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000000..5da4febe23 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,20 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "singleQuote": true, + "tabWidth": 2, + "semi": false, + "trailingComma": "none", + "printWidth": 80, + "ignorePatterns": [ + "packages/registry-types/src/comfyRegistryTypes.ts", + "src/types/generatedManagerTypes.ts", + "**/*.md", + "**/*.json", + "**/*.css", + "**/*.yaml", + "**/*.yml", + "**/*.html", + "**/*.svg", + "**/*.xml" + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 4403edd8ec..0000000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -packages/registry-types/src/comfyRegistryTypes.ts -src/types/generatedManagerTypes.ts diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index aa43a43ac0..0000000000 --- a/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "singleQuote": true, - "tabWidth": 2, - "semi": false, - "trailingComma": "none", - "printWidth": 80, - "importOrder": ["^@core/(.*)$", "", "^@/(.*)$", "^[./]"], - "importOrderSeparation": true, - "importOrderSortSpecifiers": true, - "plugins": ["@prettier/plugin-oxc", "@trivago/prettier-plugin-sort-imports"] -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 54f28d4002..9cbac42d76 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,25 +1,22 @@ { "recommendations": [ + "antfu.vite", "austenc.tailwind-docs", "bradlc.vscode-tailwindcss", "davidanson.vscode-markdownlint", "dbaeumer.vscode-eslint", + "donjayamanne.githistory", "eamodio.gitlens", - "esbenp.prettier-vscode", - "figma.figma-vscode-extension", "github.vscode-github-actions", "github.vscode-pull-request-github", "hbenl.vscode-test-explorer", + "kisstkondoros.vscode-codemetrics", "lokalise.i18n-ally", "ms-playwright.playwright", + "oxc.oxc-vscode", + "sonarsource.sonarlint-vscode", "vitest.explorer", "vue.volar", - "sonarsource.sonarlint-vscode", - "deque-systems.vscode-axe-linter", - "kisstkondoros.vscode-codemetrics", - "donjayamanne.githistory", - "wix.vscode-import-cost", - "prograhammer.tslint-vue", - "antfu.vite" + "wix.vscode-import-cost" ] } diff --git a/AGENTS.md b/AGENTS.md index da2953783c..9938865a94 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,10 +27,10 @@ See @docs/guidance/*.md for file-type-specific conventions (auto-loaded by glob) - Build output: `dist/` - Configs - `vite.config.mts` - - `vitest.config.ts` - `playwright.config.ts` - `eslint.config.ts` - - `.prettierrc` + - `.oxfmtrc.json` + - `.oxlintrc.json` - etc. ## Monorepo Architecture @@ -46,7 +46,7 @@ The project uses **Nx** for build orchestration and task management - `pnpm test:unit`: Run Vitest unit tests - `pnpm test:browser`: Run Playwright E2E tests (`browser_tests/`) - `pnpm lint` / `pnpm lint:fix`: Lint (ESLint) -- `pnpm format` / `pnpm format:check`: Prettier +- `pnpm format` / `pnpm format:check`: oxfmt - `pnpm typecheck`: Vue TSC type checking - `pnpm storybook`: Start Storybook development server @@ -72,7 +72,7 @@ The project uses **Nx** for build orchestration and task management - Composition API only - Tailwind 4 styling - Avoid ` diff --git a/src/components/sidebar/SidebarIcon.test.ts b/src/components/sidebar/SidebarIcon.test.ts index 7564e7bcdc..284a298251 100644 --- a/src/components/sidebar/SidebarIcon.test.ts +++ b/src/components/sidebar/SidebarIcon.test.ts @@ -1,6 +1,5 @@ import { mount } from '@vue/test-utils' import PrimeVue from 'primevue/config' -import OverlayBadge from 'primevue/overlaybadge' import Tooltip from 'primevue/tooltip' import { describe, expect, it } from 'vitest' import { createI18n } from 'vue-i18n' @@ -33,8 +32,7 @@ describe('SidebarIcon', () => { return mount(SidebarIcon, { global: { plugins: [PrimeVue, i18n], - directives: { tooltip: Tooltip }, - components: { OverlayBadge } + directives: { tooltip: Tooltip } }, props: { ...exampleProps, ...props }, ...options @@ -54,9 +52,9 @@ describe('SidebarIcon', () => { it('creates badge when iconBadge prop is set', () => { const badge = '2' const wrapper = mountSidebarIcon({ iconBadge: badge }) - const badgeEl = wrapper.findComponent(OverlayBadge) + const badgeEl = wrapper.find('.sidebar-icon-badge') expect(badgeEl.exists()).toBe(true) - expect(badgeEl.find('.p-badge').text()).toEqual(badge) + expect(badgeEl.text()).toEqual(badge) }) it('shows tooltip on hover', async () => { diff --git a/src/components/sidebar/SidebarIcon.vue b/src/components/sidebar/SidebarIcon.vue index 3bf1ab6a7d..8c9b7062a4 100644 --- a/src/components/sidebar/SidebarIcon.vue +++ b/src/components/sidebar/SidebarIcon.vue @@ -18,22 +18,28 @@ > diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetWithControl.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetWithControl.vue index f571d6dade..e4e1220bf6 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetWithControl.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetWithControl.vue @@ -2,6 +2,7 @@ import { computed, defineAsyncComponent, ref, watch } from 'vue' import type { Component } from 'vue' +import Popover from '@/components/ui/Popover.vue' import Button from '@/components/ui/button/Button.vue' import type { SimplifiedControlWidget, @@ -19,8 +20,6 @@ const props = defineProps<{ const modelValue = defineModel() -const popover = ref() - const controlModel = ref(props.widget.controlWidget.value) const controlButtonIcon = computed(() => { @@ -37,24 +36,24 @@ const controlButtonIcon = computed(() => { }) watch(controlModel, props.widget.controlWidget.update) - -const togglePopover = (event: Event) => { - popover.value.toggle(event) -} - diff --git a/src/stores/executionStore.ts b/src/stores/executionStore.ts index 429adb807c..dda732af58 100644 --- a/src/stores/executionStore.ts +++ b/src/stores/executionStore.ts @@ -425,6 +425,18 @@ export const useExecutionStore = defineStore('execution', () => { initializingPromptIds.value = next } + function clearInitializationByPromptIds(promptIds: string[]) { + if (!promptIds.length) return + const current = initializingPromptIds.value + const toRemove = promptIds.filter((id) => current.has(id)) + if (!toRemove.length) return + const next = new Set(current) + for (const id of toRemove) { + next.delete(id) + } + initializingPromptIds.value = next + } + function isPromptInitializing( promptId: string | number | undefined ): boolean { @@ -650,6 +662,8 @@ export const useExecutionStore = defineStore('execution', () => { runningWorkflowCount, initializingPromptIds, isPromptInitializing, + clearInitializationByPromptId, + clearInitializationByPromptIds, bindExecutionEvents, unbindExecutionEvents, storePrompt, diff --git a/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts b/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts index 3e51167390..b3a92e5a33 100644 --- a/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts +++ b/src/workbench/extensions/manager/components/manager/packCard/PackCard.test.ts @@ -10,15 +10,22 @@ import type { RegistryPack } from '@/workbench/extensions/manager/types/comfyManagerTypes' +const translateMock = vi.hoisted(() => + vi.fn((key: string, choice?: number) => + typeof choice === 'number' ? `${key}-${choice}` : key + ) +) +const dateMock = vi.hoisted(() => vi.fn(() => '2024. 1. 1.')) + // Mock dependencies vi.mock('vue-i18n', () => ({ useI18n: vi.fn(() => ({ - d: vi.fn(() => '2024. 1. 1.'), - t: vi.fn((key: string) => key) + d: dateMock, + t: translateMock })), createI18n: vi.fn(() => ({ global: { - t: vi.fn((key: string) => key), + t: translateMock, te: vi.fn(() => true) } })) @@ -187,6 +194,18 @@ describe('PackCard', () => { // Should still render without errors expect(wrapper.exists()).toBe(true) }) + + it('should use localized singular/plural nodes label', () => { + const packWithNodes = { + ...mockNodePack, + comfy_nodes: ['node-a'] + } as MergedNodePack + + const wrapper = createWrapper({ nodePack: packWithNodes }) + + expect(wrapper.text()).toContain('g.nodesCount-1') + expect(translateMock).toHaveBeenCalledWith('g.nodesCount', 1) + }) }) describe('component structure', () => { diff --git a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue index 6900e71dfc..17ffdd1028 100644 --- a/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue +++ b/src/workbench/extensions/manager/components/manager/packCard/PackCard.vue @@ -36,8 +36,8 @@

-
- {{ nodesCount }} {{ $t('g.nodes') }} +
+ {{ nodesLabel }}
() -const { d } = useI18n() +const { d, t } = useI18n() const colorPaletteStore = useColorPaletteStore() const isLightTheme = computed( @@ -115,6 +115,9 @@ const isDisabled = computed( const nodesCount = computed(() => isMergedNodePack(nodePack) ? nodePack.comfy_nodes?.length : undefined ) +const nodesLabel = computed(() => + nodesCount.value ? t('g.nodesCount', nodesCount.value) : '' +) const publisherName = computed(() => { if (!nodePack) return null diff --git a/vite.config.mts b/vite.config.mts index e3246e608e..9769ec300a 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -51,6 +51,14 @@ const DISTRIBUTION: 'desktop' | 'localhost' | 'cloud' = ? 'cloud' : 'localhost' +// Nightly builds are from main branch; RC/stable builds are from core/* branches +// Can be overridden via IS_NIGHTLY env var for testing +const IS_NIGHTLY = + process.env.IS_NIGHTLY === 'true' || + (process.env.IS_NIGHTLY !== 'false' && + process.env.CI === 'true' && + process.env.GITHUB_REF_NAME === 'main') + // Disable Vue DevTools for production cloud distribution const DISABLE_VUE_PLUGINS = process.env.DISABLE_VUE_PLUGINS === 'true' || @@ -502,7 +510,8 @@ export default defineConfig({ __ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''), __ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''), __USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true', - __DISTRIBUTION__: JSON.stringify(DISTRIBUTION) + __DISTRIBUTION__: JSON.stringify(DISTRIBUTION), + __IS_NIGHTLY__: JSON.stringify(IS_NIGHTLY) }, resolve: { @@ -536,7 +545,8 @@ export default defineConfig({ '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**', - '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*' + '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint}.config.*', + '**/.{oxlintrc,oxfmtrc}.json' ], silent: 'passed-only' } diff --git a/vitest.setup.ts b/vitest.setup.ts index fca677557c..81c39723a5 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -46,6 +46,7 @@ globalThis.__ALGOLIA_APP_ID__ = '' globalThis.__ALGOLIA_API_KEY__ = '' globalThis.__USE_PROD_CONFIG__ = false globalThis.__DISTRIBUTION__ = 'localhost' +globalThis.__IS_NIGHTLY__ = false // Define runtime config for tests window.__CONFIG__ = {