// Brand book — same surface as onboarding step 2, but inline editable + live preview.
const { useState: useStateBb } = React;
const BB_FONTS = [
{ name: 'Manrope', vibe: 'Универсальный sans · UI-friendly' },
{ name: 'Inter', vibe: 'Чистый sans · нейтральный' },
{ name: 'Space Grotesk', vibe: 'Геометричный · технологичный' },
{ name: 'Playfair Display', vibe: 'Серифный · редакционный' },
{ name: 'DM Serif Display', vibe: 'Высокий контраст · финансы / люкс' },
{ name: 'Instrument Serif', vibe: 'Тёплый serif · мягкий и литературный' },
{ name: 'JetBrains Mono', vibe: 'Моно · техно' },
];
const BB_TONES = ['Дружелюбный', 'Экспертный', 'Игривый', 'Формальный'];
function Brandbook({ org, onChange }) {
const { Button, Card, ColorSwatch, Pill, Textarea, IconButton, SectionHead } = window.UI;
const I = window.Icons;
const { SlideFrame } = window.Carousel;
const brand = org.brand;
const [fontMenu, setFontMenu] = useStateBb(null);
const [activeKind, setActiveKind] = useStateBb('cover');
const setBrand = (patch) => onChange({ ...org, brand: { ...brand, ...patch } });
const sampleSlide = {
cover: { kind: 'cover', title: 'Ваш бренд в каждой карусели', subtitle: 'Цвета и шрифты подставляются автоматически.', label: '01 · cover', fg: brand.colors[1], bg: brand.colors[0], accent: brand.colors[2] },
header: { kind: 'header', title: 'Раздел документа', body: 'Этот стиль используется для смены темы внутри карусели.', label: '02 · header', fg: brand.colors[0], bg: brand.colors[3] || brand.colors[1], accent: brand.colors[2] },
number: { kind: 'number', number: '7×', body: 'Любая цифра тут смотрится крупно и контрастно.', label: '03 · number', fg: brand.colors[1], bg: brand.colors[0], accent: brand.colors[2] },
quote: { kind: 'quote', body: '«Брендбук — это не правила. Это память бренда.»', author: '— основатель', label: '04 · quote', fg: brand.colors[0], bg: brand.colors[3] || brand.colors[1], accent: brand.colors[2] },
}[activeKind];
return (
{/* Colors */}
Цвета
1–4 цвета. Первый — основной фон, третий — акцент.
{brand.colors.length < 4 && (
)}
{brand.colors.map((c, i) => (
setBrand({ colors: brand.colors.map((x, idx) => idx === i ? v : x) })}
onRemove={brand.colors.length > 1 ? () => setBrand({ colors: brand.colors.filter((_, idx) => idx !== i) }) : null}
removable={brand.colors.length > 1}
size={56}
/>
{c.toUpperCase()}
))}
{/* Fonts */}
Шрифты
Первый — для заголовков, второй — для текста.
{[0, 1].map((idx) => (
{fontMenu === idx && (
{BB_FONTS.map((f) => (
))}
)}
))}
{/* Tone */}
Тон голоса
Несколько ярлыков + описание своими словами.
{BB_TONES.map((t) => {
const on = brand.tonePresets.includes(t);
return
setBrand({ tonePresets: on ? brand.tonePresets.filter(x => x !== t) : [...brand.tonePresets, t] })}>{t};
})}
{/* Brandbook PDF */}
Брендбук в PDF
Загрузите готовый брендбук — Omneee распарсит и будет учитывать всё, что внутри.
{org.brandPdf ? (
{org.brandPdf.name}
{org.brandPdf.pages} стр · {org.brandPdf.size} · {org.brandPdf.when}
распарсен
onChange({ ...org, brandPdf: null })}>
) : (
)}
{/* Brand assets / images */}
{/* Logo slot is special - always shown first */}
setBrand({ logo: l })}/>
{(org.brandImages || []).map((img) => (
))}
{/* Live preview */}
живой превью
{['cover', 'header', 'number', 'quote'].map((k) => (
setActiveKind(k)}>{k}
))}
);
}
window.Screens = window.Screens || {};
window.Screens.Brandbook = Brandbook;
function LogoSlot({ logo, onChange }) {
const I = window.Icons;
return (
);
}