Настройки внешнего вида
Сделай экран настроек внешнего вида: сегментированный переключатель темы на три варианта, ползунок масштаба интерфейса от 80 до 140 процентов с показом текущего значения, переключатель плотной вёрстки и раздел «Ещё» под разделителем с выбором шрифта. Справа от заголовка экрана метка с текущей темой. Состояние храни в компоненте и отдавай наружу через колбэк onChange.
appearance-settings__mcp__claude-code__claude-opus-5__1 · 24.09.2026, 13:34:55 мск
Первый вызов модели получил 12 533 токена входа, включая системный промпт Claude Code; вызовов модели 2, каждый отправлял разговор заново, и самый большой получил 18 407 токенов. Контекст до задачи — оценка по длине текста, для русского текста она занижает число токенов в полтора-два раза.
Компилятор
Ошибок нет: все компоненты и пропсы существуют.
Линтер и покрытие
- inline-styleСтроки 62, 76, 84, 86, 95, 104, 105, 106Инлайновые стили вместо классов и токенов библиотеки
JxBadge, JxDivider, JxSelect, JxSlider, JxSwitch, JxToggle
Ожидались JxToggle, JxSlider, JxSwitch, JxDivider, JxBadge · покрытие 100%
- get_rules({"task":"ui"}) → ~1247 токенов
- get_component_api({"name":"JxToggle"}) → ~368 токенов
- get_component_api({"name":"JxSlider"}) → ~354 токенов
- get_component_api({"name":"JxSwitch"}) → ~289 токенов
- get_component_api({"name":"JxDivider"}) → ~153 токенов
- get_component_api({"name":"JxSelect"}) → ~563 токенов
- get_component_api({"name":"JxBadge"}) → ~274 токенов
Инструменты (search_components,get_component_api,get_component_examples,list_design_tokens,get_docs,get_rules, ~1213)
Вход 4 · выход 2 069 · из кэша 24 546 · в кэш 6 390 · 23 с
Рендер
Код компонента
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
import { useState } from 'react';
import {
JxBadge,
JxDivider,
JxSelect,
JxSlider,
JxSwitch,
JxToggle,
} from '@jinx-ui/react';
type ThemeValue = 'light' | 'dark' | 'system';
export interface AppearanceSettings {
theme: ThemeValue;
scale: number;
compact: boolean;
font: string;
}
export interface AppearanceSettingsScreenProps {
value?: AppearanceSettings;
onChange?: (settings: AppearanceSettings) => void;
}
const THEMES: { value: ThemeValue; label: string }[] = [
{ value: 'light', label: 'Светлая' },
{ value: 'dark', label: 'Тёмная' },
{ value: 'system', label: 'Системная' },
];
const FONTS = [
{ value: 'sans', label: 'Без засечек', meta: 'По умолчанию' },
{ value: 'display', label: 'Акцидентный' },
{ value: 'mono', label: 'Монопространственный' },
];
const DEFAULTS: AppearanceSettings = {
theme: 'system',
scale: 100,
compact: false,
font: 'sans',
};
export default function AppearanceSettingsScreen({
value,
onChange,
}: AppearanceSettingsScreenProps) {
const [settings, setSettings] = useState<AppearanceSettings>(value ?? DEFAULTS);
const current = value ?? settings;
const update = (patch: Partial<AppearanceSettings>) => {
const next = { ...current, ...patch };
setSettings(next);
onChange?.(next);
};
const themeLabel =
THEMES.find((item) => item.value === current.theme)?.label ?? 'Системная';
return (
<section
style={{
display: 'flex',
flexDirection: 'column',
gap: 24,
maxWidth: 520,
padding: 24,
background: 'var(--jx-surface)',
border: '1px solid var(--jx-border)',
borderRadius: 'var(--jx-r-lg)',
boxShadow: 'var(--jx-shadow-sm)',
color: 'var(--jx-text)',
}}
>
<header
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 16,
flexWrap: 'wrap',
}}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<h2
style={{
margin: 0,
fontFamily: 'var(--jx-font-display)',
fontSize: 20,
color: 'var(--jx-text)',
}}
>
Внешний вид
</h2>
<p style={{ margin: 0, fontSize: 13, color: 'var(--jx-text-2)' }}>
Настройки применяются сразу и сохраняются для этого устройства.
</p>
</div>
<JxBadge tone="accent" dot>
{themeLabel}
</JxBadge>
</header>
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<span style={{ fontSize: 13, color: 'var(--jx-text-2)' }}>Тема</span>
<JxToggle
items={THEMES}
value={current.theme}
onValueChange={(next) => update({ theme: next as ThemeValue })}
/>
</div>
<JxSlider
label="Масштаб интерфейса"
min={80}
max={140}
step={10}
unit="%"
showOutput
value={current.scale}
onValueChange={(next) => update({ scale: next })}
/>
<JxSwitch
label="Плотная вёрстка"
checked={current.compact}
onChange={(event) => update({ compact: event.target.checked })}
/>
</div>
<JxDivider label="Ещё" />
<JxSelect
label="Шрифт"
placeholder="Выберите шрифт"
options={FONTS}
value={current.font}
onValueChange={(next) => update({ font: next })}
/>
</section>
);
}