feat: LLM-блоки на страницах цветов и типографики + docs/LLM_CONTEXT.md
- Создан компонент components/llm/LlmBlock.tsx (use client):
LlmBlock, LlmSection, LlmTable, LlmRules — переиспользуемый
паттерн для LLM-спецификаций на всех страницах брендбука
- /foundation/colors: добавлен раздел «LLM-спецификация» (v2.1)
· таблица фирменных цветов Oracal (7 шт.)
· таблица цветов сайта oclinica.ru (11 шт., +3 новых: коралловый,
светло-жёлтый, светло-зелёный)
· таблица контрастности WCAG 2.1
· правила применения ✓/✕
· кнопка «Скопировать» — plain text для LLM
- /foundation/typography: добавлен раздел «LLM-спецификация» (v2.0)
· таблица шрифтов DINPro vs Fira Sans
· шкала DINPro (6 стилей)
· шкала Fira Sans (11 стилей, включая letter-spacing)
· применение по носителям + правила ✓/✕
- docs/LLM_CONTEXT.md: создан сводный машиночитаемый файл бренда
Версия 2.1: все цвета, типографика, логотип, оффлайн, CSS-vars,
правила — единый контекст для AI при работе с брендом клиники
- docs/TZ.md: добавлено требование ФТ-03-LLM
- docs/SPRINTS.md: задачи LLM-блоков в Sprint 3–8
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
interface LlmBlockProps {
|
||||
/** Путь страницы, например "/foundation/colors" */
|
||||
path: string;
|
||||
/** Версия данных, например "v2.1" */
|
||||
version: string;
|
||||
/** Плоский текст для копирования */
|
||||
specText: string;
|
||||
/** Содержимое блока — таблицы, правила */
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* LlmBlock — переиспользуемый блок LLM-спецификации.
|
||||
* Добавляется в конец каждой страницы брендбука, содержащей дизайн-стандарты.
|
||||
* Требование: ФТ-03-LLM (TZ.md) · docs/LLM_CONTEXT.md
|
||||
*/
|
||||
export function LlmBlock({ path, version, specText, children }: LlmBlockProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
function handleCopy(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(specText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<details
|
||||
open
|
||||
className="rounded-xl overflow-hidden"
|
||||
style={{
|
||||
border: "2px dashed var(--brand-053m)",
|
||||
}}
|
||||
>
|
||||
{/* Заголовок */}
|
||||
<summary
|
||||
className="flex items-center justify-between px-5 py-3 cursor-pointer select-none list-none"
|
||||
style={{ background: "rgba(126,207,202,0.07)" }}
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span
|
||||
className="shrink-0 text-[10px] font-bold px-1.5 py-0.5 rounded tracking-wider"
|
||||
style={{ background: "var(--brand-053m)", color: "#fff" }}
|
||||
>
|
||||
LLM
|
||||
</span>
|
||||
<span className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}>
|
||||
LLM-спецификация
|
||||
</span>
|
||||
<span
|
||||
className="text-xs truncate hidden sm:inline"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
· машиночитаемые данные · docs/LLM_CONTEXT.md
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 shrink-0 ml-3">
|
||||
<span
|
||||
className="text-[10px] font-mono hidden md:inline"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{path} · {version}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="text-xs px-3 py-1 rounded font-medium transition-colors shrink-0"
|
||||
style={{
|
||||
background: copied ? "#d1fae5" : "var(--brand-053m)",
|
||||
color: copied ? "#065f46" : "#fff",
|
||||
}}
|
||||
>
|
||||
{copied ? "✓ Скопировано" : "Скопировать"}
|
||||
</button>
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
{/* Содержимое */}
|
||||
<div
|
||||
className="px-5 py-5 space-y-6 border-t"
|
||||
style={{
|
||||
borderColor: "var(--brand-053m)",
|
||||
borderStyle: "dashed",
|
||||
background: "var(--bb-content-bg)",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
/* ─── Утилиты для содержимого блока ──────────────────────────── */
|
||||
|
||||
/** Заголовок подсекции внутри LLM-блока */
|
||||
export function LlmSection({ title }: { title: string }) {
|
||||
return (
|
||||
<p
|
||||
className="text-[10px] font-semibold uppercase tracking-widest pb-1 border-b"
|
||||
style={{ color: "var(--bb-text-muted)", borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
{title}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
/** Компактная таблица для LLM-блока */
|
||||
export function LlmTable({
|
||||
headers,
|
||||
rows,
|
||||
}: {
|
||||
headers: string[];
|
||||
rows: (string | React.ReactNode)[][];
|
||||
}) {
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs border-collapse font-mono">
|
||||
<thead>
|
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h}
|
||||
className="text-left px-2 py-1.5 font-medium border whitespace-nowrap"
|
||||
style={{ color: "var(--bb-text-muted)", borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
{h}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, ri) => (
|
||||
<tr key={ri} style={{ borderTop: `1px solid var(--bb-border)` }}>
|
||||
{row.map((cell, ci) => (
|
||||
<td
|
||||
key={ci}
|
||||
className="px-2 py-1 border"
|
||||
style={{
|
||||
borderColor: "var(--bb-border)",
|
||||
color: "var(--bb-text-muted)",
|
||||
maxWidth: "240px",
|
||||
}}
|
||||
>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Список правил ✓ / ✕ */
|
||||
export function LlmRules({ rules }: { rules: { ok: boolean; text: string }[] }) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-0.5">
|
||||
{rules.map((r) => (
|
||||
<div key={r.text} className="flex items-start gap-1.5 py-0.5 text-xs font-mono">
|
||||
<span
|
||||
style={{ color: r.ok ? "#059669" : "#dc2626", fontWeight: 700, flexShrink: 0 }}
|
||||
>
|
||||
{r.ok ? "✓" : "✕"}
|
||||
</span>
|
||||
<span style={{ color: "var(--bb-text-muted)" }}>{r.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user