[feat] Add Storybook configuration and settings panel stories

- Set up Storybook 9.1.2 with Vue 3, PrimeVue, and Tailwind CSS support
- Created comprehensive stories for settings panel components:
  - SettingItem: Various input types (boolean, text, number, slider, combo, color)
  - SettingGroup: Grouped settings with dividers and different categories
  - SettingsPanel: Multiple setting groups and empty states
  - PanelTemplate: Reusable panel layout with header/footer slots
  - AboutPanel: System information and project badges
  - SettingDialogContent: Complete dialog with responsive layouts
- Updated TypeScript configuration to include Storybook files

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
snomiao
2025-08-16 06:20:42 +00:00
parent 26dbbb1395
commit 945e8e9984
12 changed files with 1169 additions and 186 deletions

1
.gitignore vendored
View File

@@ -75,4 +75,3 @@ vite.config.mts.timestamp-*.mjs
*storybook.log
storybook-static

View File

@@ -1,96 +1,63 @@
import type { StorybookConfig } from '@storybook/vue3-vite'
import vue from '@vitejs/plugin-vue'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
import type { InlineConfig } from 'vite'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: ['@storybook/addon-docs'],
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-docs',
'@storybook/addon-controls',
'@storybook/addon-actions',
'@storybook/addon-viewport',
'@storybook/addon-backgrounds'
],
framework: {
name: '@storybook/vue3-vite',
options: {}
},
async viteFinal(config) {
// Use dynamic import to avoid CJS deprecation warning
viteFinal: async (config) => {
const { mergeConfig } = await import('vite')
// Filter out any plugins that might generate import maps
if (config.plugins) {
config.plugins = config.plugins.filter((plugin: any) => {
if (plugin && plugin.name && plugin.name.includes('import-map')) {
return false
}
return true
})
}
return mergeConfig(config, {
// Replace plugins entirely to avoid inheritance issues
plugins: [
// Only include plugins we explicitly need for Storybook
vue(),
Icons({
compiler: 'vue3',
customCollections: {
comfy: FileSystemIconLoader(
process.cwd() + '/src/assets/icons/custom'
)
comfy: FileSystemIconLoader('../src/assets/icons/custom')
}
}),
Components({
dts: false, // Disable dts generation in Storybook
dts: false, // Disable DTS generation for Storybook
resolvers: [
IconsResolver({
customCollections: ['comfy']
})
],
dirs: [
process.cwd() + '/src/components',
process.cwd() + '/src/layout',
process.cwd() + '/src/views'
],
dirs: ['../src/components', '../src/layout', '../src/views'],
deep: true,
extensions: ['vue']
})
// Note: Explicitly NOT including generateImportMapPlugin to avoid externalization
],
server: {
allowedHosts: true
},
resolve: {
alias: {
'@': process.cwd() + '/src'
'@': new URL('../src', import.meta.url).pathname
}
},
build: {
rollupOptions: {
external: () => {
// Don't externalize any modules in Storybook build
// This ensures PrimeVue and other dependencies are bundled
return false
},
onwarn: (warning, warn) => {
// Suppress specific warnings
if (
warning.code === 'UNUSED_EXTERNAL_IMPORT' &&
warning.message?.includes('resolveComponent')
) {
return
}
// Suppress Storybook font asset warnings
if (
warning.code === 'UNRESOLVED_IMPORT' &&
warning.message?.includes('nunito-sans')
) {
return
}
warn(warning)
}
},
chunkSizeWarningLimit: 1000
define: {
...config.define,
global: 'globalThis',
__COMFYUI_FRONTEND_VERSION__: JSON.stringify('1.26.4'),
__SENTRY_ENABLED__: JSON.stringify(false),
__SENTRY_DSN__: JSON.stringify(''),
__ALGOLIA_APP_ID__: JSON.stringify(''),
__ALGOLIA_API_KEY__: JSON.stringify(''),
__USE_PROD_CONFIG__: JSON.stringify(false)
}
} satisfies InlineConfig)
})
}
}
export default config

View File

