mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 12:51:26 +08:00
feat(ui): new Switch (#11061)
* refactor(ui): migrate switch component from heroui to radix-ui replace heroui switch implementation with radix-ui for better maintainability update package.json and yarn.lock to include new dependency * fix(eslint): enable heroui import restriction for deprecated Switch component * refactor(ui): update Switch component props from isSelected/onValueChange to checked/onCheckedChange Standardize Switch component props across the codebase to use checked/onCheckedChange instead of isSelected/onValueChange for better consistency with common React patterns. Also updates loading state prop from isLoading to loading and removes size prop where unnecessary. The changes include: - Replacing isSelected with checked - Replacing onValueChange with onCheckedChange - Updating isLoading to loading - Removing redundant size props - Adjusting styling to accommodate new loading state * refactor(switch): improve switch component styling and structure - Add default values for loading and disabled props - Update styling classes and add group cursor pointer - Restructure loading indicator and thumb positioning - Wrap DescriptionSwitch children in flex container * refactor(ui): improve switch component structure and usage - Restructure DescriptionSwitch to use explicit props instead of children - Add label, description, and position props for better customization - Update all switch usages in SettingsTab to use new props format * refactor(primitives): simplify switch props by omitting children Remove redundant children prop from CustomSwitchProps since it's already omitted from the parent type * fix(switch): add useId for label accessibility in DescriptionSwitch Ensure proper label association with switch input by generating unique ID using React's useId hook * refactor(settings): remove commented out SettingRowTitleSmall components * refactor(SettingsTab): add todo comment for memoization optimization * feat(switch): add size prop to customize switch dimensions Add sm, md, and lg size options to the Switch component with corresponding styles. This allows for better visual consistency across different UI contexts. * style(ui): adjust switch component styling and theme colors update switch component layout and spacing to improve consistency modify secondary-foreground color variable to use correct semantic token * feat(switch): add new switch component styles and animations - Add new switch.css file with gradient and transition styles - Update switch.tsx component with new styling classes and animations - Remove loader icon in favor of animated gradient effect * fix(i18n): Auto update translations for PR #11061 * style(primitives): remove redundant border style from switch component * refactor(switch): remove switch.css and update switch component styles Remove deprecated switch.css file and migrate styles to inline tailwind classes. Update disabled state styling to use opacity instead of linear gradient for better consistency. * refactor(switch): simplify switch thumb implementation Replace complex div structure with svg for loading state Adjust disabled opacity and loading state styling * style(switch): adjust thumb size and positioning for better consistency * feat(switch): add storybook documentation for switch component Add comprehensive Storybook documentation for the Switch component, including: - Basic usage examples - Different states (checked, disabled, loading) - Size variations - DescriptionSwitch variant - Real-world usage scenarios - Accessibility examples - Form integration examples Also remove redundant box-content class from switch styles * fix(switch): adjust thumb positioning for md and lg sizes * style(primitives): improve switch component styling and spacing - Add padding to the container - Simplify label height logic - Update typography classes for better consistency - Adjust switch container alignment * feat(switch): add size prop to DescriptionSwitch component Add support for sm, md, and lg sizes to DescriptionSwitch component with responsive text sizing. Also includes comprehensive Storybook documentation with examples of all sizes and states. * style(switch): align label text to right when isLeftSide is true * refactor(stories): clean up DescriptionSwitch stories by removing unused imports and simplifying JSX * refactor(ui): rename CustomizedSwitch to Switch for consistency Simplify component naming by removing redundant 'Customized' prefix and aligning with common naming conventions * refactor(switch): extract switch root styles into cva variants Improve maintainability by using class-variance-authority to manage switch root styles and variants * refactor(switch): extract thumb variants into separate cva function Improve maintainability by moving switch thumb styling logic into a dedicated variants configuration. This makes the component more readable and easier to modify. * feat(switch): add classNames prop for custom styling Allow custom class names to be applied to switch root, thumb, and thumbSvg elements for more flexible styling options. * feat(switch): add loading animation variants for switch thumb Extract loading animation logic into separate cva variants for better maintainability and reusability --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
08d4509714
commit
4a38fd6ebc
@ -143,19 +143,31 @@ export default defineConfig([
|
||||
files: ['**/*.{ts,tsx,js,jsx}'],
|
||||
ignores: [],
|
||||
rules: {
|
||||
// 'no-restricted-imports': [
|
||||
// 'error',
|
||||
// {
|
||||
// paths: [
|
||||
// {
|
||||
// name: 'antd',
|
||||
// importNames: ['Flex', 'Switch', 'message', 'Button', 'Tooltip'],
|
||||
// message:
|
||||
// '❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
// {
|
||||
// name: 'antd',
|
||||
// importNames: ['Flex', 'Switch', 'message', 'Button', 'Tooltip'],
|
||||
// message:
|
||||
// '❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"'
|
||||
// },
|
||||
{
|
||||
name: 'antd',
|
||||
importNames: ['Switch'],
|
||||
message:
|
||||
'❌ Do not import this component from antd. Use our custom components instead: import { ... } from "@cherrystudio/ui"'
|
||||
},
|
||||
{
|
||||
name: '@heroui/react',
|
||||
importNames: ['Switch'],
|
||||
message:
|
||||
'❌ Do not import the component from heroui directly. It\'s deprecated.'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
// Schema key naming convention (cache & preferences)
|
||||
|
||||
@ -179,6 +179,7 @@
|
||||
"@opeoginni/github-copilot-openai-compatible": "^0.1.21",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@reduxjs/toolkit": "^2.2.5",
|
||||
"@shikijs/markdown-it": "^3.12.0",
|
||||
"@swc/plugin-styled-components": "^8.0.4",
|
||||
|
||||
@ -1,54 +1,178 @@
|
||||
import type { SwitchProps } from '@heroui/react'
|
||||
import { cn, Spinner, Switch } from '@heroui/react'
|
||||
import { cn } from '@cherrystudio/ui/utils'
|
||||
import * as SwitchPrimitive from '@radix-ui/react-switch'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import * as React from 'react'
|
||||
import { useId } from 'react'
|
||||
|
||||
const switchRootVariants = cva(
|
||||
[
|
||||
'cs-switch cs-switch-root',
|
||||
'group relative cursor-pointer peer inline-flex shrink-0 items-center rounded-full shadow-xs outline-none transition-all',
|
||||
'data-[state=unchecked]:bg-gray-500/20 data-[state=checked]:bg-primary',
|
||||
'disabled:cursor-not-allowed disabled:opacity-40',
|
||||
'focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
sm: ['w-9 h-5'],
|
||||
md: ['w-11 h-5.5'],
|
||||
lg: ['w-11 h-6']
|
||||
},
|
||||
loading: {
|
||||
false: null,
|
||||
true: ['bg-primary-hover!']
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 'md',
|
||||
loading: false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const switchThumbVariants = cva(
|
||||
[
|
||||
'cs-switch cs-switch-thumb',
|
||||
'pointer-events-none block rounded-full ring-0 transition-all data-[state=unchecked]:translate-x-0'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
sm: ['size-4.5 ml-[1px] data-[state=checked]:translate-x-4'],
|
||||
md: ['size-[19px] ml-0.5 data-[state=checked]:translate-x-[21px]'],
|
||||
lg: ['size-5 ml-[3px] data-[state=checked]:translate-x-4.5']
|
||||
},
|
||||
loading: {
|
||||
false: null,
|
||||
true: ['bg-primary-hover!']
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
size: 'sm',
|
||||
loading: true,
|
||||
className: 'size-3.5 ml-0.5 data-[state=checked]:translate-x-4.5'
|
||||
},
|
||||
{
|
||||
size: 'md',
|
||||
loading: true,
|
||||
className: 'size-4 ml-1 data-[state=checked]:translate-x-5'
|
||||
},
|
||||
{
|
||||
size: 'lg',
|
||||
loading: true,
|
||||
className: 'size-4.5 ml-1 data-[state=checked]:translate-x-4.5'
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
const switchThumbSvgVariants = cva(['transition-all'], {
|
||||
variants: {
|
||||
loading: {
|
||||
false: null,
|
||||
true: ['animate-spin']
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
loading: false
|
||||
}
|
||||
})
|
||||
|
||||
// Enhanced Switch component with loading state support
|
||||
interface CustomSwitchProps extends SwitchProps {
|
||||
isLoading?: boolean
|
||||
interface SwitchProps extends Omit<React.ComponentProps<typeof SwitchPrimitive.Root>, 'children'> {
|
||||
/** When true, displays a loading animation in the switch thumb. Defaults to false when undefined. */
|
||||
loading?: boolean
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
classNames?: {
|
||||
root?: string
|
||||
thumb?: string
|
||||
thumbSvg?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A customized Switch component based on HeroUI Switch
|
||||
* @see https://www.heroui.com/docs/components/switch#api
|
||||
* @param isLoading When true, displays a loading spinner in the switch thumb
|
||||
*/
|
||||
const CustomizedSwitch = ({ isLoading, children, ref, thumbIcon, ...props }: CustomSwitchProps) => {
|
||||
const finalThumbIcon = isLoading ? <Spinner size="sm" /> : thumbIcon
|
||||
|
||||
function Switch({ loading = false, size = 'md', className, classNames, ...props }: SwitchProps) {
|
||||
return (
|
||||
<Switch ref={ref} {...props} thumbIcon={finalThumbIcon}>
|
||||
{children}
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
const DescriptionSwitch = ({ children, ...props }: CustomSwitchProps) => {
|
||||
return (
|
||||
<CustomizedSwitch
|
||||
size="sm"
|
||||
classNames={{
|
||||
base: cn(
|
||||
'inline-flex w-full max-w-md flex-row-reverse items-center hover:bg-content2',
|
||||
'cursor-pointer justify-between gap-2 rounded-lg border-2 border-transparent py-2 pr-1',
|
||||
'data-[selected=true]:border-primary'
|
||||
),
|
||||
wrapper: 'p-0 h-4 overflow-visible',
|
||||
thumb: cn(
|
||||
'h-6 w-6 border-2 shadow-lg',
|
||||
'group-data-[hover=true]:border-primary',
|
||||
//selected
|
||||
'group-data-[selected=true]:ms-6',
|
||||
// pressed
|
||||
'group-data-[pressed=true]:w-7',
|
||||
'group-data-pressed:group-data-selected:ms-4'
|
||||
)
|
||||
}}
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(switchRootVariants({ size, loading }), className, classNames?.root)}
|
||||
{...props}>
|
||||
{children}
|
||||
</CustomizedSwitch>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={cn(switchThumbVariants({ size, loading }), classNames?.thumb)}>
|
||||
<svg
|
||||
width="inherit"
|
||||
height="inherit"
|
||||
viewBox="0 0 19 19"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn(switchThumbSvgVariants({ loading }), classNames?.thumbSvg)}>
|
||||
<path
|
||||
d="M9.5 0C14.7467 0 19 4.25329 19 9.5C19 14.7467 14.7467 19 9.5 19C4.25329 19 0 14.7467 0 9.5C0 4.25329 4.25329 0 9.5 0ZM9.5 6.33301C8.91711 6.33301 8.44445 6.8058 8.44434 7.38867V11.6113C8.44445 12.1942 8.91711 12.667 9.5 12.667C10.0829 12.667 10.5555 12.1942 10.5557 11.6113V7.38867C10.5555 6.8058 10.0829 6.33301 9.5 6.33301Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</SwitchPrimitive.Thumb>
|
||||
</SwitchPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
CustomizedSwitch.displayName = 'Switch'
|
||||
interface DescriptionSwitchProps extends SwitchProps {
|
||||
/** Text label displayed next to the switch. */
|
||||
label: string
|
||||
/** Optional helper text shown below the label. */
|
||||
description?: string
|
||||
/** Switch position relative to label. Defaults to 'right'. */
|
||||
position?: 'left' | 'right'
|
||||
}
|
||||
|
||||
export { DescriptionSwitch, CustomizedSwitch as Switch }
|
||||
export type { CustomSwitchProps as SwitchProps }
|
||||
// TODO: It's not finished. We need to use Typography components instead of native html element.
|
||||
const DescriptionSwitch = ({
|
||||
label,
|
||||
description,
|
||||
position = 'right',
|
||||
size = 'md',
|
||||
...props
|
||||
}: DescriptionSwitchProps) => {
|
||||
const isLeftSide = position === 'left'
|
||||
const id = useId()
|
||||
return (
|
||||
<div className={cn('flex w-full gap-3 justify-between p-4xs', isLeftSide && 'flex-row-reverse')}>
|
||||
<label className={cn('flex flex-col gap-5xs cursor-pointer')} htmlFor={id}>
|
||||
{/* TODO: use standard typography component */}
|
||||
<p
|
||||
className={cn(
|
||||
'font-medium tracking-normal',
|
||||
{
|
||||
'text-sm leading-4': size === 'sm',
|
||||
'text-md leading-4.5': size === 'md',
|
||||
'text-lg leading-5.5': size === 'lg'
|
||||
},
|
||||
isLeftSide && 'text-right'
|
||||
)}>
|
||||
{label}
|
||||
</p>
|
||||
{/* TODO: use standard typography component */}
|
||||
{description && (
|
||||
<span
|
||||
className={cn('text-foreground-secondary', {
|
||||
'text-[10px] leading-3': size === 'sm',
|
||||
'text-xs leading-3.5': size === 'md',
|
||||
'text-sm leading-4': size === 'lg'
|
||||
})}>
|
||||
{description}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<div className="flex justify-center items-center">
|
||||
<Switch id={id} size={size} {...props} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Switch.displayName = 'Switch'
|
||||
|
||||
export { DescriptionSwitch, Switch }
|
||||
export type { SwitchProps }
|
||||
|
||||
@ -0,0 +1,823 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { Bell, Eye, Lock, Moon, Shield, Wifi, Zap } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { DescriptionSwitch } from '../../../src/components/primitives/switch'
|
||||
|
||||
const meta: Meta<typeof DescriptionSwitch> = {
|
||||
title: 'Components/Primitives/DescriptionSwitch',
|
||||
component: DescriptionSwitch,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'An enhanced Switch component with integrated label and optional description text. Perfect for settings panels and preference forms where context is important. Built on top of the Radix UI Switch primitive with support for multiple sizes, loading states, and flexible positioning.'
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
label: {
|
||||
control: { type: 'text' },
|
||||
description: 'Text label displayed next to the switch (required)'
|
||||
},
|
||||
description: {
|
||||
control: { type: 'text' },
|
||||
description: 'Optional helper text shown below the label'
|
||||
},
|
||||
position: {
|
||||
control: { type: 'select' },
|
||||
options: ['left', 'right'],
|
||||
description: 'Switch position relative to label'
|
||||
},
|
||||
disabled: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Whether the switch is disabled'
|
||||
},
|
||||
loading: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'When true, displays a loading animation in the switch thumb'
|
||||
},
|
||||
size: {
|
||||
control: { type: 'select' },
|
||||
options: ['sm', 'md', 'lg'],
|
||||
description: 'The size of the switch'
|
||||
},
|
||||
defaultChecked: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Default checked state'
|
||||
},
|
||||
checked: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Checked state in controlled mode'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// Default
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<div className="w-[400px]">
|
||||
<DescriptionSwitch label="Enable notifications" description="Receive alerts for important updates" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Without Description
|
||||
export const WithoutDescription: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[400px] flex-col gap-4">
|
||||
<DescriptionSwitch label="Enable notifications" />
|
||||
<DescriptionSwitch label="Auto-save changes" defaultChecked />
|
||||
<DescriptionSwitch label="Dark mode" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// With Description
|
||||
export const WithDescription: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[400px] flex-col gap-4">
|
||||
<DescriptionSwitch label="Enable notifications" description="Receive alerts for important updates" />
|
||||
<DescriptionSwitch
|
||||
label="Auto-save changes"
|
||||
description="Automatically save your work as you type"
|
||||
defaultChecked
|
||||
/>
|
||||
<DescriptionSwitch label="Dark mode" description="Use dark theme for better visibility at night" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Positions
|
||||
export const Positions: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[500px] flex-col gap-8">
|
||||
<div>
|
||||
<h3 className="mb-4 text-sm font-semibold">Switch on Right (Default)</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Get notified about new messages and updates"
|
||||
position="right"
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Push notifications"
|
||||
description="Receive instant alerts on your device"
|
||||
position="right"
|
||||
defaultChecked
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Marketing emails"
|
||||
description="Stay informed about new features and offers"
|
||||
position="right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="mb-4 text-sm font-semibold">Switch on Left</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Get notified about new messages and updates"
|
||||
position="left"
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Push notifications"
|
||||
description="Receive instant alerts on your device"
|
||||
position="left"
|
||||
defaultChecked
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Marketing emails"
|
||||
description="Stay informed about new features and offers"
|
||||
position="left"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Sizes
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[400px] flex-col gap-6">
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Small (sm)</p>
|
||||
<DescriptionSwitch
|
||||
label="Small switch"
|
||||
description="Compact size for dense layouts and space-constrained interfaces"
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Medium (md) - Default</p>
|
||||
<DescriptionSwitch
|
||||
label="Medium switch"
|
||||
description="Default size that works well in most situations"
|
||||
size="md"
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Large (lg)</p>
|
||||
<DescriptionSwitch
|
||||
label="Large switch"
|
||||
description="Larger size for emphasis and improved touch targets"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// States
|
||||
export const States: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[400px] flex-col gap-4">
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Normal (Unchecked)</p>
|
||||
<DescriptionSwitch label="Normal state" description="Default interactive state, ready to be toggled" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Checked</p>
|
||||
<DescriptionSwitch label="Checked state" description="Currently enabled and active" defaultChecked />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Disabled (Unchecked)</p>
|
||||
<DescriptionSwitch label="Disabled state" description="Cannot be toggled, currently inactive" disabled />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Disabled (Checked)</p>
|
||||
<DescriptionSwitch
|
||||
label="Disabled state"
|
||||
description="Enabled but locked, cannot be changed"
|
||||
disabled
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Loading</p>
|
||||
<DescriptionSwitch
|
||||
label="Loading state"
|
||||
description="Processing your request, please wait"
|
||||
loading
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Controlled
|
||||
export const Controlled: Story = {
|
||||
render: function ControlledExample() {
|
||||
const [checked, setChecked] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="w-[400px]">
|
||||
<DescriptionSwitch
|
||||
label="Controlled switch"
|
||||
description="This switch is controlled by React state"
|
||||
checked={checked}
|
||||
onCheckedChange={setChecked}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-sm text-muted-foreground">Current state: {checked ? 'On' : 'Off'}</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setChecked(!checked)}
|
||||
className="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90">
|
||||
Toggle State
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Long Text
|
||||
export const LongText: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[500px] flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Enable comprehensive analytics and tracking"
|
||||
description="When enabled, this feature will collect and analyze detailed usage statistics, user behavior patterns, interaction data, and performance metrics to help improve the application experience and provide personalized recommendations."
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Short label"
|
||||
description="This is a very long description that explains in great detail what this particular setting does, why it might be useful, what the implications are of enabling or disabling it, and any other relevant information that users should know before making a decision."
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Notification Settings Example
|
||||
export const NotificationSettings: Story = {
|
||||
render: function NotificationSettingsExample() {
|
||||
const [notifications, setNotifications] = useState({
|
||||
email: true,
|
||||
push: false,
|
||||
sms: false,
|
||||
desktop: true,
|
||||
mobile: false,
|
||||
weekly: true
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-[500px] space-y-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Notification Preferences</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Receive updates and alerts via email"
|
||||
checked={notifications.email}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, email: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Push notifications"
|
||||
description="Get instant notifications on this device"
|
||||
checked={notifications.push}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, push: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="SMS notifications"
|
||||
description="Receive text message alerts for critical updates"
|
||||
checked={notifications.sms}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, sms: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Desktop notifications"
|
||||
description="Show notifications on your desktop"
|
||||
checked={notifications.desktop}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, desktop: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Mobile notifications"
|
||||
description="Receive alerts on your mobile device"
|
||||
checked={notifications.mobile}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, mobile: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Weekly digest"
|
||||
description="Get a summary of activity every week"
|
||||
checked={notifications.weekly}
|
||||
onCheckedChange={(checked) => setNotifications({ ...notifications, weekly: !!checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Privacy Settings Example
|
||||
export const PrivacySettings: Story = {
|
||||
render: function PrivacySettingsExample() {
|
||||
const [privacy, setPrivacy] = useState({
|
||||
profileVisible: true,
|
||||
activityTracking: false,
|
||||
dataSharing: false,
|
||||
personalization: true,
|
||||
thirdParty: false
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-[500px] space-y-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Privacy & Data</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Public profile"
|
||||
description="Make your profile visible to other users"
|
||||
checked={privacy.profileVisible}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, profileVisible: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Activity tracking"
|
||||
description="Allow us to track your activity to improve services"
|
||||
checked={privacy.activityTracking}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, activityTracking: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Data sharing"
|
||||
description="Share anonymous usage data with partners"
|
||||
checked={privacy.dataSharing}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, dataSharing: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Personalization"
|
||||
description="Use your data to personalize your experience"
|
||||
checked={privacy.personalization}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, personalization: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Third-party cookies"
|
||||
description="Allow third-party cookies for enhanced features"
|
||||
checked={privacy.thirdParty}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, thirdParty: !!checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Application Settings Example
|
||||
export const ApplicationSettings: Story = {
|
||||
render: function ApplicationSettingsExample() {
|
||||
const [settings, setSettings] = useState({
|
||||
autoSave: true,
|
||||
spellCheck: true,
|
||||
darkMode: false,
|
||||
compactMode: false,
|
||||
animations: true,
|
||||
sound: false,
|
||||
offlineMode: false
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-[500px] space-y-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Application Settings</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Auto-save"
|
||||
description="Automatically save your work every few minutes"
|
||||
checked={settings.autoSave}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, autoSave: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Spell check"
|
||||
description="Check spelling as you type"
|
||||
checked={settings.spellCheck}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, spellCheck: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Dark mode"
|
||||
description="Use dark theme throughout the application"
|
||||
checked={settings.darkMode}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, darkMode: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Compact mode"
|
||||
description="Reduce spacing for a more dense layout"
|
||||
checked={settings.compactMode}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, compactMode: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Animations"
|
||||
description="Enable smooth transitions and animations"
|
||||
checked={settings.animations}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, animations: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Sound effects"
|
||||
description="Play sounds for notifications and actions"
|
||||
checked={settings.sound}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, sound: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Offline mode"
|
||||
description="Enable working without internet connection"
|
||||
checked={settings.offlineMode}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, offlineMode: !!checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// With Icons
|
||||
export const WithIcons: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-[500px] flex-col gap-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Bell className="mt-1 size-5 text-muted-foreground" />
|
||||
<div className="flex-1">
|
||||
<DescriptionSwitch label="Notifications" description="Receive alerts for important updates" defaultChecked />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Moon className="mt-1 size-5 text-muted-foreground" />
|
||||
<div className="flex-1">
|
||||
<DescriptionSwitch label="Dark mode" description="Use dark theme for better visibility at night" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Shield className="mt-1 size-5 text-muted-foreground" />
|
||||
<div className="flex-1">
|
||||
<DescriptionSwitch
|
||||
label="Two-factor authentication"
|
||||
description="Add an extra layer of security to your account"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Wifi className="mt-1 size-5 text-muted-foreground" />
|
||||
<div className="flex-1">
|
||||
<DescriptionSwitch label="Offline mode" description="Work without internet connection" defaultChecked />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Zap className="mt-1 size-5 text-muted-foreground" />
|
||||
<div className="flex-1">
|
||||
<DescriptionSwitch
|
||||
label="Performance mode"
|
||||
description="Optimize for speed and responsiveness"
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Loading Simulation
|
||||
export const LoadingSimulation: Story = {
|
||||
render: function LoadingSimulationExample() {
|
||||
const [states, setStates] = useState({
|
||||
wifi: { enabled: false, loading: false },
|
||||
bluetooth: { enabled: false, loading: false },
|
||||
location: { enabled: false, loading: false }
|
||||
})
|
||||
|
||||
const handleToggle = async (setting: keyof typeof states, checked: boolean) => {
|
||||
setStates((prev) => ({
|
||||
...prev,
|
||||
[setting]: { ...prev[setting], loading: true }
|
||||
}))
|
||||
|
||||
// Simulate API call
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500))
|
||||
|
||||
setStates((prev) => ({
|
||||
...prev,
|
||||
[setting]: { enabled: checked, loading: false }
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-[500px] space-y-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">System Settings</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Wi-Fi"
|
||||
description="Connect to wireless networks"
|
||||
checked={states.wifi.enabled}
|
||||
onCheckedChange={(checked) => handleToggle('wifi', !!checked)}
|
||||
loading={states.wifi.loading}
|
||||
disabled={states.wifi.loading}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Bluetooth"
|
||||
description="Connect to Bluetooth devices"
|
||||
checked={states.bluetooth.enabled}
|
||||
onCheckedChange={(checked) => handleToggle('bluetooth', !!checked)}
|
||||
loading={states.bluetooth.loading}
|
||||
disabled={states.bluetooth.loading}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Location services"
|
||||
description="Allow apps to use your location"
|
||||
checked={states.location.enabled}
|
||||
onCheckedChange={(checked) => handleToggle('location', !!checked)}
|
||||
loading={states.location.loading}
|
||||
disabled={states.location.loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Toggle switches to see a simulated 1.5-second loading state</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Complex Settings Panel
|
||||
export const ComplexSettingsPanel: Story = {
|
||||
render: function ComplexSettingsPanelExample() {
|
||||
const [settings, setSettings] = useState({
|
||||
notifications: {
|
||||
email: true,
|
||||
push: false,
|
||||
desktop: true
|
||||
},
|
||||
privacy: {
|
||||
profile: true,
|
||||
activity: false,
|
||||
analytics: true
|
||||
},
|
||||
features: {
|
||||
autoSave: true,
|
||||
darkMode: false,
|
||||
compactView: false
|
||||
},
|
||||
security: {
|
||||
twoFactor: false,
|
||||
biometric: true,
|
||||
sessionTimeout: false
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="w-[600px] space-y-8">
|
||||
{/* Notifications Section */}
|
||||
<div>
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<Bell className="size-5" />
|
||||
<h3 className="text-base font-semibold">Notifications</h3>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Receive updates and alerts via email"
|
||||
checked={settings.notifications.email}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
notifications: { ...settings.notifications, email: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Push notifications"
|
||||
description="Get instant notifications on this device"
|
||||
checked={settings.notifications.push}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
notifications: { ...settings.notifications, push: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Desktop notifications"
|
||||
description="Show notifications on your desktop"
|
||||
checked={settings.notifications.desktop}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
notifications: { ...settings.notifications, desktop: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Privacy Section */}
|
||||
<div>
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<Eye className="size-5" />
|
||||
<h3 className="text-base font-semibold">Privacy</h3>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Public profile"
|
||||
description="Make your profile visible to other users"
|
||||
checked={settings.privacy.profile}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
privacy: { ...settings.privacy, profile: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Activity tracking"
|
||||
description="Allow us to track your activity"
|
||||
checked={settings.privacy.activity}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
privacy: { ...settings.privacy, activity: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Analytics"
|
||||
description="Help improve the app by sharing usage data"
|
||||
checked={settings.privacy.analytics}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
privacy: { ...settings.privacy, analytics: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features Section */}
|
||||
<div>
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<Zap className="size-5" />
|
||||
<h3 className="text-base font-semibold">Features</h3>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Auto-save"
|
||||
description="Automatically save your work"
|
||||
checked={settings.features.autoSave}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
features: { ...settings.features, autoSave: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Dark mode"
|
||||
description="Use dark theme throughout the app"
|
||||
checked={settings.features.darkMode}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
features: { ...settings.features, darkMode: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Compact view"
|
||||
description="Reduce spacing for more content"
|
||||
checked={settings.features.compactView}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
features: { ...settings.features, compactView: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Security Section */}
|
||||
<div>
|
||||
<div className="mb-4 flex items-center gap-2">
|
||||
<Lock className="size-5" />
|
||||
<h3 className="text-base font-semibold">Security</h3>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Two-factor authentication"
|
||||
description="Require a second verification step when signing in"
|
||||
checked={settings.security.twoFactor}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
security: { ...settings.security, twoFactor: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Biometric authentication"
|
||||
description="Use fingerprint or face recognition"
|
||||
checked={settings.security.biometric}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
security: { ...settings.security, biometric: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Auto session timeout"
|
||||
description="Automatically sign out after inactivity"
|
||||
checked={settings.security.sessionTimeout}
|
||||
onCheckedChange={(checked) =>
|
||||
setSettings({
|
||||
...settings,
|
||||
security: { ...settings.security, sessionTimeout: !!checked }
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Accessibility Features
|
||||
export const AccessibilityFeatures: Story = {
|
||||
render: () => (
|
||||
<div className="w-[500px] space-y-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Keyboard Navigation</h3>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Use Tab to navigate between switches and Space/Enter to toggle them. Each switch has a proper label for screen
|
||||
readers.
|
||||
</p>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch label="High contrast mode" description="Increase contrast for better visibility" />
|
||||
<DescriptionSwitch label="Reduce motion" description="Minimize animations and transitions" />
|
||||
<DescriptionSwitch
|
||||
label="Screen reader optimization"
|
||||
description="Optimize interface for screen readers"
|
||||
defaultChecked
|
||||
/>
|
||||
<DescriptionSwitch label="Large text" description="Increase font size throughout the app" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Responsive Layout
|
||||
export const ResponsiveLayout: Story = {
|
||||
render: () => (
|
||||
<div className="space-y-6">
|
||||
<div className="w-[300px]">
|
||||
<h3 className="mb-4 text-sm font-semibold">Narrow Layout (300px)</h3>
|
||||
<div className="flex flex-col gap-3">
|
||||
<DescriptionSwitch label="Notifications" description="Receive important alerts" size="sm" />
|
||||
<DescriptionSwitch label="Auto-save" description="Save automatically" size="sm" defaultChecked />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-[500px]">
|
||||
<h3 className="mb-4 text-sm font-semibold">Standard Layout (500px)</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch label="Notifications" description="Receive alerts for important updates and messages" />
|
||||
<DescriptionSwitch label="Auto-save" description="Automatically save your work as you type" defaultChecked />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-[700px]">
|
||||
<h3 className="mb-4 text-sm font-semibold">Wide Layout (700px)</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Notifications"
|
||||
description="Receive alerts for important updates, messages, and system notifications to stay informed"
|
||||
size="lg"
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Auto-save"
|
||||
description="Automatically save your work as you type to prevent data loss and ensure your progress is always preserved"
|
||||
size="lg"
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
666
packages/ui/stories/components/primitives/Switch.stories.tsx
Normal file
666
packages/ui/stories/components/primitives/Switch.stories.tsx
Normal file
@ -0,0 +1,666 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { Bell, Moon, Shield, Wifi, Zap } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { DescriptionSwitch, Switch } from '../../../src/components/primitives/switch'
|
||||
|
||||
const meta: Meta<typeof Switch> = {
|
||||
title: 'Components/Primitives/Switch',
|
||||
component: Switch,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'A switch component based on Radix UI Switch, allowing users to toggle between on/off states. Supports three sizes (sm, md, lg), loading states, and an enhanced DescriptionSwitch variant with label and description. Built with accessibility in mind.'
|
||||
}
|
||||
}
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
disabled: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Whether the switch is disabled'
|
||||
},
|
||||
loading: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'When true, displays a loading animation in the switch thumb'
|
||||
},
|
||||
size: {
|
||||
control: { type: 'select' },
|
||||
options: ['sm', 'md', 'lg'],
|
||||
description: 'The size of the switch'
|
||||
},
|
||||
defaultChecked: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Default checked state'
|
||||
},
|
||||
checked: {
|
||||
control: { type: 'boolean' },
|
||||
description: 'Checked state in controlled mode'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// Default
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="default1" />
|
||||
<label htmlFor="default1" className="cursor-pointer text-sm">
|
||||
Enable notifications
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="default2" />
|
||||
<label htmlFor="default2" className="cursor-pointer text-sm">
|
||||
Auto-save changes
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="default3" />
|
||||
<label htmlFor="default3" className="cursor-pointer text-sm">
|
||||
Dark mode
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// With Default Checked
|
||||
export const WithDefaultChecked: Story = {
|
||||
render: () => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="checked1" defaultChecked />
|
||||
<label htmlFor="checked1" className="cursor-pointer text-sm">
|
||||
Option 1 (Default On)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="checked2" />
|
||||
<label htmlFor="checked2" className="cursor-pointer text-sm">
|
||||
Option 2
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="checked3" defaultChecked />
|
||||
<label htmlFor="checked3" className="cursor-pointer text-sm">
|
||||
Option 3 (Default On)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Disabled
|
||||
export const Disabled: Story = {
|
||||
render: () => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="disabled1" disabled />
|
||||
<label htmlFor="disabled1" className="cursor-not-allowed text-sm opacity-50">
|
||||
Disabled (Off)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="disabled2" disabled defaultChecked />
|
||||
<label htmlFor="disabled2" className="cursor-not-allowed text-sm opacity-50">
|
||||
Disabled (On)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Loading State
|
||||
export const Loading: Story = {
|
||||
render: () => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="loading1" loading />
|
||||
<label htmlFor="loading1" className="cursor-pointer text-sm">
|
||||
Loading state (Off)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="loading2" loading defaultChecked />
|
||||
<label htmlFor="loading2" className="cursor-pointer text-sm">
|
||||
Loading state (On)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="loading3" loading disabled defaultChecked />
|
||||
<label htmlFor="loading3" className="cursor-not-allowed text-sm opacity-50">
|
||||
Loading + Disabled
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Controlled
|
||||
export const Controlled: Story = {
|
||||
render: function ControlledExample() {
|
||||
const [checked, setChecked] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="controlled" checked={checked} onCheckedChange={setChecked} />
|
||||
<label htmlFor="controlled" className="cursor-pointer text-sm">
|
||||
Controlled switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">Current state: {checked ? 'On' : 'Off'}</div>
|
||||
<button
|
||||
onClick={() => setChecked(!checked)}
|
||||
className="w-fit rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90">
|
||||
Toggle State
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Sizes
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Small (sm)</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-sm-1" size="sm" />
|
||||
<label htmlFor="size-sm-1" className="cursor-pointer text-sm">
|
||||
Small switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-sm-2" size="sm" defaultChecked />
|
||||
<label htmlFor="size-sm-2" className="cursor-pointer text-sm">
|
||||
Small switch (on)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-sm-3" size="sm" loading defaultChecked />
|
||||
<label htmlFor="size-sm-3" className="cursor-pointer text-sm">
|
||||
Small switch (loading)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Medium (md) - Default</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-md-1" size="md" />
|
||||
<label htmlFor="size-md-1" className="cursor-pointer text-sm">
|
||||
Medium switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-md-2" size="md" defaultChecked />
|
||||
<label htmlFor="size-md-2" className="cursor-pointer text-sm">
|
||||
Medium switch (on)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-md-3" size="md" loading defaultChecked />
|
||||
<label htmlFor="size-md-3" className="cursor-pointer text-sm">
|
||||
Medium switch (loading)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Large (lg)</p>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-lg-1" size="lg" />
|
||||
<label htmlFor="size-lg-1" className="cursor-pointer text-sm">
|
||||
Large switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-lg-2" size="lg" defaultChecked />
|
||||
<label htmlFor="size-lg-2" className="cursor-pointer text-sm">
|
||||
Large switch (on)
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="size-lg-3" size="lg" loading defaultChecked />
|
||||
<label htmlFor="size-lg-3" className="cursor-pointer text-sm">
|
||||
Large switch (loading)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Description Switch - Basic
|
||||
export const DescriptionSwitchBasic: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-96 flex-col gap-4">
|
||||
<DescriptionSwitch label="Enable notifications" description="Receive alerts for important updates" />
|
||||
<DescriptionSwitch label="Auto-save" description="Automatically save changes as you work" defaultChecked />
|
||||
<DescriptionSwitch label="Dark mode" description="Use dark theme for better visibility at night" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Description Switch - Positions
|
||||
export const DescriptionSwitchPositions: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-96 flex-col gap-6">
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Switch on Right (Default)</p>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Get notified about new messages"
|
||||
position="right"
|
||||
/>
|
||||
<DescriptionSwitch label="Marketing emails" description="Receive promotional content" position="right" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="mb-3 text-sm text-muted-foreground">Switch on Left</p>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Get notified about new messages"
|
||||
position="left"
|
||||
/>
|
||||
<DescriptionSwitch label="Marketing emails" description="Receive promotional content" position="left" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Description Switch - Sizes
|
||||
export const DescriptionSwitchSizes: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-96 flex-col gap-6">
|
||||
<DescriptionSwitch label="Small switch" description="Compact size for dense layouts" size="sm" />
|
||||
<DescriptionSwitch label="Medium switch" description="Default size for most use cases" size="md" defaultChecked />
|
||||
<DescriptionSwitch label="Large switch" description="Larger size for emphasis" size="lg" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Description Switch - States
|
||||
export const DescriptionSwitchStates: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-96 flex-col gap-4">
|
||||
<DescriptionSwitch label="Normal state" description="Default interactive state" />
|
||||
<DescriptionSwitch label="Checked state" description="Currently enabled" defaultChecked />
|
||||
<DescriptionSwitch label="Disabled state" description="Cannot be toggled" disabled />
|
||||
<DescriptionSwitch label="Disabled + Checked" description="Enabled but locked" disabled defaultChecked />
|
||||
<DescriptionSwitch label="Loading state" description="Processing your request" loading defaultChecked />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Size Comparison
|
||||
export const SizeComparison: Story = {
|
||||
render: () => (
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-xs font-medium text-muted-foreground">Off</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-sm-1" size="sm" />
|
||||
<span className="text-xs text-muted-foreground">sm</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-md-1" size="md" />
|
||||
<span className="text-xs text-muted-foreground">md</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-lg-1" size="lg" />
|
||||
<span className="text-xs text-muted-foreground">lg</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-xs font-medium text-muted-foreground">On</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-sm-2" size="sm" defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">sm</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-md-2" size="md" defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">md</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-lg-2" size="lg" defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">lg</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-xs font-medium text-muted-foreground">Loading</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-sm-3" size="sm" loading defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">sm</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-md-3" size="md" loading defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">md</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Switch id="compare-lg-3" size="lg" loading defaultChecked />
|
||||
<span className="text-xs text-muted-foreground">lg</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Real World Examples
|
||||
export const RealWorldExamples: Story = {
|
||||
render: function RealWorldExample() {
|
||||
const [settings, setSettings] = useState({
|
||||
notifications: true,
|
||||
autoSave: true,
|
||||
darkMode: false,
|
||||
analytics: true
|
||||
})
|
||||
|
||||
const [privacy, setPrivacy] = useState({
|
||||
shareData: false,
|
||||
allowCookies: true,
|
||||
trackLocation: false,
|
||||
personalizedAds: false
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex w-[500px] flex-col gap-8">
|
||||
{/* General Settings */}
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">General Settings</h3>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="settings-notifications"
|
||||
checked={settings.notifications}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, notifications: !!checked })}
|
||||
/>
|
||||
<label htmlFor="settings-notifications" className="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<Bell className="size-4" />
|
||||
Push Notifications
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="settings-autosave"
|
||||
checked={settings.autoSave}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, autoSave: !!checked })}
|
||||
/>
|
||||
<label htmlFor="settings-autosave" className="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<Zap className="size-4" />
|
||||
Auto-save Changes
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="settings-darkmode"
|
||||
checked={settings.darkMode}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, darkMode: !!checked })}
|
||||
/>
|
||||
<label htmlFor="settings-darkmode" className="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<Moon className="size-4" />
|
||||
Dark Mode
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
id="settings-analytics"
|
||||
checked={settings.analytics}
|
||||
onCheckedChange={(checked) => setSettings({ ...settings, analytics: !!checked })}
|
||||
/>
|
||||
<label htmlFor="settings-analytics" className="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<Shield className="size-4" />
|
||||
Usage Analytics
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Privacy Settings with DescriptionSwitch */}
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Privacy Settings</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Share usage data"
|
||||
description="Help us improve by sharing anonymous usage statistics"
|
||||
checked={privacy.shareData}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, shareData: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Allow cookies"
|
||||
description="Enable cookies for better user experience"
|
||||
checked={privacy.allowCookies}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, allowCookies: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Track location"
|
||||
description="Use your location for personalized content"
|
||||
checked={privacy.trackLocation}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, trackLocation: !!checked })}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Personalized ads"
|
||||
description="Show ads based on your interests"
|
||||
checked={privacy.personalizedAds}
|
||||
onCheckedChange={(checked) => setPrivacy({ ...privacy, personalizedAds: !!checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Interactive Loading Example
|
||||
export const InteractiveLoading: Story = {
|
||||
render: function InteractiveLoadingExample() {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isEnabled, setIsEnabled] = useState(false)
|
||||
|
||||
const handleToggle = async (checked: boolean) => {
|
||||
setIsLoading(true)
|
||||
// Simulate API call
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
setIsEnabled(checked)
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex w-96 flex-col gap-4">
|
||||
<DescriptionSwitch
|
||||
label="Wi-Fi Connection"
|
||||
description="Connect to wireless networks"
|
||||
checked={isEnabled}
|
||||
onCheckedChange={handleToggle}
|
||||
loading={isLoading}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<div className="rounded-md bg-muted p-4">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Wifi className="size-4" />
|
||||
<span className="font-medium">Status:</span>
|
||||
<span className="text-muted-foreground">
|
||||
{isLoading ? 'Connecting...' : isEnabled ? 'Connected' : 'Disconnected'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">Click the switch to see a simulated 2-second loading state</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Form Example
|
||||
export const FormExample: Story = {
|
||||
render: function FormExample() {
|
||||
const [formData, setFormData] = useState({
|
||||
emailNotifications: true,
|
||||
pushNotifications: false,
|
||||
smsNotifications: false,
|
||||
newsletter: true,
|
||||
twoFactorAuth: false,
|
||||
biometricAuth: true
|
||||
})
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const [saved, setSaved] = useState(false)
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setIsSaving(true)
|
||||
setSaved(false)
|
||||
// Simulate API call
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500))
|
||||
setIsSaving(false)
|
||||
setSaved(true)
|
||||
setTimeout(() => setSaved(false), 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="w-[500px] space-y-6">
|
||||
<h3 className="text-base font-semibold">Account Preferences</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="mb-3 text-sm font-medium">Notifications</h4>
|
||||
<div className="space-y-3">
|
||||
<DescriptionSwitch
|
||||
label="Email notifications"
|
||||
description="Receive updates via email"
|
||||
checked={formData.emailNotifications}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, emailNotifications: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Push notifications"
|
||||
description="Get instant alerts on your device"
|
||||
checked={formData.pushNotifications}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, pushNotifications: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="SMS notifications"
|
||||
description="Receive text message alerts"
|
||||
checked={formData.smsNotifications}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, smsNotifications: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Newsletter subscription"
|
||||
description="Stay updated with our latest news"
|
||||
checked={formData.newsletter}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, newsletter: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 className="mb-3 text-sm font-medium">Security</h4>
|
||||
<div className="space-y-3">
|
||||
<DescriptionSwitch
|
||||
label="Two-factor authentication"
|
||||
description="Add an extra layer of security"
|
||||
checked={formData.twoFactorAuth}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, twoFactorAuth: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
<DescriptionSwitch
|
||||
label="Biometric authentication"
|
||||
description="Use fingerprint or face recognition"
|
||||
checked={formData.biometricAuth}
|
||||
onCheckedChange={(checked) => setFormData({ ...formData, biometricAuth: !!checked })}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSaving}
|
||||
className="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50">
|
||||
{isSaving ? 'Saving...' : 'Save Changes'}
|
||||
</button>
|
||||
{saved && <p className="text-sm text-green-600">Settings saved successfully!</p>}
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Accessibility Example
|
||||
export const Accessibility: Story = {
|
||||
render: () => (
|
||||
<div className="flex w-96 flex-col gap-6">
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">Keyboard Navigation</h3>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Use Tab to navigate between switches and Space/Enter to toggle them.
|
||||
</p>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="a11y-1" />
|
||||
<label htmlFor="a11y-1" className="cursor-pointer text-sm">
|
||||
First switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="a11y-2" />
|
||||
<label htmlFor="a11y-2" className="cursor-pointer text-sm">
|
||||
Second switch
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch id="a11y-3" />
|
||||
<label htmlFor="a11y-3" className="cursor-pointer text-sm">
|
||||
Third switch
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="mb-4 text-base font-semibold">ARIA Labels</h3>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Switches include proper ARIA attributes for screen reader support.
|
||||
</p>
|
||||
<DescriptionSwitch
|
||||
label="Accessibility features"
|
||||
description="Enable enhanced accessibility options for better usability"
|
||||
defaultChecked
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -415,7 +415,7 @@ const PopupContainer: React.FC<PopupContainerProps> = ({
|
||||
</Form.Item>
|
||||
{!rawContent && (
|
||||
<Form.Item label={i18n.t('chat.topics.export.obsidian_reasoning')}>
|
||||
<Switch isSelected={exportReasoning} onValueChange={setExportReasoning} />
|
||||
<Switch checked={exportReasoning} onCheckedChange={setExportReasoning} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
@ -115,6 +115,7 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
const { theme } = useTheme()
|
||||
const { themeNames } = useCodeStyle()
|
||||
|
||||
// FIXME: We should use useMemo to calculate these states instead of using useEffect to sync
|
||||
const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
|
||||
const [enableTemperature, setEnableTemperature] = useState(assistant?.settings?.enableTemperature ?? true)
|
||||
const [contextCount, setContextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
|
||||
@ -270,10 +271,9 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<HelpTooltip title={t('chat.settings.temperature.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
<Switch
|
||||
size="sm"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
isSelected={enableTemperature}
|
||||
onValueChange={(enabled) => {
|
||||
checked={enableTemperature}
|
||||
onCheckedChange={(enabled) => {
|
||||
setEnableTemperature(enabled)
|
||||
onUpdateAssistantSettings({ enableTemperature: enabled })
|
||||
}}
|
||||
@ -340,9 +340,8 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<SettingRow>
|
||||
<SettingRowTitleSmall>{t('models.stream_output')}</SettingRowTitleSmall>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={streamOutput}
|
||||
onValueChange={(checked) => {
|
||||
checked={streamOutput}
|
||||
onCheckedChange={(checked) => {
|
||||
setStreamOutput(checked)
|
||||
onUpdateAssistantSettings({ streamOutput: checked })
|
||||
}}
|
||||
@ -357,9 +356,8 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</SettingRowTitleSmall>
|
||||
</Row>
|
||||
<Switch
|
||||
size="sm"
|
||||
isSelected={enableMaxTokens}
|
||||
onValueChange={async (enabled) => {
|
||||
checked={enableMaxTokens}
|
||||
onCheckedChange={async (enabled) => {
|
||||
if (enabled) {
|
||||
const confirmed = await modalConfirm({
|
||||
title: t('chat.settings.max_tokens.confirm'),
|
||||
@ -410,38 +408,36 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<CollapsibleSettingGroup title={t('settings.messages.title')} defaultExpanded={true}>
|
||||
<SettingGroup>
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={showPrompt} onValueChange={setShowPrompt}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.prompt')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>{t('settings.messages.use_serif_font')}</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={messageFont === 'serif'}
|
||||
onValueChange={(checked) => setMessageFont(checked ? 'serif' : 'system')}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.use_serif_font')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={showPrompt}
|
||||
onCheckedChange={setShowPrompt}
|
||||
label={t('settings.messages.prompt')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>
|
||||
{t('chat.settings.thought_auto_collapse.label')}
|
||||
<HelpTooltip title={t('chat.settings.thought_auto_collapse.tip')} />
|
||||
</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch isSelected={thoughtAutoCollapse} onValueChange={setThoughtAutoCollapse}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('chat.settings.thought_auto_collapse.label')}
|
||||
<HelpTooltip content={t('chat.settings.thought_auto_collapse.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={messageFont === 'serif'}
|
||||
onCheckedChange={(checked) => setMessageFont(checked ? 'serif' : 'system')}
|
||||
label={t('settings.messages.use_serif_font')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch isSelected={showMessageOutline} onValueChange={setShowMessageOutline}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.show_message_outline')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={thoughtAutoCollapse}
|
||||
onCheckedChange={setThoughtAutoCollapse}
|
||||
label={t('chat.settings.thought_auto_collapse.label')}
|
||||
description={t('chat.settings.thought_auto_collapse.tip')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
checked={showMessageOutline}
|
||||
onCheckedChange={setShowMessageOutline}
|
||||
label={t('settings.messages.show_message_outline')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
@ -534,16 +530,12 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>
|
||||
{t('settings.math.single_dollar.label')}
|
||||
<HelpTooltip title={t('settings.math.single_dollar.tip')} />
|
||||
</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch size="sm" isSelected={mathEnableSingleDollar} onValueChange={setMathEnableSingleDollar}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('settings.math.single_dollar.label')}
|
||||
<HelpTooltip content={t('settings.math.single_dollar.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={mathEnableSingleDollar}
|
||||
onCheckedChange={setMathEnableSingleDollar}
|
||||
label={t('settings.math.single_dollar.label')}
|
||||
description={t('settings.math.single_dollar.tip')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
</SettingGroup>
|
||||
@ -567,32 +559,21 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>
|
||||
{t('chat.settings.code_fancy_block.label')}
|
||||
<HelpTooltip title={t('chat.settings.code_fancy_block.tip')} />
|
||||
</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch size="sm" isSelected={codeFancyBlock} onValueChange={setCodeFancyBlock}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('chat.settings.code_fancy_block.label')}
|
||||
<HelpTooltip content={t('chat.settings.code_fancy_block.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={codeFancyBlock}
|
||||
onCheckedChange={setCodeFancyBlock}
|
||||
label={t('chat.settings.code_fancy_block.label')}
|
||||
description={t('chat.settings.code_fancy_block.tip')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>
|
||||
{t('chat.settings.code_execution.title')}
|
||||
<HelpTooltip title={t('chat.settings.code_execution.tip')} />
|
||||
</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeExecution.enabled}
|
||||
onValueChange={(checked) => setCodeExecution({ enabled: checked })}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('chat.settings.code_execution.title')}
|
||||
<HelpTooltip content={t('chat.settings.code_execution.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeExecution.enabled}
|
||||
onCheckedChange={(checked) => setCodeExecution({ enabled: checked })}
|
||||
label={t('chat.settings.code_execution.title')}
|
||||
description={t('chat.settings.code_execution.tip')}
|
||||
/>
|
||||
</SettingRow>
|
||||
{codeExecution.enabled && (
|
||||
<>
|
||||
@ -616,90 +597,80 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
)}
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
{/* <SettingRowTitleSmall>{t('chat.settings.code_editor.title')}</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeEditor.enabled}
|
||||
onValueChange={(checked) => setCodeEditor({ enabled: checked })}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_editor.title')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeEditor.enabled}
|
||||
onCheckedChange={(checked) => setCodeEditor({ enabled: checked })}
|
||||
label={t('chat.settings.code_editor.title')}
|
||||
/>
|
||||
</SettingRow>
|
||||
{codeEditor.enabled && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
<SettingRow style={{ paddingLeft: 8 }}>
|
||||
{/* <SettingRowTitleSmall>
|
||||
{t('chat.settings.code_editor.highlight_active_line')}
|
||||
<HelpTooltip title={t('chat.settings.code_editor.highlight_active_line.tip')} />
|
||||
</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeEditor.highlightActiveLine}
|
||||
onValueChange={(checked) => setCodeEditor({ highlightActiveLine: checked })}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('chat.settings.code_editor.highlight_active_line')}
|
||||
<HelpTooltip content={t('chat.settings.code_editor.highlight_active_line.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeEditor.highlightActiveLine}
|
||||
onCheckedChange={(checked) => setCodeEditor({ highlightActiveLine: checked })}
|
||||
label={t('chat.settings.code_editor.highlight_active_line')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow style={{ paddingLeft: 8 }}>
|
||||
{/* <SettingRowTitleSmall>{t('chat.settings.code_editor.fold_gutter')}</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeEditor.foldGutter}
|
||||
onValueChange={(checked) => setCodeEditor({ foldGutter: checked })}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_editor.fold_gutter')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeEditor.foldGutter}
|
||||
onCheckedChange={(checked) => setCodeEditor({ foldGutter: checked })}
|
||||
label={t('chat.settings.code_editor.fold_gutter')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow style={{ paddingLeft: 8 }}>
|
||||
{/* <SettingRowTitleSmall>{t('chat.settings.code_editor.autocompletion')}</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeEditor.autocompletion}
|
||||
onValueChange={(checked) => setCodeEditor({ autocompletion: checked })}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_editor.autocompletion')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeEditor.autocompletion}
|
||||
onCheckedChange={(checked) => setCodeEditor({ autocompletion: checked })}
|
||||
label={t('chat.settings.code_editor.autocompletion')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow style={{ paddingLeft: 8 }}>
|
||||
{/* <SettingRowTitleSmall>{t('chat.settings.code_editor.keymap')}</SettingRowTitleSmall> */}
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={codeEditor.keymap}
|
||||
onValueChange={(checked) => setCodeEditor({ keymap: checked })}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_editor.keymap')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={codeEditor.keymap}
|
||||
onCheckedChange={(checked) => setCodeEditor({ keymap: checked })}
|
||||
label={t('chat.settings.code_editor.keymap')}
|
||||
/>
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={codeShowLineNumbers} onValueChange={setCodeShowLineNumbers}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.show_line_numbers')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={codeShowLineNumbers}
|
||||
onCheckedChange={setCodeShowLineNumbers}
|
||||
label={t('chat.settings.show_line_numbers')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={codeCollapsible} onValueChange={setCodeCollapsible}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_collapsible')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={codeCollapsible}
|
||||
onCheckedChange={setCodeCollapsible}
|
||||
label={t('chat.settings.code_collapsible')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={codeWrappable} onValueChange={setCodeWrappable}>
|
||||
<SettingRowTitleSmall>{t('chat.settings.code_wrappable')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={codeWrappable}
|
||||
onCheckedChange={setCodeWrappable}
|
||||
label={t('chat.settings.code_wrappable')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={codeImageTools} onValueChange={setCodeImageTools}>
|
||||
<SettingRowTitleSmall>
|
||||
{t('chat.settings.code_image_tools.label')}
|
||||
<HelpTooltip content={t('chat.settings.code_image_tools.tip')} />
|
||||
</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={codeImageTools}
|
||||
onCheckedChange={setCodeImageTools}
|
||||
label={t('chat.settings.code_image_tools.label')}
|
||||
description={t('chat.settings.code_image_tools.tip')}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingDivider />
|
||||
@ -708,17 +679,18 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<SettingGroup>
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={showInputEstimatedTokens}
|
||||
onValueChange={setShowInputEstimatedTokens}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.input.show_estimated_tokens')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={showInputEstimatedTokens}
|
||||
onCheckedChange={setShowInputEstimatedTokens}
|
||||
label={t('settings.messages.input.show_estimated_tokens')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={pasteLongTextAsFile} onValueChange={setPasteLongTextAsFile}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.input.paste_long_text_as_file')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={pasteLongTextAsFile}
|
||||
onCheckedChange={setPasteLongTextAsFile}
|
||||
label={t('settings.messages.input.paste_long_text_as_file')}
|
||||
/>
|
||||
</SettingRow>
|
||||
{pasteLongTextAsFile && (
|
||||
<>
|
||||
@ -740,54 +712,54 @@ const SettingsTab: FC<Props> = (props) => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={renderInputMessageAsMarkdown}
|
||||
onValueChange={setRenderInputMessageAsMarkdown}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.markdown_rendering_input_message')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={renderInputMessageAsMarkdown}
|
||||
onCheckedChange={setRenderInputMessageAsMarkdown}
|
||||
label={t('settings.messages.markdown_rendering_input_message')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
{!(language || navigator.language).startsWith('en') && (
|
||||
<>
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={autoTranslateWithSpace}
|
||||
onValueChange={setAutoTranslateWithSpace}>
|
||||
<SettingRowTitleSmall>{t('settings.input.auto_translate_with_space')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={autoTranslateWithSpace}
|
||||
onCheckedChange={setAutoTranslateWithSpace}
|
||||
label={t('settings.input.auto_translate_with_space')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
</>
|
||||
)}
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={showTranslateConfirm} onValueChange={setShowTranslateConfirm}>
|
||||
<SettingRowTitleSmall>{t('settings.input.show_translate_confirm')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
<DescriptionSwitch
|
||||
checked={showTranslateConfirm}
|
||||
onCheckedChange={setShowTranslateConfirm}
|
||||
label={t('settings.input.show_translate_confirm')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={enableQuickPanelTriggers}
|
||||
onValueChange={setEnableQuickPanelTriggers}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.input.enable_quick_triggers')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch size="sm" isSelected={confirmDeleteMessage} onValueChange={setConfirmDeleteMessage}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.input.confirm_delete_message')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={enableQuickPanelTriggers}
|
||||
onCheckedChange={setEnableQuickPanelTriggers}
|
||||
label={t('settings.messages.input.enable_quick_triggers')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
size="sm"
|
||||
isSelected={confirmRegenerateMessage}
|
||||
onValueChange={setConfirmRegenerateMessage}>
|
||||
<SettingRowTitleSmall>{t('settings.messages.input.confirm_regenerate_message')}</SettingRowTitleSmall>
|
||||
</DescriptionSwitch>
|
||||
checked={confirmDeleteMessage}
|
||||
onCheckedChange={setConfirmDeleteMessage}
|
||||
label={t('settings.messages.input.confirm_delete_message')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<DescriptionSwitch
|
||||
checked={confirmRegenerateMessage}
|
||||
onCheckedChange={setConfirmRegenerateMessage}
|
||||
label={t('settings.messages.input.confirm_regenerate_message')}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
|
||||
@ -96,7 +96,7 @@ const MiniAppSettings: FC = () => {
|
||||
<SettingLabelGroup>
|
||||
<SettingRowTitle>{t('settings.miniapps.open_link_external.title')}</SettingRowTitle>
|
||||
</SettingLabelGroup>
|
||||
<Switch isSelected={minappsOpenLinkExternal} onValueChange={(checked) => setMinappsOpenLinkExternal(checked)} />
|
||||
<Switch checked={minappsOpenLinkExternal} onCheckedChange={(checked) => setMinappsOpenLinkExternal(checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
{/* 缓存小程序数量设置 */}
|
||||
@ -134,8 +134,8 @@ const MiniAppSettings: FC = () => {
|
||||
<SettingDescription>{t('settings.miniapps.sidebar_description')}</SettingDescription>
|
||||
</SettingLabelGroup>
|
||||
<Switch
|
||||
isSelected={showOpenedMinappsInSidebar}
|
||||
onValueChange={(checked) => setShowOpenedMinappsInSidebar(checked)}
|
||||
checked={showOpenedMinappsInSidebar}
|
||||
onCheckedChange={(checked) => setShowOpenedMinappsInSidebar(checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
</Container>
|
||||
|
||||
@ -800,8 +800,8 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
return (
|
||||
<RowFlex>
|
||||
<Switch
|
||||
isSelected={(painting[item.key!] || item.initialValue) as boolean}
|
||||
onValueChange={(checked) => updatePaintingState({ [item.key!]: checked })}
|
||||
checked={(painting[item.key!] || item.initialValue) as boolean}
|
||||
onCheckedChange={(checked) => updatePaintingState({ [item.key!]: checked })}
|
||||
/>
|
||||
</RowFlex>
|
||||
)
|
||||
|
||||
@ -938,7 +938,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
<InfoTooltip content={t('paintings.auto_create_paint_tip')} />
|
||||
</SettingTitle>
|
||||
<RowFlex>
|
||||
<Switch isSelected={painting.autoCreate} onValueChange={(checked) => onChangeAutoCreate(checked)} />
|
||||
<Switch checked={painting.autoCreate} onCheckedChange={(checked) => onChangeAutoCreate(checked)} />
|
||||
</RowFlex>
|
||||
</LeftContainer>
|
||||
<MainContainer>
|
||||
|
||||
@ -464,8 +464,8 @@ const SiliconPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
</SettingTitle>
|
||||
<RowFlex>
|
||||
<Switch
|
||||
isSelected={painting.promptEnhancement}
|
||||
onValueChange={(checked) => updatePaintingState({ promptEnhancement: checked })}
|
||||
checked={painting.promptEnhancement}
|
||||
onCheckedChange={(checked) => updatePaintingState({ promptEnhancement: checked })}
|
||||
/>
|
||||
</RowFlex>
|
||||
</LeftContainer>
|
||||
|
||||
@ -198,8 +198,8 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
if (type === 'boolean') {
|
||||
return (
|
||||
<Switch
|
||||
isSelected={value !== undefined ? value : defaultValue}
|
||||
onValueChange={(checked) => onChange(propertyName, checked)}
|
||||
checked={value !== undefined ? value : defaultValue}
|
||||
onCheckedChange={(checked) => onChange(propertyName, checked)}
|
||||
style={{ width: '2px' }}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -228,13 +228,13 @@ const AboutSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.general.auto_check_update.title')}</SettingRowTitle>
|
||||
<Switch isSelected={autoCheckUpdate} onValueChange={(v) => setAutoCheckUpdate(v)} />
|
||||
<Switch checked={autoCheckUpdate} onCheckedChange={(v) => setAutoCheckUpdate(v)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.general.test_plan.title')}</SettingRowTitle>
|
||||
<Tooltip content={t('settings.general.test_plan.tooltip')}>
|
||||
<Switch isSelected={testPlan} onValueChange={(v) => handleSetTestPlan(v)} />
|
||||
<Switch checked={testPlan} onCheckedChange={(v) => handleSetTestPlan(v)} />
|
||||
</Tooltip>
|
||||
</SettingRow>
|
||||
{testPlan && (
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Switch } from '@cherrystudio/ui'
|
||||
import { permissionModeCards } from '@renderer/config/agent'
|
||||
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||
import useScrollPosition from '@renderer/hooks/useScrollPosition'
|
||||
@ -13,7 +14,7 @@ import type {
|
||||
} from '@renderer/types'
|
||||
import { AgentConfigurationSchema } from '@renderer/types'
|
||||
import { Modal, Tag } from 'antd'
|
||||
import { Alert, Card, Input, Switch } from 'antd'
|
||||
import { Alert, Card, Input } from 'antd'
|
||||
import { ShieldAlert, Wrench } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
@ -401,8 +402,8 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
|
||||
})}
|
||||
checked={isApproved}
|
||||
disabled={isAuto || isUpdatingTools}
|
||||
size="small"
|
||||
onChange={(checked) => handleToggleTool(tool.id, checked)}
|
||||
size="sm"
|
||||
onCheckedChange={(checked) => handleToggleTool(tool.id, checked)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
@ -483,9 +484,9 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
|
||||
name: server.name
|
||||
})}
|
||||
checked={isSelected}
|
||||
size="small"
|
||||
size="sm"
|
||||
disabled={!server.isActive || isUpdatingMcp}
|
||||
onChange={(checked) => handleToggleMcp(server.id, checked)}
|
||||
onCheckedChange={(checked) => handleToggleMcp(server.id, checked)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -87,10 +87,9 @@ const AssistantMCPSettings: React.FC<Props> = ({ assistant, updateAssistant }) =
|
||||
: undefined
|
||||
}>
|
||||
<Switch
|
||||
isSelected={isEnabled}
|
||||
checked={isEnabled}
|
||||
disabled={!server.isActive}
|
||||
onValueChange={() => handleServerToggle(server.id)}
|
||||
size="sm"
|
||||
onCheckedChange={() => handleServerToggle(server.id)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ServerItem>
|
||||
|
||||
@ -94,8 +94,8 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
||||
: ''
|
||||
}>
|
||||
<Switch
|
||||
isSelected={assistant.enableMemory || false}
|
||||
onValueChange={handleMemoryToggle}
|
||||
checked={assistant.enableMemory || false}
|
||||
onCheckedChange={handleMemoryToggle}
|
||||
disabled={!isMemoryEnabled}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
@ -246,8 +246,8 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
|
||||
</Label>
|
||||
</RowFlex>
|
||||
<Switch
|
||||
isSelected={enableTemperature}
|
||||
onValueChange={(enabled) => {
|
||||
checked={enableTemperature}
|
||||
onCheckedChange={(enabled) => {
|
||||
setEnableTemperature(enabled)
|
||||
updateAssistantSettings({ enableTemperature: enabled })
|
||||
}}
|
||||
@ -295,8 +295,8 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
|
||||
/>
|
||||
</RowFlex>
|
||||
<Switch
|
||||
isSelected={enableTopP}
|
||||
onValueChange={(enabled) => {
|
||||
checked={enableTopP}
|
||||
onCheckedChange={(enabled) => {
|
||||
setEnableTopP(enabled)
|
||||
updateAssistantSettings({ enableTopP: enabled })
|
||||
}}
|
||||
@ -387,8 +387,8 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
|
||||
/>
|
||||
</RowFlex>
|
||||
<Switch
|
||||
isSelected={enableMaxTokens}
|
||||
onValueChange={async (enabled) => {
|
||||
checked={enableMaxTokens}
|
||||
onCheckedChange={async (enabled) => {
|
||||
if (enabled) {
|
||||
const confirmed = await modalConfirm({
|
||||
title: t('chat.settings.max_tokens.confirm'),
|
||||
@ -430,8 +430,8 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
|
||||
<SettingRow style={{ minHeight: 30 }}>
|
||||
<Label>{t('models.stream_output')}</Label>
|
||||
<Switch
|
||||
isSelected={streamOutput}
|
||||
onValueChange={(checked) => {
|
||||
checked={streamOutput}
|
||||
onCheckedChange={(checked) => {
|
||||
setStreamOutput(checked)
|
||||
updateAssistantSettings({ streamOutput: checked })
|
||||
}}
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
WifiOutlined,
|
||||
YuqueOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { Button, RowFlex } from '@cherrystudio/ui'
|
||||
import { Button, RowFlex, Switch } from '@cherrystudio/ui'
|
||||
import { usePreference } from '@data/hooks/usePreference'
|
||||
import DividerWithText from '@renderer/components/DividerWithText'
|
||||
import { NutstoreIcon } from '@renderer/components/Icons/NutstoreIcons'
|
||||
@ -22,7 +22,7 @@ import { reset } from '@renderer/services/BackupService'
|
||||
import type { AppInfo } from '@renderer/types'
|
||||
import { formatFileSize } from '@renderer/utils'
|
||||
import { occupiedDirs } from '@shared/config/constant'
|
||||
import { Progress, Switch, Typography } from 'antd'
|
||||
import { Progress, Typography } from 'antd'
|
||||
import { FileText, FolderCog, FolderInput, FolderOpen, SaveIcon } from 'lucide-react'
|
||||
import type { FC } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
@ -291,7 +291,7 @@ const DataSettings: FC = () => {
|
||||
<MigrationPathRow style={{ marginTop: '20px', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Switch
|
||||
defaultChecked={shouldCopyData}
|
||||
onChange={(checked) => (shouldCopyData = checked)}
|
||||
onCheckedChange={(checked) => (shouldCopyData = checked)}
|
||||
style={{ marginRight: '8px' }}
|
||||
title={t('settings.data.app_data.copy_data_option')}
|
||||
/>
|
||||
@ -616,7 +616,7 @@ const DataSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.backup.skip_file_data_title')}</SettingRowTitle>
|
||||
<Switch checked={skipBackupFile} onChange={onSkipBackupFilesChange} />
|
||||
<Switch checked={skipBackupFile} onCheckedChange={onSkipBackupFilesChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
|
||||
|
||||
@ -35,18 +35,15 @@ const ExportMenuOptions: FC = () => {
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.image')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.image}
|
||||
onValueChange={(checked) => handleToggleOption('image', checked)}
|
||||
/>
|
||||
<Switch checked={exportMenuOptions.image} onCheckedChange={(checked) => handleToggleOption('image', checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.markdown')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.markdown}
|
||||
onValueChange={(checked) => handleToggleOption('markdown', checked)}
|
||||
checked={exportMenuOptions.markdown}
|
||||
onCheckedChange={(checked) => handleToggleOption('markdown', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -54,8 +51,8 @@ const ExportMenuOptions: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.markdown_reason')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.markdown_reason}
|
||||
onValueChange={(checked) => handleToggleOption('markdown_reason', checked)}
|
||||
checked={exportMenuOptions.markdown_reason}
|
||||
onCheckedChange={(checked) => handleToggleOption('markdown_reason', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -63,26 +60,23 @@ const ExportMenuOptions: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.notion')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.notion}
|
||||
onValueChange={(checked) => handleToggleOption('notion', checked)}
|
||||
checked={exportMenuOptions.notion}
|
||||
onCheckedChange={(checked) => handleToggleOption('notion', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.yuque')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.yuque}
|
||||
onValueChange={(checked) => handleToggleOption('yuque', checked)}
|
||||
/>
|
||||
<Switch checked={exportMenuOptions.yuque} onCheckedChange={(checked) => handleToggleOption('yuque', checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.joplin')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.joplin}
|
||||
onValueChange={(checked) => handleToggleOption('joplin', checked)}
|
||||
checked={exportMenuOptions.joplin}
|
||||
onCheckedChange={(checked) => handleToggleOption('joplin', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -90,8 +84,8 @@ const ExportMenuOptions: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.obsidian')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.obsidian}
|
||||
onValueChange={(checked) => handleToggleOption('obsidian', checked)}
|
||||
checked={exportMenuOptions.obsidian}
|
||||
onCheckedChange={(checked) => handleToggleOption('obsidian', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -99,23 +93,23 @@ const ExportMenuOptions: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.siyuan')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.siyuan}
|
||||
onValueChange={(checked) => handleToggleOption('siyuan', checked)}
|
||||
checked={exportMenuOptions.siyuan}
|
||||
onCheckedChange={(checked) => handleToggleOption('siyuan', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.docx')}</SettingRowTitle>
|
||||
<Switch isSelected={exportMenuOptions.docx} onValueChange={(checked) => handleToggleOption('docx', checked)} />
|
||||
<Switch checked={exportMenuOptions.docx} onCheckedChange={(checked) => handleToggleOption('docx', checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.export_menu.plain_text')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={exportMenuOptions.plain_text}
|
||||
onValueChange={(checked) => handleToggleOption('plain_text', checked)}
|
||||
checked={exportMenuOptions.plain_text}
|
||||
onCheckedChange={(checked) => handleToggleOption('plain_text', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
|
||||
@ -122,7 +122,7 @@ const JoplinSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.joplin.export_reasoning.title')}</SettingRowTitle>
|
||||
<Switch isSelected={joplinExportReasoning} onValueChange={handleToggleJoplinExportReasoning} />
|
||||
<Switch checked={joplinExportReasoning} onCheckedChange={handleToggleJoplinExportReasoning} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.joplin.export_reasoning.help')}</SettingHelpText>
|
||||
|
||||
@ -261,7 +261,7 @@ const LocalBackupSettings: React.FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.backup.skip_file_data_title')}</SettingRowTitle>
|
||||
<Switch isSelected={localBackupSkipBackupFile} onValueChange={onSkipBackupFilesChange} />
|
||||
<Switch checked={localBackupSkipBackupFile} onCheckedChange={onSkipBackupFilesChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
|
||||
|
||||
@ -98,7 +98,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.markdown_export.force_dollar_math.title')}</SettingRowTitle>
|
||||
<Switch isSelected={forceDollarMathInMarkdown} onValueChange={handleToggleForceDollarMath} />
|
||||
<Switch checked={forceDollarMathInMarkdown} onCheckedChange={handleToggleForceDollarMath} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.markdown_export.force_dollar_math.help')}</SettingHelpText>
|
||||
@ -106,7 +106,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.message_title.use_topic_naming.title')}</SettingRowTitle>
|
||||
<Switch isSelected={useTopicNamingForMessageTitle} onValueChange={handleToggleTopicNaming} />
|
||||
<Switch checked={useTopicNamingForMessageTitle} onCheckedChange={handleToggleTopicNaming} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.message_title.use_topic_naming.help')}</SettingHelpText>
|
||||
@ -114,7 +114,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.markdown_export.show_model_name.title')}</SettingRowTitle>
|
||||
<Switch isSelected={showModelNameInExport} onValueChange={handleToggleShowModelName} />
|
||||
<Switch checked={showModelNameInExport} onCheckedChange={handleToggleShowModelName} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.markdown_export.show_model_name.help')}</SettingHelpText>
|
||||
@ -122,7 +122,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.markdown_export.show_model_provider.title')}</SettingRowTitle>
|
||||
<Switch isSelected={showModelProviderInMarkdown} onValueChange={handleToggleShowModelProvider} />
|
||||
<Switch checked={showModelProviderInMarkdown} onCheckedChange={handleToggleShowModelProvider} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.markdown_export.show_model_provider.help')}</SettingHelpText>
|
||||
@ -130,7 +130,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.markdown_export.exclude_citations.title')}</SettingRowTitle>
|
||||
<Switch isSelected={excludeCitationsInExport} onValueChange={handleToggleExcludeCitations} />
|
||||
<Switch checked={excludeCitationsInExport} onCheckedChange={handleToggleExcludeCitations} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.markdown_export.exclude_citations.help')}</SettingHelpText>
|
||||
@ -138,7 +138,7 @@ const MarkdownExportSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.markdown_export.standardize_citations.title')}</SettingRowTitle>
|
||||
<Switch isSelected={standardizeCitationsInExport} onValueChange={handleToggleStandardizeCitations} />
|
||||
<Switch checked={standardizeCitationsInExport} onCheckedChange={handleToggleStandardizeCitations} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.markdown_export.standardize_citations.help')}</SettingHelpText>
|
||||
|
||||
@ -128,7 +128,7 @@ const NotionSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.notion.export_reasoning.title')}</SettingRowTitle>
|
||||
<Switch isSelected={notionExportReasoning} onValueChange={handleNotionExportReasoningChange} />
|
||||
<Switch checked={notionExportReasoning} onCheckedChange={handleNotionExportReasoningChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.notion.export_reasoning.help')}</SettingHelpText>
|
||||
|
||||
@ -319,7 +319,7 @@ const NutstoreSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.backup.skip_file_data_title')}</SettingRowTitle>
|
||||
<Switch isSelected={nutstoreSkipBackupFile} onValueChange={onSkipBackupFilesChange} />
|
||||
<Switch checked={nutstoreSkipBackupFile} onCheckedChange={onSkipBackupFilesChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
|
||||
|
||||
@ -243,7 +243,7 @@ const S3Settings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.s3.skipBackupFile.label')}</SettingRowTitle>
|
||||
<Switch isSelected={s3SkipBackupFile} onValueChange={onSkipBackupFilesChange} />
|
||||
<Switch checked={s3SkipBackupFile} onCheckedChange={onSkipBackupFilesChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.s3.skipBackupFile.help')}</SettingHelpText>
|
||||
|
||||
@ -201,7 +201,7 @@ const WebDavSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.backup.skip_file_data_title')}</SettingRowTitle>
|
||||
<Switch isSelected={webdavSkipBackupFile} onValueChange={onSkipBackupFilesChange} />
|
||||
<Switch checked={webdavSkipBackupFile} onCheckedChange={onSkipBackupFilesChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
|
||||
@ -209,7 +209,7 @@ const WebDavSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.webdav.disableStream.title')}</SettingRowTitle>
|
||||
<Switch isSelected={webdavDisableStream} onValueChange={onDisableStreamChange} />
|
||||
<Switch checked={webdavDisableStream} onCheckedChange={onDisableStreamChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.webdav.disableStream.help')}</SettingHelpText>
|
||||
|
||||
@ -231,7 +231,7 @@ const DisplaySettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.theme.window.style.transparent')}</SettingRowTitle>
|
||||
<Switch isSelected={windowStyle === 'transparent'} onValueChange={handleWindowStyleChange} />
|
||||
<Switch checked={windowStyle === 'transparent'} onCheckedChange={handleWindowStyleChange} />
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
@ -355,8 +355,8 @@ const DisplaySettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.advanced.auto_switch_to_topics')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={clickAssistantToShowTopic}
|
||||
onValueChange={(checked) => setClickAssistantToShowTopic(checked)}
|
||||
checked={clickAssistantToShowTopic}
|
||||
onCheckedChange={(checked) => setClickAssistantToShowTopic(checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
@ -364,12 +364,12 @@ const DisplaySettings: FC = () => {
|
||||
)}
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.topic.show.time')}</SettingRowTitle>
|
||||
<Switch isSelected={showTopicTime} onValueChange={(checked) => setShowTopicTime(checked)} />
|
||||
<Switch checked={showTopicTime} onCheckedChange={(checked) => setShowTopicTime(checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.topic.pin_to_top')}</SettingRowTitle>
|
||||
<Switch isSelected={pinTopicsToTop} onValueChange={(checked) => setPinTopicsToTop(checked)} />
|
||||
<Switch checked={pinTopicsToTop} onCheckedChange={(checked) => setPinTopicsToTop(checked)} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={theme}>
|
||||
|
||||
@ -268,12 +268,12 @@ const GeneralSettings: FC = () => {
|
||||
/>
|
||||
)}
|
||||
</RowFlex>
|
||||
<Switch isSelected={enableSpellCheck} onValueChange={handleSpellCheckChange} />
|
||||
<Switch checked={enableSpellCheck} onCheckedChange={handleSpellCheckChange} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.hardware_acceleration.title')}</SettingRowTitle>
|
||||
<Switch isSelected={disableHardwareAcceleration} onValueChange={handleHardwareAccelerationChange} />
|
||||
<Switch checked={disableHardwareAcceleration} onCheckedChange={handleHardwareAccelerationChange} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={theme}>
|
||||
@ -289,24 +289,24 @@ const GeneralSettings: FC = () => {
|
||||
/>
|
||||
</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={notificationSettings.assistant}
|
||||
onValueChange={(v) => handleNotificationChange('assistant', v)}
|
||||
checked={notificationSettings.assistant}
|
||||
onCheckedChange={(v) => handleNotificationChange('assistant', v)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.notification.backup')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={notificationSettings.backup}
|
||||
onValueChange={(v) => handleNotificationChange('backup', v)}
|
||||
checked={notificationSettings.backup}
|
||||
onCheckedChange={(v) => handleNotificationChange('backup', v)}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.notification.knowledge_embed')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={notificationSettings.knowledge}
|
||||
onValueChange={(v) => handleNotificationChange('knowledge', v)}
|
||||
checked={notificationSettings.knowledge}
|
||||
onCheckedChange={(v) => handleNotificationChange('knowledge', v)}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
@ -315,12 +315,12 @@ const GeneralSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.launch.onboot')}</SettingRowTitle>
|
||||
<Switch isSelected={launchOnBoot} onValueChange={(checked) => updateLaunchOnBoot(checked)} />
|
||||
<Switch checked={launchOnBoot} onCheckedChange={(checked) => updateLaunchOnBoot(checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.launch.totray')}</SettingRowTitle>
|
||||
<Switch isSelected={launchToTray} onValueChange={(checked) => updateLaunchToTray(checked)} />
|
||||
<Switch checked={launchToTray} onCheckedChange={(checked) => updateLaunchToTray(checked)} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={theme}>
|
||||
@ -328,12 +328,12 @@ const GeneralSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.tray.show')}</SettingRowTitle>
|
||||
<Switch isSelected={tray} onValueChange={(checked) => updateTray(checked)} />
|
||||
<Switch checked={tray} onCheckedChange={(checked) => updateTray(checked)} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.tray.onclose')}</SettingRowTitle>
|
||||
<Switch isSelected={trayOnClose} onValueChange={(checked) => updateTrayOnClose(checked)} />
|
||||
<Switch checked={trayOnClose} onCheckedChange={(checked) => updateTrayOnClose(checked)} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={theme}>
|
||||
@ -342,8 +342,8 @@ const GeneralSettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.privacy.enable_privacy_mode')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={enableDataCollection}
|
||||
onValueChange={(v) => {
|
||||
checked={enableDataCollection}
|
||||
onCheckedChange={(v) => {
|
||||
setEnableDataCollection(v)
|
||||
window.api.config.set('enableDataCollection', v)
|
||||
}}
|
||||
@ -358,7 +358,7 @@ const GeneralSettings: FC = () => {
|
||||
<SettingRowTitle>{t('settings.developer.enable_developer_mode')}</SettingRowTitle>
|
||||
<InfoTooltip content={t('settings.developer.help')} />
|
||||
</Flex>
|
||||
<Switch isSelected={enableDeveloperMode} onValueChange={setEnableDeveloperMode} />
|
||||
<Switch checked={enableDeveloperMode} onCheckedChange={setEnableDeveloperMode} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
</SettingContainer>
|
||||
|
||||
@ -116,11 +116,10 @@ const McpServerCard: FC<McpServerCardProps> = ({
|
||||
</ServerNameWrapper>
|
||||
<ToolbarWrapper onClick={(e) => e.stopPropagation()}>
|
||||
<Switch
|
||||
isSelected={server.isActive}
|
||||
checked={server.isActive}
|
||||
key={server.id}
|
||||
disabled={isLoading}
|
||||
onValueChange={onToggle}
|
||||
size="sm"
|
||||
onCheckedChange={onToggle}
|
||||
data-no-dnd
|
||||
/>
|
||||
<Button size="sm" variant="destructive" className="rounded-full" onClick={onDelete}>
|
||||
|
||||
@ -653,7 +653,7 @@ const McpSettings: React.FC = () => {
|
||||
tooltip={t('settings.mcp.longRunningTooltip')}
|
||||
layout="horizontal"
|
||||
valuePropName="checked">
|
||||
<Switch size="sm" className="ml-2.5" />
|
||||
<Switch className="ml-2.5" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="timeout"
|
||||
@ -758,10 +758,10 @@ const McpSettings: React.FC = () => {
|
||||
</Flex>
|
||||
<Flex className="items-center gap-4">
|
||||
<Switch
|
||||
isSelected={server.isActive}
|
||||
checked={server.isActive}
|
||||
key={server.id}
|
||||
isLoading={loadingServer === server.id}
|
||||
onValueChange={onToggleActive}
|
||||
loading={loadingServer === server.id}
|
||||
onCheckedChange={onToggleActive}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
@ -146,7 +146,7 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
|
||||
width: 150, // Fixed width might be good for alignment
|
||||
align: 'center',
|
||||
render: (_, tool) => (
|
||||
<Switch isSelected={isToolEnabled(tool)} onValueChange={(checked) => handleToggle(tool, checked)} size="sm" />
|
||||
<Switch checked={isToolEnabled(tool)} onCheckedChange={(checked) => handleToggle(tool, checked)} />
|
||||
)
|
||||
},
|
||||
{
|
||||
@ -169,10 +169,9 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
|
||||
: t('settings.mcp.tools.autoApprove.tooltip.disabled')
|
||||
}>
|
||||
<Switch
|
||||
isSelected={isToolAutoApproved(tool, server)}
|
||||
checked={isToolAutoApproved(tool, server)}
|
||||
disabled={!isToolEnabled(tool)}
|
||||
onValueChange={(checked) => handleAutoApproveToggle(tool, checked)}
|
||||
size="sm"
|
||||
onCheckedChange={(checked) => handleAutoApproveToggle(tool, checked)}
|
||||
/>
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
@ -585,7 +585,7 @@ const MemorySettings = () => {
|
||||
<TextBadge text="Beta" />
|
||||
</RowFlex>
|
||||
<RowFlex className="items-center gap-2.5">
|
||||
<Switch isSelected={globalMemoryEnabled} onValueChange={handleGlobalMemoryToggle} />
|
||||
<Switch checked={globalMemoryEnabled} onCheckedChange={handleGlobalMemoryToggle} />
|
||||
<Button variant="ghost" onClick={() => setSettingsModalVisible(true)} size="icon">
|
||||
<Settings2 size={16} />
|
||||
</Button>
|
||||
|
||||
@ -173,8 +173,8 @@ const AssistantSettings: FC = () => {
|
||||
</RowFlex>
|
||||
<Switch
|
||||
style={{ marginLeft: 10 }}
|
||||
isSelected={enableTemperature}
|
||||
onValueChange={(enabled) => {
|
||||
checked={enableTemperature}
|
||||
onCheckedChange={(enabled) => {
|
||||
setEnableTemperature(enabled)
|
||||
onUpdateAssistantSettings({ enableTemperature: enabled })
|
||||
}}
|
||||
@ -215,8 +215,8 @@ const AssistantSettings: FC = () => {
|
||||
</RowFlex>
|
||||
<Switch
|
||||
style={{ marginLeft: 10 }}
|
||||
isSelected={enableTopP}
|
||||
onValueChange={(enabled) => {
|
||||
checked={enableTopP}
|
||||
onCheckedChange={(enabled) => {
|
||||
setEnableTopP(enabled)
|
||||
onUpdateAssistantSettings({ enableTopP: enabled })
|
||||
}}
|
||||
@ -280,8 +280,8 @@ const AssistantSettings: FC = () => {
|
||||
</RowFlex>
|
||||
<Switch
|
||||
style={{ marginLeft: 10 }}
|
||||
isSelected={enableMaxTokens}
|
||||
onValueChange={async (enabled) => {
|
||||
checked={enableMaxTokens}
|
||||
onCheckedChange={async (enabled) => {
|
||||
if (enabled) {
|
||||
const confirmed = await modalConfirm({
|
||||
title: t('chat.settings.max_tokens.confirm'),
|
||||
|
||||
@ -59,7 +59,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
<ColFlex className="items-stretch gap-2">
|
||||
<RowFlex className="items-center gap-4">
|
||||
<div>{t('settings.models.topic_naming.auto')}</div>
|
||||
<Switch isSelected={enableTopicNaming} onValueChange={setEnableTopicNaming} />
|
||||
<Switch checked={enableTopicNaming} onCheckedChange={setEnableTopicNaming} />
|
||||
</RowFlex>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<div>
|
||||
|
||||
@ -164,8 +164,8 @@ const NotesSettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('notes.settings.display.compress_content')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={!settings.isFullWidth}
|
||||
onValueChange={(checked) => updateSettings({ isFullWidth: !checked })}
|
||||
checked={!settings.isFullWidth}
|
||||
onCheckedChange={(checked) => updateSettings({ isFullWidth: !checked })}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingHelpText>{t('notes.settings.display.compress_content_description')}</SettingHelpText>
|
||||
@ -188,8 +188,8 @@ const NotesSettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('notes.settings.display.show_table_of_contents')}</SettingRowTitle>
|
||||
<Switch
|
||||
isSelected={settings.showTableOfContents}
|
||||
onValueChange={(checked) => updateSettings({ showTableOfContents: checked })}
|
||||
checked={settings.showTableOfContents}
|
||||
onCheckedChange={(checked) => updateSettings({ showTableOfContents: checked })}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingHelpText>{t('notes.settings.display.show_table_of_contents_description')}</SettingHelpText>
|
||||
|
||||
@ -124,7 +124,7 @@ const ApiOptionsSettings = ({ providerId }: Props) => {
|
||||
</label>
|
||||
<InfoTooltip content={item.tip}></InfoTooltip>
|
||||
</RowFlex>
|
||||
<Switch id={item.key} isSelected={item.checked} onValueChange={item.onChange} />
|
||||
<Switch id={item.key} checked={item.checked} onCheckedChange={item.onChange} />
|
||||
</RowFlex>
|
||||
))}
|
||||
</ColFlex>
|
||||
|
||||
@ -343,10 +343,9 @@ const ModelEditContent: FC<ModelEditContentProps & ModalProps> = ({ provider, mo
|
||||
label={t('settings.models.add.supported_text_delta.label')}
|
||||
tooltip={t('settings.models.add.supported_text_delta.tooltip')}>
|
||||
<Switch
|
||||
isSelected={supportedTextDelta}
|
||||
checked={supportedTextDelta}
|
||||
className="ml-auto"
|
||||
size="sm"
|
||||
onValueChange={(checked) => {
|
||||
onCheckedChange={(checked) => {
|
||||
setSupportedTextDelta(checked)
|
||||
// 直接传递新值给autoSave
|
||||
autoSave({ supported_text_delta: checked })
|
||||
|
||||
@ -410,9 +410,9 @@ const ProviderSetting: FC<Props> = ({ providerId }) => {
|
||||
)}
|
||||
</Flex>
|
||||
<Switch
|
||||
isSelected={provider.enabled}
|
||||
checked={provider.enabled}
|
||||
key={provider.id}
|
||||
onValueChange={(enabled) => {
|
||||
onCheckedChange={(enabled) => {
|
||||
updateProvider({ apiHost, enabled })
|
||||
if (enabled) {
|
||||
moveProviderToTop(provider.id)
|
||||
|
||||
@ -82,14 +82,14 @@ const QuickAssistantSettings: FC = () => {
|
||||
iconProps={{ className: 'cursor-pointer' }}
|
||||
/>
|
||||
</SettingRowTitle>
|
||||
<Switch isSelected={enableQuickAssistant} onValueChange={handleEnableQuickAssistant} />
|
||||
<Switch checked={enableQuickAssistant} onCheckedChange={handleEnableQuickAssistant} />
|
||||
</SettingRow>
|
||||
{enableQuickAssistant && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.quickAssistant.click_tray_to_show')}</SettingRowTitle>
|
||||
<Switch isSelected={clickTrayToShowQuickAssistant} onValueChange={handleClickTrayToShowQuickAssistant} />
|
||||
<Switch checked={clickTrayToShowQuickAssistant} onCheckedChange={handleClickTrayToShowQuickAssistant} />
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
@ -98,7 +98,7 @@ const QuickAssistantSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.quickAssistant.read_clipboard_at_startup')}</SettingRowTitle>
|
||||
<Switch isSelected={readClipboardAtStartup} onValueChange={handleClickReadClipboardAtStartup} />
|
||||
<Switch checked={readClipboardAtStartup} onCheckedChange={handleClickReadClipboardAtStartup} />
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -101,8 +101,8 @@ const SelectionAssistantSettings: FC = () => {
|
||||
{!isSupportedOS && <SettingDescription>{t('selection.settings.enable.description')}</SettingDescription>}
|
||||
</SettingLabel>
|
||||
<Switch
|
||||
isSelected={isSupportedOS && selectionEnabled}
|
||||
onValueChange={handleEnableCheckboxChange}
|
||||
checked={isSupportedOS && selectionEnabled}
|
||||
onCheckedChange={handleEnableCheckboxChange}
|
||||
disabled={!isSupportedOS}
|
||||
/>
|
||||
</SettingRow>
|
||||
@ -162,7 +162,7 @@ const SelectionAssistantSettings: FC = () => {
|
||||
<SettingRowTitle>{t('selection.settings.toolbar.compact_mode.title')}</SettingRowTitle>
|
||||
<SettingDescription>{t('selection.settings.toolbar.compact_mode.description')}</SettingDescription>
|
||||
</SettingLabel>
|
||||
<Switch isSelected={isCompact} onValueChange={setIsCompact} />
|
||||
<Switch checked={isCompact} onCheckedChange={setIsCompact} />
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
|
||||
@ -174,7 +174,7 @@ const SelectionAssistantSettings: FC = () => {
|
||||
<SettingRowTitle>{t('selection.settings.window.follow_toolbar.title')}</SettingRowTitle>
|
||||
<SettingDescription>{t('selection.settings.window.follow_toolbar.description')}</SettingDescription>
|
||||
</SettingLabel>
|
||||
<Switch isSelected={isFollowToolbar} onValueChange={setIsFollowToolbar} />
|
||||
<Switch checked={isFollowToolbar} onCheckedChange={setIsFollowToolbar} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
@ -182,7 +182,7 @@ const SelectionAssistantSettings: FC = () => {
|
||||
<SettingRowTitle>{t('selection.settings.window.remember_size.title')}</SettingRowTitle>
|
||||
<SettingDescription>{t('selection.settings.window.remember_size.description')}</SettingDescription>
|
||||
</SettingLabel>
|
||||
<Switch isSelected={isRemeberWinSize} onValueChange={setIsRemeberWinSize} />
|
||||
<Switch checked={isRemeberWinSize} onCheckedChange={setIsRemeberWinSize} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
@ -190,7 +190,7 @@ const SelectionAssistantSettings: FC = () => {
|
||||
<SettingRowTitle>{t('selection.settings.window.auto_close.title')}</SettingRowTitle>
|
||||
<SettingDescription>{t('selection.settings.window.auto_close.description')}</SettingDescription>
|
||||
</SettingLabel>
|
||||
<Switch isSelected={isAutoClose} onValueChange={setIsAutoClose} />
|
||||
<Switch checked={isAutoClose} onCheckedChange={setIsAutoClose} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
@ -198,7 +198,7 @@ const SelectionAssistantSettings: FC = () => {
|
||||
<SettingRowTitle>{t('selection.settings.window.auto_pin.title')}</SettingRowTitle>
|
||||
<SettingDescription>{t('selection.settings.window.auto_pin.description')}</SettingDescription>
|
||||
</SettingLabel>
|
||||
<Switch isSelected={isAutoPin} onValueChange={setIsAutoPin} />
|
||||
<Switch checked={isAutoPin} onCheckedChange={setIsAutoPin} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
|
||||
@ -392,7 +392,7 @@ const ShortcutSettings: FC = () => {
|
||||
align: 'right',
|
||||
width: '50px',
|
||||
render: (record: Shortcut) => (
|
||||
<Switch size="sm" isSelected={record.enabled} onValueChange={() => dispatch(toggleShortcut(record.key))} />
|
||||
<Switch checked={record.enabled} onCheckedChange={() => dispatch(toggleShortcut(record.key))} />
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
@ -23,7 +23,7 @@ const BasicSettings: FC = () => {
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.tool.websearch.search_with_time')}</SettingRowTitle>
|
||||
<Switch isSelected={searchWithTime} onValueChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||
<Switch checked={searchWithTime} onCheckedChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider style={{ marginTop: 15, marginBottom: 10 }} />
|
||||
<SettingRow style={{ height: 40 }}>
|
||||
|
||||
@ -67,8 +67,8 @@ const TranslateSettings: FC<{
|
||||
<Flex className="items-center justify-between">
|
||||
<div style={{ fontWeight: 500 }}>{t('translate.settings.preview')}</div>
|
||||
<Switch
|
||||
isSelected={enableMarkdown}
|
||||
onValueChange={(checked) => {
|
||||
checked={enableMarkdown}
|
||||
onCheckedChange={(checked) => {
|
||||
setEnableMarkdown(checked)
|
||||
db.settings.put({ id: 'translate:markdown:enabled', value: checked })
|
||||
}}
|
||||
@ -80,9 +80,9 @@ const TranslateSettings: FC<{
|
||||
<RowFlex className="items-center justify-between">
|
||||
<div style={{ fontWeight: 500 }}>{t('translate.settings.autoCopy')}</div>
|
||||
<Switch
|
||||
isSelected={autoCopy}
|
||||
checked={autoCopy}
|
||||
color="primary"
|
||||
onValueChange={(isSelected) => {
|
||||
onCheckedChange={(isSelected) => {
|
||||
updateSettings({ autoCopy: isSelected })
|
||||
}}
|
||||
/>
|
||||
@ -93,9 +93,9 @@ const TranslateSettings: FC<{
|
||||
<Flex className="items-center justify-between">
|
||||
<div style={{ fontWeight: 500 }}>{t('translate.settings.scroll_sync')}</div>
|
||||
<Switch
|
||||
isSelected={isScrollSyncEnabled}
|
||||
checked={isScrollSyncEnabled}
|
||||
color="primary"
|
||||
onValueChange={(isSelected) => {
|
||||
onCheckedChange={(isSelected) => {
|
||||
setIsScrollSyncEnabled(isSelected)
|
||||
db.settings.put({ id: 'translate:scroll:sync', value: isSelected })
|
||||
}}
|
||||
@ -145,9 +145,9 @@ const TranslateSettings: FC<{
|
||||
</RowFlex>
|
||||
</div>
|
||||
<Switch
|
||||
isSelected={isBidirectional}
|
||||
checked={isBidirectional}
|
||||
color="primary"
|
||||
onValueChange={(isSelected) => {
|
||||
onCheckedChange={(isSelected) => {
|
||||
setIsBidirectional(isSelected)
|
||||
// 双向翻译设置不需要持久化,它只是界面状态
|
||||
}}
|
||||
|
||||
55
yarn.lock
55
yarn.lock
@ -231,7 +231,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/openai-compatible@npm:1.0.27, @ai-sdk/openai-compatible@npm:^1.0.19":
|
||||
"@ai-sdk/openai-compatible@npm:1.0.27":
|
||||
version: 1.0.27
|
||||
resolution: "@ai-sdk/openai-compatible@npm:1.0.27"
|
||||
dependencies:
|
||||
@ -243,6 +243,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/openai-compatible@npm:^1.0.19":
|
||||
version: 1.0.19
|
||||
resolution: "@ai-sdk/openai-compatible@npm:1.0.19"
|
||||
dependencies:
|
||||
"@ai-sdk/provider": "npm:2.0.0"
|
||||
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
checksum: 10c0/5b7b21fb515e829c3d8a499a5760ffc035d9b8220695996110e361bd79e9928859da4ecf1ea072735bcbe4977c6dd0661f543871921692e86f8b5bfef14fe0e5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/openai-compatible@patch:@ai-sdk/openai-compatible@npm%3A1.0.27#~/.yarn/patches/@ai-sdk-openai-compatible-npm-1.0.27-06f74278cf.patch":
|
||||
version: 1.0.27
|
||||
resolution: "@ai-sdk/openai-compatible@patch:@ai-sdk/openai-compatible@npm%3A1.0.27#~/.yarn/patches/@ai-sdk-openai-compatible-npm-1.0.27-06f74278cf.patch::version=1.0.27&hash=c44b76"
|
||||
@ -291,6 +303,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/provider-utils@npm:3.0.10":
|
||||
version: 3.0.10
|
||||
resolution: "@ai-sdk/provider-utils@npm:3.0.10"
|
||||
dependencies:
|
||||
"@ai-sdk/provider": "npm:2.0.0"
|
||||
"@standard-schema/spec": "npm:^1.0.0"
|
||||
eventsource-parser: "npm:^3.0.5"
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
checksum: 10c0/d2c16abdb84ba4ef48c9f56190b5ffde224b9e6ae5147c5c713d2623627732d34b96aa9aef2a2ea4b0c49e1b863cc963c7d7ff964a1dc95f0f036097aaaaaa98
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/provider-utils@npm:3.0.17, @ai-sdk/provider-utils@npm:^3.0.10, @ai-sdk/provider-utils@npm:^3.0.17":
|
||||
version: 3.0.17
|
||||
resolution: "@ai-sdk/provider-utils@npm:3.0.17"
|
||||
@ -7730,6 +7755,31 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-switch@npm:^1.2.6":
|
||||
version: 1.2.6
|
||||
resolution: "@radix-ui/react-switch@npm:1.2.6"
|
||||
dependencies:
|
||||
"@radix-ui/primitive": "npm:1.1.3"
|
||||
"@radix-ui/react-compose-refs": "npm:1.1.2"
|
||||
"@radix-ui/react-context": "npm:1.1.2"
|
||||
"@radix-ui/react-primitive": "npm:2.1.3"
|
||||
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
|
||||
"@radix-ui/react-use-previous": "npm:1.1.1"
|
||||
"@radix-ui/react-use-size": "npm:1.1.1"
|
||||
peerDependencies:
|
||||
"@types/react": "*"
|
||||
"@types/react-dom": "*"
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
"@types/react-dom":
|
||||
optional: true
|
||||
checksum: 10c0/888303cbeb0e69ebba5676b225f9ea0f00f61453c6b8a6b66384b5c5c4c7fb0ccc53493c1eb14ec6d436e5b867b302aadd6af51a1f2e6c04581c583fd9be65be
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@radix-ui/react-tabs@npm:^1.1.13":
|
||||
version: 1.1.13
|
||||
resolution: "@radix-ui/react-tabs@npm:1.1.13"
|
||||
@ -13822,6 +13872,7 @@ __metadata:
|
||||
"@paymoapp/electron-shutdown-handler": "npm:^1.1.2"
|
||||
"@playwright/test": "npm:^1.55.1"
|
||||
"@radix-ui/react-context-menu": "npm:^2.2.16"
|
||||
"@radix-ui/react-switch": "npm:^1.2.6"
|
||||
"@reduxjs/toolkit": "npm:^2.2.5"
|
||||
"@shikijs/markdown-it": "npm:^3.12.0"
|
||||
"@strongtz/win32-arm64-msvc": "npm:^0.4.7"
|
||||
@ -18519,7 +18570,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource-parser@npm:^3.0.0":
|
||||
"eventsource-parser@npm:^3.0.0, eventsource-parser@npm:^3.0.5":
|
||||
version: 3.0.5
|
||||
resolution: "eventsource-parser@npm:3.0.5"
|
||||
checksum: 10c0/5cb75e3f84ff1cfa1cee6199d4fd430c4544855ab03e953ddbe5927e7b31bc2af3933ab8aba6440ba160ed2c48972b6c317f27b8a1d0764c7b12e34e249de631
|
||||
|
||||
Loading…
Reference in New Issue
Block a user