@@ -1,53 +1,57 @@
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
import { setup } from '@storybook/vue3'
import type { Preview } from '@storybook/vue3-vite'
import { createPinia } from 'pinia'
import 'primeicons/primeicons.css'
import PrimeVue from 'primevue/config'
import ConfirmationService from 'primevue/confirmationservice'
import ToastService from 'primevue/toastservice'
import Tooltip from 'primevue/tooltip'
import { createI18n } from 'vue-i18n'
import '../public/materialdesignicons.min.css'
// Import styles
import '../src/assets/css/style.css'
import { i18n } from '../src/i18n'
import '../src/lib/litegraph/public/css/litegraph.css'
import { useWidgetStore } from '../src/stores/widgetStore'
import { useColorPaletteStore } from '../src/stores/workspace/colorPaletteStore'
const ComfyUIPreset = definePreset(Aura, {
semantic: {
// @ts-expect-error fix me
primary: Aura['primitive'].blue
}
})
// Mock Firebase for Storybook
const mockFirebase = {
auth: () => ({
currentUser: null,
onAuthStateChanged: () => () => {}
}),
firestore: () => ({})
}
// Setup Vue app for Storybook
// Setup Vue plugins for Storybook
setup((app) => {
app.directive('tooltip', Tooltip)
const pinia = createPinia()
app.use(pinia)
// Initialize stores
useColorPaletteStore(pinia)
useWidgetStore(pinia)
app.use(i18n)
app.use(PrimeVue, {
theme: {
preset: ComfyUIPreset,
options: {
prefix: 'p',
cssLayer: {
name: 'primevue',
order: 'primevue, tailwind-utilities'
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
messages: {
en: {
g: {
searchSettings: 'Search Settings',
noResultsFound: 'No Results Found',
searchFailedMessage: 'Try adjusting your search terms',
experimental: 'Experimental',
loadingPanel: 'Loading {panel}...'
},
darkModeSelector: '.dark-theme, :root:has(.dark-theme)'
settings: {},
settingsCategories: {}
}
}
})
app.use(ConfirmationService)
app.use(pinia)
app.use(i18n)
app.use(PrimeVue, {
theme: {
preset: null // Will use CSS for theming
}
})
app.use(ToastService)
app.directive('tooltip', Tooltip)
// Provide mock services
app.provide('firebase', mockFirebase)
})
// Dark theme decorator
@@ -77,9 +81,40 @@ const preview: Preview = {
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#0a0a0a' }
{
name: 'light',
value: '#ffffff'
},
{
name: 'dark',
value: '#1a1a1a'
}
]
},
viewport: {
viewports: {
small: {
name: 'Small',
styles: {
width: '640px',
height: '480px'
}
},
medium: {
name: 'Medium',
styles: {
width: '768px',
height: '1024px'
}
},
large: {
name: 'Large',
styles: {
width: '1024px',
height: '768px'
}
}
}
}
},
globalTypes: {

235
package-lock.json generated
View File

@@ -59,8 +59,13 @@
"@lobehub/i18n-cli": "^1.20.0",
"@pinia/testing": "^0.1.5",
"@playwright/test": "^1.52.0",
"@storybook/addon-docs": "^9.1.1",
"@storybook/vue3-vite": "^9.1.1",
"@storybook/addon-actions": "^9.0.8",
"@storybook/addon-backgrounds": "^9.0.8",
"@storybook/addon-controls": "^9.0.8",
"@storybook/addon-docs": "^9.1.2",
"@storybook/addon-onboarding": "^9.1.2",
"@storybook/addon-viewport": "^9.0.8",
"@storybook/vue3-vite": "^9.1.2",
"@trivago/prettier-plugin-sort-imports": "^5.2.0",
"@types/dompurify": "^3.0.5",
"@types/fs-extra": "^11.0.4",
@@ -74,7 +79,7 @@
"eslint": "^9.12.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-storybook": "^9.1.1",
"eslint-plugin-storybook": "^9.1.2",
"eslint-plugin-unused-imports": "^4.1.4",
"eslint-plugin-vue": "^9.27.0",
"fs-extra": "^11.2.0",
@@ -86,7 +91,7 @@
"lint-staged": "^15.2.7",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"storybook": "^9.1.1",
"storybook": "^9.1.2",
"tailwindcss": "^3.4.4",
"tsx": "^4.15.6",
"typescript": "^5.4.5",
@@ -139,9 +144,9 @@
"dev": true
},
"node_modules/@adobe/css-tools": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz",
"integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==",
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz",
"integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==",
"dev": true,
"license": "MIT"
},
@@ -4325,17 +4330,50 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@storybook/addon-actions": {
"version": "9.0.8",
"resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-9.0.8.tgz",
"integrity": "sha512-LFePu7PPnWN0Il/uoUpmA5T0J0C7d6haJIbg0pXrjxW2MQVSYXE4S4LSUz8fOImltBDV3xAl6tLPYHFj6VcrOA==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
}
},
"node_modules/@storybook/addon-backgrounds": {
"version": "9.0.8",
"resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-9.0.8.tgz",
"integrity": "sha512-4Vvr4wYHtiZ8UVWdCahK0XEMU4zNgInnNcVQ31YkUg41MVSY+aoZqtNuxOuRbFzUtjL9/aVsbY0Sg9Lp1/EJ4g==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
}
},
"node_modules/@storybook/addon-controls": {
"version": "9.0.8",
"resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-9.0.8.tgz",
"integrity": "sha512-6MY9QeBv2vNmBXH+ONmbpp/Gu/odSxriN1+BAY+il9OyXZBMq3OiDsjoH7xY5V7PGr+0XhZfOLkamvx3q+lQTg==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
}
},
"node_modules/@storybook/addon-docs": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.1.tgz",
"integrity": "sha512-CzgvTy3V5X4fe+VPkiZVwPKARlpEBDAKte8ajLAlHJQLFpADdYrBRQ0se6I+kcxva7rZQzdhuH7qjXMDRVcfnw==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.2.tgz",
"integrity": "sha512-U3eHJ8lQFfEZ/OcgdKkUBbW2Y2tpAsHfy8lQOBgs5Pgj9biHEJcUmq+drOS/sJhle673eoBcUFmspXulI4KP1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@mdx-js/react": "^3.0.0",
"@storybook/csf-plugin": "9.1.1",
"@storybook/csf-plugin": "9.1.2",
"@storybook/icons": "^1.4.0",
"@storybook/react-dom-shim": "9.1.1",
"@storybook/react-dom-shim": "9.1.2",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"ts-dedent": "^2.0.0"
@@ -4345,7 +4383,7 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.1"
"storybook": "^9.1.2"
}
},
"node_modules/@storybook/addon-docs/node_modules/@storybook/icons": {
@@ -4363,9 +4401,9 @@
}
},
"node_modules/@storybook/addon-docs/node_modules/@storybook/react-dom-shim": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.1.tgz",
"integrity": "sha512-L+HCOXvOP+PwKrVS8od9aF+F4hO7zA0Nt1vnpbg2LeAHCxYghrjFVtioe7gSlzrlYdozQrPLY98a4OkDB7KGrw==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.2.tgz",
"integrity": "sha512-nw7BLAHCJswPZGsuL0Gs2AvFUWriusCTgPBmcHppSw/AqvT4XRFRDE+5q3j04/XKuZBrAA2sC4L+HuC0uzEChQ==",
"dev": true,
"license": "MIT",
"funding": {
@@ -4375,7 +4413,7 @@
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"storybook": "^9.1.1"
"storybook": "^9.1.2"
}
},
"node_modules/@storybook/addon-docs/node_modules/react": {
@@ -4408,14 +4446,39 @@
"dev": true,
"license": "MIT"
},
"node_modules/@storybook/addon-onboarding": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/addon-onboarding/-/addon-onboarding-9.1.2.tgz",
"integrity": "sha512-WfYIBmRtwUF13Hcu6BdsqATsAuBK0dwsz7O4tL0FGrIwY/vdzZ5jNzYvzzgilzlu9QiPvzEIBvs6X4BVulN3LQ==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.2"
}
},
"node_modules/@storybook/addon-viewport": {
"version": "9.0.8",
"resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-9.0.8.tgz",
"integrity": "sha512-HgIFDzNXvMx0zQBM5mhwBoAJlrF9KRlxNCZnJbqrFLCJO4Ps2PMtB0HRGHcg0gm3RLcqyps0DpiF7wll3udb7Q==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
}
},
"node_modules/@storybook/builder-vite": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.1.tgz",
"integrity": "sha512-rM0QOfykr39SFBRQnoAa5PU3xTHnJE1R5tigvjved1o7sumcfjrhqmEyAgNZv1SoRztOO92jwkTi7En6yheOKg==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.2.tgz",
"integrity": "sha512-5Y7e5wnSzFxCGP63UNRRZVoxHe1znU4dYXazJBobAlEcUPBk7A0sH2716tA6bS4oz92oG9tgvn1g996hRrw4ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@storybook/csf-plugin": "9.1.1",
"@storybook/csf-plugin": "9.1.2",
"ts-dedent": "^2.0.0"
},
"funding": {
@@ -4423,14 +4486,14 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.1",
"storybook": "^9.1.2",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/@storybook/csf-plugin": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.1.tgz",
"integrity": "sha512-MwdtvzzFpkard06pCfDrgRXZiBfWAQICdKh7kzpv1L8SwewsRgUr5WZQuEAVfYdSvCFJbWnNN4KirzPhe5ENCg==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.2.tgz",
"integrity": "sha512-bfMh6r+RieBLPWtqqYN70le2uTE4JzOYPMYSCagHykUti3uM/1vRFaZNkZtUsRy5GwEzE5jLdDXioG1lOEeT2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4441,7 +4504,7 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.1"
"storybook": "^9.1.2"
}
},
"node_modules/@storybook/csf-plugin/node_modules/unplugin": {
@@ -4466,9 +4529,9 @@
"license": "MIT"
},
"node_modules/@storybook/vue3": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/vue3/-/vue3-9.1.1.tgz",
"integrity": "sha512-eKY1wKKmFrO8IpgHIV7XAyv7WRvI9rdvni4niy0bcho7QLD27trmJ9lJ3mAwZ8rEpUjgYOSDi6i5/jangbZc4w==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/vue3/-/vue3-9.1.2.tgz",
"integrity": "sha512-aYLh6/DZEuoOtsn/qePb9I/kuzwZGy+mS/ELlFoj72vpJc4d21hKZfiepO5bZ3z73XK7nLmdMVQ2tIwvsin4Vw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4484,19 +4547,19 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.1",
"storybook": "^9.1.2",
"vue": "^3.0.0"
}
},
"node_modules/@storybook/vue3-vite": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@storybook/vue3-vite/-/vue3-vite-9.1.1.tgz",
"integrity": "sha512-JdQPPYCVxvw+hXEd27JH5ESmP7o86/dwNGiWvFUZLUp1utjrtXfr68QiFWRWjWRCe/4RvNgypX3tKoZMZ3ay6w==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/@storybook/vue3-vite/-/vue3-vite-9.1.2.tgz",
"integrity": "sha512-MSXNtSbY8dnlcSzmbjkzJs1gAmDVRH1b4lBYU8TPHb8YmFz9vdYi8JNjOFztEjcnFe6VPVeCys69MpzZqGF31g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@storybook/builder-vite": "9.1.1",
"@storybook/vue3": "9.1.1",
"@storybook/builder-vite": "9.1.2",
"@storybook/vue3": "9.1.2",
"find-package-json": "^1.2.0",
"magic-string": "^0.30.0",
"typescript": "^5.8.3",
@@ -4511,7 +4574,7 @@
"url": "https://opencollective.com/storybook"
},
"peerDependencies": {
"storybook": "^9.1.1",
"storybook": "^9.1.2",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
@@ -4561,9 +4624,9 @@
"peer": true
},
"node_modules/@testing-library/jest-dom": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.4.tgz",
"integrity": "sha512-xDXgLjVunjHqczScfkCJ9iyjdNOVHvvCdqHSSxwM9L0l/wHkTRum67SDc020uAlCoqktJplgO2AAQeLP1wgqDQ==",
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.7.0.tgz",
"integrity": "sha512-RI2e97YZ7MRa+vxP4UUnMuMFL2buSsf0ollxUbTgrbPLKhMn8KVTx7raS6DYjC7v1NDVrioOvaShxsguLNISCA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4571,7 +4634,6 @@
"aria-query": "^5.0.0",
"css.escape": "^1.5.1",
"dom-accessibility-api": "^0.6.3",
"lodash": "^4.17.21",
"picocolors": "^1.1.1",
"redent": "^3.0.0"
},
@@ -5211,9 +5273,9 @@
}
},
"node_modules/@types/react": {
"version": "19.1.9",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
"integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
"version": "19.1.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -5354,14 +5416,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
"integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.1.tgz",
"integrity": "sha512-8fZxek3ONTwBu9ptw5nCKqZOSkXshZB7uAxuFF0J/wTMkKydjXCzqqga7MlFMpHi9DoG4BadhmTkITBcg8Aybw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.39.0",
"@typescript-eslint/types": "^8.39.0",
"@typescript-eslint/tsconfig-utils": "^8.39.1",
"@typescript-eslint/types": "^8.39.1",
"debug": "^4.3.4"
},
"engines": {
@@ -5376,9 +5438,9 @@
}
},
"node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz",
"integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5408,9 +5470,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
"integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.1.tgz",
"integrity": "sha512-ePUPGVtTMR8XMU2Hee8kD0Pu4NDE1CN9Q1sxGSGd/mbOtGZDM7pnhXNJnzW63zk/q+Z54zVzj44HtwXln5CvHA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8422,7 +8484,6 @@
"version": "1.39.9",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.9.tgz",
"integrity": "sha512-9OtbkZmTA2Qc9groyA1PUNeb6knVTkvB2RSdr/LcJXDL8IdEakaxwXLHXa7VX/Wj0GmdMJPR3WhnPGhiP3E+qg==",
"license": "MIT",
"workspaces": [
"docs",
"benchmarks"
@@ -8627,9 +8688,9 @@
}
},
"node_modules/eslint-plugin-storybook": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.1.tgz",
"integrity": "sha512-g4/i9yW6cl4TCEMzYyALNvO3d/jB6TDvSs/Pmye7dHDrra2B7dgZJGzmEWILD62brVrLVHNoXgy2dNPtx80kmw==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.2.tgz",
"integrity": "sha512-EQa/kChrYrekxv36q3pvW57anqxMlAP4EdPXEDyA/EDrCQJaaTbWEdsMnVZtD744RjPP0M5wzaUjHbMhNooAwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8640,18 +8701,18 @@
},
"peerDependencies": {
"eslint": ">=8",
"storybook": "^9.1.1"
"storybook": "^9.1.2"
}
},
"node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
"integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.1.tgz",
"integrity": "sha512-RkBKGBrjgskFGWuyUGz/EtD8AF/GW49S21J8dvMzpJitOF1slLEbbHnNEtAHtnDAnx8qDEdRrULRnWVx27wGBw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0"
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -8662,9 +8723,9 @@
}
},
"node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
"integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz",
"integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8676,16 +8737,16 @@
}
},
"node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
"integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.1.tgz",
"integrity": "sha512-EKkpcPuIux48dddVDXyQBlKdeTPMmALqBUbEk38McWv0qVEZwOpVJBi7ugK5qVNgeuYjGNQxrrnoM/5+TI/BPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.39.0",
"@typescript-eslint/tsconfig-utils": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/visitor-keys": "8.39.0",
"@typescript-eslint/project-service": "8.39.1",
"@typescript-eslint/tsconfig-utils": "8.39.1",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/visitor-keys": "8.39.1",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -8705,16 +8766,16 @@
}
},
"node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.1.tgz",
"integrity": "sha512-VF5tZ2XnUSTuiqZFXCZfZs1cgkdd3O/sSYmdo2EpSyDlC86UM/8YytTmKnehOW3TGAlivqTDT6bS87B/GQ/jyg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0"
"@typescript-eslint/scope-manager": "8.39.1",
"@typescript-eslint/types": "8.39.1",
"@typescript-eslint/typescript-estree": "8.39.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -8729,13 +8790,13 @@
}
},
"node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
"integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
"version": "8.39.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.1.tgz",
"integrity": "sha512-W8FQi6kEh2e8zVhQ0eeRnxdvIoOkAp/CPAahcNio6nO9dsIwb9b34z90KOlheoyuVf6LSOEdjlkxSkapNEc+4A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/types": "8.39.1",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -16163,9 +16224,9 @@
"license": "MIT"
},
"node_modules/storybook": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.1.tgz",
"integrity": "sha512-q6GaGZdVZh6rjOdGnc+4hGTu8ECyhyjQDw4EZNxKtQjDO8kqtuxbFm8l/IP2l+zLVJAatGWKkaX9Qcd7QZxz+Q==",
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.2.tgz",
"integrity": "sha512-TYcq7WmgfVCAQge/KueGkVlM/+g33sQcmbATlC3X6y/g2FEeSSLGrb6E6d3iemht8oio+aY6ld3YOdAnMwx45Q==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -40,8 +40,13 @@
"@lobehub/i18n-cli": "^1.20.0",
"@pinia/testing": "^0.1.5",
"@playwright/test": "^1.52.0",
"@storybook/addon-docs": "^9.1.1",
"@storybook/vue3-vite": "^9.1.1",
"@storybook/addon-actions": "^9.0.8",
"@storybook/addon-backgrounds": "^9.0.8",
"@storybook/addon-controls": "^9.0.8",
"@storybook/addon-docs": "^9.1.2",
"@storybook/addon-onboarding": "^9.1.2",
"@storybook/addon-viewport": "^9.0.8",
"@storybook/vue3-vite": "^9.1.2",
"@trivago/prettier-plugin-sort-imports": "^5.2.0",
"@types/dompurify": "^3.0.5",
"@types/fs-extra": "^11.0.4",
@@ -55,7 +60,7 @@
"eslint": "^9.12.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.6",
"eslint-plugin-storybook": "^9.1.1",
"eslint-plugin-storybook": "^9.1.2",
"eslint-plugin-unused-imports": "^4.1.4",
"eslint-plugin-vue": "^9.27.0",
"fs-extra": "^11.2.0",
@@ -67,7 +72,7 @@
"lint-staged": "^15.2.7",
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"storybook": "^9.1.1",
"storybook": "^9.1.2",
"tailwindcss": "^3.4.4",
"tsx": "^4.15.6",
"typescript": "^5.4.5",

View File

@@ -0,0 +1,108 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import SettingDialogContent from './SettingDialogContent.vue'
const meta: Meta<typeof SettingDialogContent> = {
title: 'Components/Dialog/SettingDialogContent',
component: SettingDialogContent,
parameters: {
layout: 'fullscreen',
docs: {
description: {
component:
'The complete settings dialog content with sidebar navigation and tabbed panels.'
}
}
},
argTypes: {
defaultPanel: {
control: 'select',
options: [
'about',
'keybinding',
'extension',
'server-config',
'user',
'credits'
],
description: 'The default panel to show when the dialog opens'
}
},
decorators: [
() => ({
template:
'<div style="width: 100vw; height: 100vh; padding: 20px; background: #f5f5f5;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {}
}
export const AboutPanel: Story = {
args: {
defaultPanel: 'about'
}
}
export const KeybindingPanel: Story = {
args: {
defaultPanel: 'keybinding'
}
}
export const ExtensionPanel: Story = {
args: {
defaultPanel: 'extension'
}
}
export const ServerConfigPanel: Story = {
args: {
defaultPanel: 'server-config'
}
}
export const UserPanel: Story = {
args: {
defaultPanel: 'user'
}
}
export const CreditsPanel: Story = {
args: {
defaultPanel: 'credits'
}
}
// Responsive variants
export const Mobile: Story = {
args: {},
parameters: {
viewport: {
defaultViewport: 'mobile1'
}
}
}
export const Tablet: Story = {
args: {},
parameters: {
viewport: {
defaultViewport: 'tablet'
}
}
}
export const Desktop: Story = {
args: {},
parameters: {
viewport: {
defaultViewport: 'desktop'
}
}
}

View File

@@ -0,0 +1,63 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import AboutPanel from './AboutPanel.vue'
const meta: Meta<typeof AboutPanel> = {
title: 'Components/Setting/AboutPanel',
component: AboutPanel,
parameters: {
layout: 'padded',
docs: {
description: {
component:
'The About panel displays project information, badges, and system statistics.'
}
}
},
decorators: [
() => ({
template:
'<div style="max-width: 600px; min-height: 400px; padding: 16px;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {},
parameters: {
docs: {
description: {
story:
'The default About panel showing project badges and system information.'
}
}
}
}
export const WithSystemStats: Story = {
args: {},
parameters: {
docs: {
description: {
story: 'About panel with system statistics visible.'
}
}
}
}
export const Mobile: Story = {
args: {},
parameters: {
viewport: {
defaultViewport: 'mobile1'
},
docs: {
description: {
story: 'About panel optimized for mobile devices.'
}
}
}
}

View File

@@ -0,0 +1,197 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import PanelTemplate from './PanelTemplate.vue'
const meta: Meta<typeof PanelTemplate> = {
title: 'Components/Setting/PanelTemplate',
component: PanelTemplate,
parameters: {
layout: 'padded',
docs: {
description: {
component:
'A template component for settings panels that provides consistent layout with header, content, and footer slots.'
}
}
},
argTypes: {
value: {
control: 'text',
description: 'The value identifier for the tab panel'
},
class: {
control: 'text',
description: 'Additional CSS classes to apply'
}
},
decorators: [
() => ({
template:
'<div style="width: 600px; height: 400px; border: 1px solid #ddd;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
value: 'example-panel'
},
render: (args) => ({
components: { PanelTemplate },
setup() {
return { args }
},
template: `
<PanelTemplate v-bind="args">
<div class="p-4">
<h3 class="text-lg font-semibold mb-4">Panel Content</h3>
<p class="mb-4">This is the main content area of the panel.</p>
<div class="space-y-2">
<div class="p-2 bg-gray-100 rounded">Setting Item 1</div>
<div class="p-2 bg-gray-100 rounded">Setting Item 2</div>
<div class="p-2 bg-gray-100 rounded">Setting Item 3</div>
</div>
</div>
</PanelTemplate>
`
})
}
export const WithHeader: Story = {
args: {
value: 'header-panel'
},
render: (args) => ({
components: { PanelTemplate },
setup() {
return { args }
},
template: `
<PanelTemplate v-bind="args">
<template #header>
<div class="p-3 bg-blue-50 border border-blue-200 rounded mb-4">
<h4 class="text-blue-800 font-medium">Panel Header</h4>
<p class="text-blue-600 text-sm">This is a header message for the panel.</p>
</div>
</template>
<div class="p-4">
<h3 class="text-lg font-semibold mb-4">Panel Content</h3>
<p>Content with a header message above.</p>
</div>
</PanelTemplate>
`
})
}
export const WithFooter: Story = {
args: {
value: 'footer-panel'
},
render: (args) => ({
components: { PanelTemplate },
setup() {
return { args }
},
template: `
<PanelTemplate v-bind="args">
<div class="p-4">
<h3 class="text-lg font-semibold mb-4">Panel Content</h3>
<p>Content with a footer below.</p>
</div>
<template #footer>
<div class="p-3 bg-gray-50 border-t">
<div class="flex justify-end space-x-2">
<button class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600">
Cancel
</button>
<button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Save
</button>
</div>
</div>
</template>
</PanelTemplate>
`
})
}
export const WithHeaderAndFooter: Story = {
args: {
value: 'full-panel'
},
render: (args) => ({
components: { PanelTemplate },
setup() {
return { args }
},
template: `
<PanelTemplate v-bind="args">
<template #header>
<div class="p-3 bg-green-50 border border-green-200 rounded mb-4">
<h4 class="text-green-800 font-medium">Important Notice</h4>
<p class="text-green-600 text-sm">Please review all settings before saving.</p>
</div>
</template>
<div class="p-4">
<h3 class="text-lg font-semibold mb-4">Settings Content</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium mb-1">Setting 1</label>
<input type="text" class="w-full p-2 border rounded" value="Default value" />
</div>
<div>
<label class="block text-sm font-medium mb-1">Setting 2</label>
<select class="w-full p-2 border rounded">
<option>Option 1</option>
<option>Option 2</option>
</select>
</div>
</div>
</div>
<template #footer>
<div class="p-3 bg-gray-50 border-t">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">Changes will be saved automatically</span>
<button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Apply Changes
</button>
</div>
</div>
</template>
</PanelTemplate>
`
})
}
export const LongContent: Story = {
args: {
value: 'long-panel'
},
render: (args) => ({
components: { PanelTemplate },
setup() {
return { args }
},
template: `
<PanelTemplate v-bind="args">
<div class="p-4">
<h3 class="text-lg font-semibold mb-4">Scrollable Content</h3>
<div class="space-y-4">
${Array.from(
{ length: 20 },
(_, i) => `
<div class="p-3 bg-gray-100 rounded">
<h4 class="font-medium">Setting Group ${i + 1}</h4>
<p class="text-sm text-gray-600">This is setting group ${i + 1} with some description text.</p>
</div>
`
).join('')}
</div>
</div>
</PanelTemplate>
`
})
}

View File

@@ -0,0 +1,150 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import type { SettingParams } from '@/types/settingTypes'
import SettingGroup from './SettingGroup.vue'
const meta: Meta<typeof SettingGroup> = {
title: 'Components/Setting/SettingGroup',
component: SettingGroup,
parameters: {
layout: 'padded'
},
argTypes: {
group: {
control: 'object',
description: 'The setting group configuration'
},
divider: {
control: 'boolean',
description: 'Show divider above the group'
}
},
decorators: [
() => ({
template: '<div style="max-width: 500px; padding: 16px;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
const createMockSetting = (
overrides: Partial<SettingParams> = {}
): SettingParams => ({
id: 'test.setting' as any,
name: 'Test Setting',
type: 'boolean',
defaultValue: false,
...overrides
})
export const BasicGroup: Story = {
args: {
group: {
label: 'Basic Settings',
settings: [
createMockSetting({
id: 'basic.enable' as any,
name: 'Enable Feature',
type: 'boolean',
defaultValue: true,
tooltip: 'Enable or disable this feature'
}),
createMockSetting({
id: 'basic.name' as any,
name: 'Display Name',
type: 'text',
defaultValue: 'My App',
tooltip: 'The name to display in the title bar'
})
]
},
divider: false
}
}
export const GroupWithDivider: Story = {
args: {
group: {
label: 'Advanced Settings',
settings: [
createMockSetting({
id: 'advanced.debug' as any,
name: 'Debug Mode',
type: 'boolean',
defaultValue: false,
experimental: true,
tooltip: 'Enable debug logging and developer tools'
}),
createMockSetting({
id: 'advanced.timeout' as any,
name: 'Request Timeout (ms)',
type: 'number',
defaultValue: 5000,
tooltip: 'How long to wait for requests',
attrs: {
min: 1000,
max: 30000,
step: 500
}
})
]
},
divider: true
}
}
export const PerformanceGroup: Story = {
args: {
group: {
label: 'Performance',
settings: [
createMockSetting({
id: 'perf.threads' as any,
name: 'Worker Threads',
type: 'slider',
defaultValue: 4,
tooltip: 'Number of worker threads to use',
attrs: {
min: 1,
max: 16,
step: 1
}
}),
createMockSetting({
id: 'perf.quality' as any,
name: 'Render Quality',
type: 'combo',
defaultValue: 'high',
tooltip: 'Rendering quality level',
options: [
{ text: 'Low', value: 'low' },
{ text: 'Medium', value: 'medium' },
{ text: 'High', value: 'high' },
{ text: 'Ultra', value: 'ultra' }
]
}),
createMockSetting({
id: 'perf.vsync' as any,
name: 'V-Sync',
type: 'boolean',
defaultValue: true,
tooltip: 'Enable vertical synchronization'
})
]
},
divider: false
}
}
export const EmptyGroup: Story = {
args: {
group: {
label: 'Empty Group',
settings: []
},
divider: false
}
}

View File

@@ -0,0 +1,159 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import type { SettingParams } from '@/types/settingTypes'
import SettingItem from './SettingItem.vue'
const meta: Meta<typeof SettingItem> = {
title: 'Components/Setting/SettingItem',
component: SettingItem,
parameters: {
layout: 'padded'
},
argTypes: {
setting: {
control: 'object',
description: 'The setting configuration object'
}
},
decorators: [
() => ({
template: '<div style="max-width: 500px; padding: 16px;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
const createMockSetting = (
overrides: Partial<SettingParams> = {}
): SettingParams => ({
id: 'test.setting' as any,
name: 'Test Setting',
type: 'boolean',
defaultValue: false,
tooltip: 'This is a test setting for demonstration purposes',
...overrides
})
export const BooleanSetting: Story = {
args: {
setting: createMockSetting({
name: 'Enable Feature',
type: 'boolean',
defaultValue: true,
tooltip: 'Toggle this feature on or off'
})
}
}
export const TextSetting: Story = {
args: {
setting: createMockSetting({
name: 'API Endpoint',
type: 'text',
defaultValue: 'https://api.example.com',
tooltip: 'The API endpoint to connect to'
})
}
}
export const NumberSetting: Story = {
args: {
setting: createMockSetting({
name: 'Max Connections',
type: 'number',
defaultValue: 10,
tooltip: 'Maximum number of concurrent connections',
attrs: {
min: 1,
max: 100,
step: 1
}
})
}
}
export const SliderSetting: Story = {
args: {
setting: createMockSetting({
name: 'Volume Level',
type: 'slider',
defaultValue: 50,
tooltip: 'Adjust the volume level',
attrs: {
min: 0,
max: 100,
step: 5
}
})
}
}
export const ComboSetting: Story = {
args: {
setting: createMockSetting({
name: 'Theme',
type: 'combo',
defaultValue: 'dark',
tooltip: 'Select your preferred theme',
options: [
{ text: 'Light', value: 'light' },
{ text: 'Dark', value: 'dark' },
{ text: 'Auto', value: 'auto' }
]
})
}
}
export const ColorSetting: Story = {
args: {
setting: createMockSetting({
name: 'Accent Color',
type: 'color',
defaultValue: '#007bff',
tooltip: 'Choose your accent color'
})
}
}
export const ExperimentalSetting: Story = {
args: {
setting: createMockSetting({
name: 'Experimental Feature',
type: 'boolean',
defaultValue: false,
experimental: true,
tooltip: 'This feature is experimental and may change'
})
}
}
export const WithLanguageTag: Story = {
args: {
setting: createMockSetting({
id: 'Comfy.Locale' as any,
name: 'Language',
type: 'combo',
defaultValue: 'en',
tooltip: 'Select your preferred language',
options: [
{ text: 'English', value: 'en' },
{ text: 'Spanish', value: 'es' },
{ text: 'French', value: 'fr' }
]
})
}
}
export const InteractiveBoolean: Story = {
args: {
setting: createMockSetting({
name: 'Interactive Boolean',
type: 'boolean',
defaultValue: false,
tooltip: 'Click to toggle this setting'
})
}
}

View File

@@ -0,0 +1,238 @@
import type { Meta, StoryObj } from '@storybook/vue3-vite'
import type { ISettingGroup, SettingParams } from '@/types/settingTypes'
import SettingsPanel from './SettingsPanel.vue'
const meta: Meta<typeof SettingsPanel> = {
title: 'Components/Setting/SettingsPanel',
component: SettingsPanel,
parameters: {
layout: 'padded'
},
argTypes: {
settingGroups: {
control: 'object',
description: 'Array of setting groups to display'
}
},
decorators: [
() => ({
template: '<div style="max-width: 600px; padding: 16px;"><story /></div>'
})
]
}
export default meta
type Story = StoryObj<typeof meta>
const createMockSetting = (
overrides: Partial<SettingParams> = {}
): SettingParams => ({
id: 'test.setting' as any,
name: 'Test Setting',
type: 'boolean',
defaultValue: false,
...overrides
})
const mockGeneralSettings: ISettingGroup = {
label: 'General',
settings: [
createMockSetting({
id: 'Comfy.Locale' as any,
name: 'Language',
type: 'combo',
defaultValue: 'en',
tooltip: 'Select your preferred language',
options: [
{ text: 'English', value: 'en' },
{ text: 'Spanish', value: 'es' },
{ text: 'French', value: 'fr' },
{ text: 'German', value: 'de' }
]
}),
createMockSetting({
id: 'Comfy.AutoSave' as any,
name: 'Auto Save',
type: 'boolean',
defaultValue: true,
tooltip: 'Automatically save your work'
}),
createMockSetting({
id: 'Comfy.AutoSaveInterval' as any,
name: 'Auto Save Interval (seconds)',
type: 'number',
defaultValue: 30,
tooltip: 'How often to auto save in seconds',
attrs: {
min: 10,
max: 300,
step: 5
}
})
]
}
const mockAppearanceSettings: ISettingGroup = {
label: 'Appearance',
settings: [
createMockSetting({
id: 'Comfy.ColorPalette' as any,
name: 'Color Palette',
type: 'combo',
defaultValue: 'dark',
tooltip: 'Choose your color theme',
options: [
{ text: 'Dark', value: 'dark' },
{ text: 'Light', value: 'light' },
{ text: 'Arc', value: 'arc' },
{ text: 'Nord', value: 'nord' }
]
}),
createMockSetting({
id: 'Comfy.AccentColor' as any,
name: 'Accent Color',
type: 'color',
defaultValue: '#007bff',
tooltip: 'Choose your accent color'
}),
createMockSetting({
id: 'Comfy.NodeOpacity' as any,
name: 'Node Opacity',
type: 'slider',
defaultValue: 80,
tooltip: 'Adjust node transparency',
attrs: {
min: 10,
max: 100,
step: 10
}
})
]
}
const mockPerformanceSettings: ISettingGroup = {
label: 'Performance',
settings: [
createMockSetting({
id: 'Comfy.MaxConcurrentTasks' as any,
name: 'Max Concurrent Tasks',
type: 'number',
defaultValue: 4,
tooltip: 'Maximum number of tasks to run simultaneously',
attrs: {
min: 1,
max: 16
}
}),
createMockSetting({
id: 'Comfy.EnableGPUAcceleration' as any,
name: 'GPU Acceleration',
type: 'boolean',
defaultValue: true,
tooltip: 'Enable GPU acceleration for better performance',
experimental: true
}),
createMockSetting({
id: 'Comfy.CacheSize' as any,
name: 'Cache Size (MB)',
type: 'slider',
defaultValue: 512,
tooltip: 'Amount of memory to use for caching',
attrs: {
min: 128,
max: 2048,
step: 128
}
})
]
}
export const EmptyPanel: Story = {
args: {
settingGroups: []
}
}
export const SingleGroup: Story = {
args: {
settingGroups: [mockGeneralSettings]
}
}
export const MultipleGroups: Story = {
args: {
settingGroups: [
mockGeneralSettings,
mockAppearanceSettings,
mockPerformanceSettings
]
}
}
export const AppearanceOnly: Story = {
args: {
settingGroups: [mockAppearanceSettings]
}
}
export const PerformanceOnly: Story = {
args: {
settingGroups: [mockPerformanceSettings]
}
}
export const MixedInputTypes: Story = {
args: {
settingGroups: [
{
label: 'Mixed Settings',
settings: [
createMockSetting({
id: 'mixed.boolean' as any,
name: 'Boolean Setting',
type: 'boolean',
defaultValue: true
}),
createMockSetting({
id: 'mixed.text' as any,
name: 'Text Setting',
type: 'text',
defaultValue: 'Default text'
}),
createMockSetting({
id: 'mixed.number' as any,
name: 'Number Setting',
type: 'number',
defaultValue: 42
}),
createMockSetting({
id: 'mixed.slider' as any,
name: 'Slider Setting',
type: 'slider',
defaultValue: 75,
attrs: { min: 0, max: 100 }
}),
createMockSetting({
id: 'mixed.combo' as any,
name: 'Combo Setting',
type: 'combo',
defaultValue: 'option2',
options: [
{ text: 'Option 1', value: 'option1' },
{ text: 'Option 2', value: 'option2' },
{ text: 'Option 3', value: 'option3' }
]
}),
createMockSetting({
id: 'mixed.color' as any,
name: 'Color Setting',
type: 'color',
defaultValue: '#ff6b35'
})
]
}
]
}
}

View File

@@ -16,6 +16,7 @@
"scripts/**/*.js",
"scripts/**/*.ts",
"tests-ui/**/*.ts",
".storybook/**/*.ts"
".storybook/**/*.ts",
"src/**/*.stories.ts"
]
}