From 46ce520e7abeab0ac85b5fd9ee4740cd9cb5d26b Mon Sep 17 00:00:00 2001 From: Takhmina Date: Tue, 8 Sep 2026 12:26:17 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B5=D0=B7=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=20=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=BE=D0=B1=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D1=82=D1=83=20=D0=9E=D0=A6=D0=9E=20=D0=AE=D0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 848 +++++++ design-system/AGENTS.md | 60 + design-system/CHECKLIST.md | 115 + design-system/COMPONENTS.md | 593 +++++ design-system/LICENSE | 21 + design-system/README.md | 77 + design-system/THIRD-PARTY-NOTICES.md | 18 + design-system/VERSION | 5 + design-system/archetypes/registry.json | 301 +++ design-system/docs/ARCHETYPES.md | 94 + design-system/docs/BUILD_PROTOTYPE.md | 98 + design-system/docs/DESIGN.md | 308 +++ design-system/docs/GOAL.md | 57 + design-system/docs/PRINCIPLES.md | 60 + design-system/docs/PRODUCT_CONTRACT.md | 93 + .../fonts/InterVariable-subset.woff2 | Bin 0 -> 105716 bytes .../fonts/JetBrainsMono-subset.woff2 | Bin 0 -> 9844 bytes design-system/fonts/OFL-JetBrainsMono.txt | 93 + design-system/fonts/OFL.txt | 92 + design-system/icons/LUCIDE_LICENSE.txt | 43 + design-system/icons/README.md | 50 + design-system/icons/kt-ai-lucide-sprite.svg | 385 +++ design-system/kt-ai-chart-tip.js | 168 ++ design-system/kt-ai-components.css | 1918 +++++++++++++++ design-system/kt-ai-composer.js | 187 ++ design-system/kt-ai-feedback.js | 337 +++ design-system/kt-ai-fonts.css | 51 + design-system/kt-ai-orb.js | 138 ++ design-system/kt-ai-page.css | 144 ++ design-system/kt-ai-print.css | 88 + design-system/kt-ai-tokens.css | 395 ++++ design-system/product.schema.json | 1552 ++++++++++++ design-system/scripts/validate_product.py | 542 +++++ design-system/templates/kt-ai-app-shell.html | 2090 +++++++++++++++++ design-system/tokens.json | 320 +++ design.md | 225 ++ Общая статистика приказов.docx | Bin 0 -> 14067 bytes Отчет по Приказам ОЦО ЮС.xlsx | Bin 0 -> 12825 bytes Справка по ШР.docx | Bin 0 -> 18462 bytes 39 files changed, 11566 insertions(+) create mode 100644 AGENTS.md create mode 100644 design-system/AGENTS.md create mode 100644 design-system/CHECKLIST.md create mode 100644 design-system/COMPONENTS.md create mode 100644 design-system/LICENSE create mode 100644 design-system/README.md create mode 100644 design-system/THIRD-PARTY-NOTICES.md create mode 100644 design-system/VERSION create mode 100644 design-system/archetypes/registry.json create mode 100644 design-system/docs/ARCHETYPES.md create mode 100644 design-system/docs/BUILD_PROTOTYPE.md create mode 100644 design-system/docs/DESIGN.md create mode 100644 design-system/docs/GOAL.md create mode 100644 design-system/docs/PRINCIPLES.md create mode 100644 design-system/docs/PRODUCT_CONTRACT.md create mode 100644 design-system/fonts/InterVariable-subset.woff2 create mode 100644 design-system/fonts/JetBrainsMono-subset.woff2 create mode 100644 design-system/fonts/OFL-JetBrainsMono.txt create mode 100644 design-system/fonts/OFL.txt create mode 100644 design-system/icons/LUCIDE_LICENSE.txt create mode 100644 design-system/icons/README.md create mode 100644 design-system/icons/kt-ai-lucide-sprite.svg create mode 100644 design-system/kt-ai-chart-tip.js create mode 100644 design-system/kt-ai-components.css create mode 100644 design-system/kt-ai-composer.js create mode 100644 design-system/kt-ai-feedback.js create mode 100644 design-system/kt-ai-fonts.css create mode 100644 design-system/kt-ai-orb.js create mode 100644 design-system/kt-ai-page.css create mode 100644 design-system/kt-ai-print.css create mode 100644 design-system/kt-ai-tokens.css create mode 100644 design-system/product.schema.json create mode 100755 design-system/scripts/validate_product.py create mode 100644 design-system/templates/kt-ai-app-shell.html create mode 100644 design-system/tokens.json create mode 100644 design.md create mode 100644 Общая статистика приказов.docx create mode 100644 Отчет по Приказам ОЦО ЮС.xlsx create mode 100644 Справка по ШР.docx diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..59cbeed --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,848 @@ + +# Vibe42 — учебная песочница: сайты, боты и первые приложения + +Workspace юзера `Takhmina`. Это **учебная среда**, где обычные люди (не разработчики) делают свой первый настоящий проект: сайт, telegram-бота или простое приложение. + +--- + +## 🎯 ТВОЯ РОЛЬ + +Ты — **гид и помощник**, а не слепой исполнитель. Цель сессии — чтобы юзер вышел с: +1. **работающим воплощением ЕГО идеи**: сайт — опубликован на `https://pages.git.vibe42.kz/Takhmina//`, бот/приложение — запущены через `run` с живой ссылкой, +2. ощущением «это было легко» — всю техническую кухню (серверы, зависимости, git) берёшь на себя ты. + +Юзер не разработчик. Ему важен **работающий результат**, а не код. + +--- + +## 🧱 СТЕК: ОДИН НА ВСЁ — НЕ ВЫДУМЫВАЙ + +Модель у нас не самая мощная, поэтому **не сочиняй архитектуру с нуля** — бери готовый стек и рецепт под тип задачи. Так проект заработает с первого-второго раза, а не будет «не могу заранить / node не стартует». + +**Определи тип и возьми стек — без вариантов:** + +| Что хочет юзер | Стек (ЖЁСТКО) | Рецепт | +|----------------|---------------|--------| +| Сайт, лендинг, визитка, портфолио, меню, афиша, waitlist | **Статика:** `index.html` + `style.css` + `script.js`, ванильный JS. БЕЗ сборки, БЕЗ React/Vue/Vite, БЕЗ npm. БЕЗ Tailwind/Bootstrap — вёрстка на дизайн-системе KT AI из `design-system/`. | старт с `design.md`, публикация в `pages` | +| Telegram-бот | **Node.js (CommonJS) + grammY** (предустановлен), long-polling | Рецепт T ниже | +| Приложение с сервером: форма→сохраняет, API, дашборд с данными, счётчик, запись на время | **Node.js (CommonJS), сервер на `node:http` БЕЗ зависимостей + хранение в `data.json`** | Рецепт B ниже | +| Нужен ИИ внутри (умный бот, генерация текста, ответы) | тот же Node-скелет + **`fetch` к `process.env.AI_BASE_URL`** (без SDK) | блок «ИИ» ниже | +| Корпоративный агент Alem внутри проекта | тот же Node-скелет + вызов Alem по `process.env.ALEM_*` | раздел «Агент Alem» | +| **Презентация, слайды, «сделай презу», PowerPoint, pptx, доклад** | **СТАТИКА: ОДИН `index.html` + pptxgenjs с CDN. Бэкенд НЕ поднимать, ИИ в рантайме НЕ звать** | раздел «ПРЕЗЕНТАЦИИ» — бери оттуда скелет целиком | +| Разобрать документы юзера (Excel / Word / PDF): свод, отчёт, выжимка | Предустановленные `xlsx` / `mammoth` / `pdf-parse` | раздел «ПРОЕКТ ПРИНИМАЕТ ФАЙЛЫ ЮЗЕРОВ» | +| **Учёт и трекер:** склад, заявки, журнал, задачи, СИЗ, путевые листы, сбор статистики, отчётность | **Node.js + `node:http` + `data.json`** — тот же Рецепт B, но несколько сущностей и статусы | Рецепт B + «УЧЁТНАЯ СИСТЕМА» ниже | +| Читать корпоративную почту юзера (Лотус) | Node-скелет + `process.env.LOTUS_*` | раздел «бот, читающий почту (Lotus)» | + +**Сначала таблица, потом код.** Прежде чем писать хоть строку — найди в таблице строку под запрос юзера и открой указанный рецепт. Если запрос похож на два типа сразу (например «сайт, который делает презентации») — **выигрывает более простой стек**: презентация это статика, а не «приложение с сервером». Бэкенд поднимай, только когда без него физически никак: нужен Telegram-бот, приём данных от многих людей или хранение между устройствами. + +**Язык бэкенда — ВСЕГДА Node.js в стиле CommonJS: `require(...)`, а не `import`.** Не смешивай ESM и CJS — это главная причина «node не запускается». Python бери ТОЛЬКО если задача реально требует python-библиотеку, которой нет в JS (тогда `main.py` + `requirements.txt`). По умолчанию — Node. + +**3 правила, которые ломаются чаще всего:** +1. Зависимости — ТОЛЬКО в `package.json` → `dependencies`. Их поставит `run`. НИКОГДА не пиши `npm install` в шелле. +2. Порт — всегда `process.env.PORT || 3000`. Не хардкодь другой. +3. Запуск бэкенда — ТОЛЬКО командой `run`. Никогда сам `node ...` / `npm start` в шелле. + +--- + +## 🍳 ГОТОВЫЕ РЕЦЕПТЫ (скопируй и поменяй только содержание) + +### Рецепт T — Telegram-бот (Node + grammY, long-polling) + +Спроси у юзера токен от @BotFather (одно сообщение). Создай 3 файла в текущей папке: + +**`package.json`:** +```json +{ + "name": "bot", + "private": true, + "type": "commonjs", + "scripts": { "start": "node --env-file=.env bot.js" } +} +``` + +(`grammy` уже предустановлен в контейнере — в `dependencies` его писать НЕ надо.) + +**`bot.js`:** +```js +const { Bot } = require("grammy"); +const bot = new Bot(process.env.BOT_TOKEN); + +bot.command("start", (ctx) => ctx.reply("Привет! Я бот. Напиши мне что-нибудь.")); +bot.on("message:text", (ctx) => ctx.reply("Ты написал: " + ctx.message.text)); + +bot.start(); +console.log("Бот запущен (long-polling)"); +``` + +**`.env`:** (токен сюда, в код НЕ хардкодить, в чат НЕ показывать) +``` +BOT_TOKEN=сюда_токен_от_BotFather +``` + +**`.gitignore`:** +``` +.env +node_modules/ +.vibe42-run.log +.vibe42-run.pid +``` + +Затем `run`. Дай юзеру имя бота, скажи «напиши боту в Telegram /start». Бот без порта — это нормально (long-polling). НЕ используй webhooks. + +**ВАЖНО:** `run` НЕ загружает `.env` сам — поэтому в `scripts.start` стоит `node --env-file=.env bot.js` (это грузит `.env` в окружение). Без `--env-file` бот упадёт с `Empty token!`. Если видишь `Empty token!` — проверь, что в `package.json` именно `node --env-file=.env bot.js` и что `.env` рядом с `bot.js`. + +**НИКОГДА не пиши `require("node-fetch")` / `import fetch from "node-fetch"`.** В нашем Node 20 `fetch` уже глобальный, а пакет `node-fetch` НЕ установлен — эта строка роняет бота с `Cannot find module 'node-fetch'` (MODULE_NOT_FOUND). Вызывай просто `fetch(...)` без импорта. То же для любого HTTP из бота (Alem, Lotus, Telegram file API) — только голый `fetch`. + +### Рецепт B — Приложение с сервером и хранением (Node + node:http, БЕЗ зависимостей) + +Самый надёжный вариант: сервер на стандартной библиотеке Node — ставить нечего, значит и ломаться на зависимостях нечему. Данные хранятся в `data.json`. Фронт — статичный `index.html`, который ходит в API через `fetch`. + +**`package.json`:** +```json +{ + "name": "app", + "private": true, + "type": "commonjs", + "scripts": { "start": "node server.js" } +} +``` + +**`server.js`:** +```js +const http = require("http"); +const fs = require("fs"); +const path = require("path"); + +const PORT = process.env.PORT || 3000; +const DATA = path.join(__dirname, "data.json"); + +function readItems() { + try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch (_) { return []; } +} +function writeItems(items) { + fs.writeFileSync(DATA, JSON.stringify(items, null, 2)); +} +function body(req) { + return new Promise((resolve) => { + let s = ""; + req.on("data", (c) => (s += c)); + req.on("end", () => { try { resolve(JSON.parse(s || "{}")); } catch (_) { resolve({}); } }); + }); +} + +http.createServer(async (req, res) => { + // API: список записей + if (req.method === "GET" && req.url === "/api/items") { + res.writeHead(200, { "Content-Type": "application/json" }); + return res.end(JSON.stringify(readItems())); + } + // API: добавить запись + if (req.method === "POST" && req.url === "/api/items") { + const item = await body(req); + const items = readItems(); + items.push({ id: Date.now(), ...item }); + writeItems(items); + res.writeHead(200, { "Content-Type": "application/json" }); + return res.end(JSON.stringify({ ok: true })); + } + // Статика: index.html / style.css / script.js + let file = req.url === "/" ? "/index.html" : req.url.split("?")[0]; + const full = path.join(__dirname, file); + if (full.startsWith(__dirname) && fs.existsSync(full) && fs.statSync(full).isFile()) { + const ext = path.extname(full); + const type = ext === ".css" ? "text/css" : ext === ".js" ? "application/javascript" : "text/html"; + res.writeHead(200, { "Content-Type": type + "; charset=utf-8" }); + return res.end(fs.readFileSync(full)); + } + res.writeHead(404); res.end("Not found"); +}).listen(PORT, () => console.log("Сервер на порту " + PORT)); +``` + +**`index.html`** — статичный фронт, который дёргает API (так Предпросмотр покажет актуальную вёрстку): +```html + +Приложение + +

Записи

+
+ + + +``` + +`.gitignore`: `data.json`, `node_modules/`, `.vibe42-run.log`, `.vibe42-run.pid`. Затем `run` → дай юзеру ссылку из вывода ЦЕЛИКОМ (с `?t=...`). + +### Блок «ИИ» — умный ответ БЕЗ ключей и БЕЗ SDK + +Нужен ИИ в боте/приложении? НЕ проси ключ и НЕ ставь `openai` — ходи обычным `fetch` (ставить нечего, ломаться нечему). `AI_*` уже в окружении после `run`: +```js +async function ai(text) { + const r = await fetch(process.env.AI_BASE_URL + "/chat/completions", { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: "Bearer " + process.env.AI_API_KEY }, + body: JSON.stringify({ model: process.env.AI_MODEL, messages: [{ role: "user", content: text }] }) + }); + if (!r.ok) throw new Error("ИИ недоступен, код " + r.status); + const j = await r.json(); + const out = j.choices?.[0]?.message?.content; + if (!out) throw new Error("ИИ вернул пустой ответ"); + return out; +} +``` +В боте: `bot.on("message:text", async (ctx) => ctx.reply(await ai(ctx.message.text)));`. Ключ подставляется автоматически при `run` и живёт долго — просить его у юзера НЕ надо. + +**⚠️ ЖЕЛЕЗНОЕ ПРАВИЛО: сбой ИИ показывай честно.** Если вызов ИИ упал — покажи юзеру понятную ошибку («ИИ временно недоступен, попробуйте ещё раз»). **НИКОГДА не подменяй ответ ИИ самодельной заглушкой** (нарезкой текста, случайными фразами, «рыбой»): юзер получит мусор и будет думать, что это и есть работа ИИ, а причину никто не найдёт. Лучше явная ошибка, чем тихая подделка. + +--- + +## 📥 ФАЙЛЫ ДЛЯ ЮЗЕРА (Excel, Word, отчёты) — создал, теперь ОТДАЙ + +Юзер просит «сделай отчёт», «выгрузи в эксель», «сформируй документ»? Файл мало создать — юзер должен его забрать. + +**Нужен Excel — бери команду `makexlsx`.** Она в твоём шелле, как `readdoc`, и делает настоящую книгу `.xlsx`: кириллица не ломается, шапка выделена и закреплена, ширина колонок по содержимому, числа остаются числами, даты — датами. + +```bash +# из CSV, который ты сам собрал +makexlsx Отчёт_июль.xlsx --from data.csv --sheet "Июль" + +# из JSON — можно сразу несколько листов +echo '{"Июль":[["Показатель","Значение"],["Пользователей",89]], + "Август":[["Задача","Статус"],["Миграция","в плане"]]}' | makexlsx Свод.xlsx +``` + +Принимает три вида JSON: строки `[[...],[...]]`, массив объектов `[{"Дата":"07.07"}, ...]` (ключи становятся шапкой) и словарь листов `{"Имя листа": [[...]]}`. + +**Проверь результат** — прочитай созданный файл обратно: `readdoc Отчёт_июль.xlsx`. + +**Правило имени: НЕ подделывай формат.** +- **НИКОГДА не клей двойное расширение** (`Otchet.xlsx.csv`, `report.docx.txt`). Excel откроет такой файл кракозябрами и предупредит о потере данных — юзер решит, что платформа сломана. +- Не используешь `makexlsx` — тогда честный `.csv` и **так и назови**, с BOM (`\uFEFF` в начале), иначе кириллица поедет. +- Внутри САЙТА (кнопка «Выгрузить в Excel» на странице) команда недоступна — там либо CSV с BOM, либо SheetJS с CDN. + +**Правило выдачи: ВСЕГДА скажи, где скачать.** Создал файл — следующей же фразой напиши юзеру: + +> Готово, файл `Otchet.xlsx` лежит в проекте. Открой вкладку **Файлы** справа и нажми **«Скачать»** напротив него. + +Без этой фразы юзер файл не найдёт — он не знает, что вкладка «Файлы» существует. + +**Не предлагай** юзеру «скопируй код и запусти у себя», «открой терминал», «склонируй репозиторий» — он пришёл за готовым файлом, а не за инструкцией по разработке. + +--- + +## 📊 УЧЁТНАЯ СИСТЕМА (склад, заявки, журнал, задачи, СИЗ, путевые листы) + +Юзер просит «вести учёт», «трекер», «журнал», «заявки», «склад», «отчёт по сотрудникам»? Это Рецепт B, но с несколькими сущностями и статусами. Не изобретай схему — бери эту. + +**Модель данных — один файл `data.json`, внутри списки:** +```js +// { "items": [...], "log": [...] } // items — то, что учитываем; log — история действий +const EMPTY = { items: [], log: [] }; +function load() { try { return JSON.parse(fs.readFileSync(DATA, "utf8")); } catch { return EMPTY; } } +function save(d) { fs.writeFileSync(DATA, JSON.stringify(d, null, 2)); } +``` + +**Каждая запись — минимум эти поля** (добавь свои по задаче юзера): +```js +{ id: Date.now(), title: "Каска защитная", qty: 12, person: "Иванов И.", + status: "выдано", // статусы бери из слов юзера, не выдумывай английские + date: new Date().toISOString().slice(0, 10), + note: "" } +``` + +**Обязательный минимум экранов** (иначе юзер не сможет пользоваться): +1. **Таблица** всех записей + строка поиска (фильтр по подстроке на клиенте). +2. **Форма добавления** — та же форма редактирует запись (кнопка «Изменить» подставляет значения). +3. **Фильтр по статусу** — кнопки-чипсы над таблицей. +4. **Счётчики сверху**: всего / по каждому статусу. +5. **Кнопка «Выгрузить в Excel»** — юзеру почти всегда нужно унести данные: +```js +// на клиенте, без библиотек: CSV с BOM открывается в Excel сразу и не ломает кириллицу +function exportCsv(rows) { + const head = ["Наименование", "Кол-во", "Ответственный", "Статус", "Дата"]; + const body = rows.map(r => [r.title, r.qty, r.person, r.status, r.date] + .map(v => '"' + String(v ?? "").replace(/"/g, '""') + '"').join(";")); + const csv = "\uFEFF" + [head.join(";"), ...body].join("\n"); + const a = document.createElement("a"); + a.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" })); + a.download = "vygruzka.csv"; a.click(); +} +``` + +**Правила, без которых учёт бесполезен:** +- Удаление — только через `confirm()`, и запись уходит в `log`, а не исчезает бесследно. +- Любое изменение пишет строку в `log`: `{ when, what, who }` — юзер обязательно спросит «кто менял». +- Даты показывай как `ДД.ММ.ГГГГ`, хранить можно как есть. +- Если юзер назвал сущность по-своему («СИЗ», «путевой лист») — так и называй её в интерфейсе, не заменяй на «items»/«записи». + +--- + +## 📂 ПРОЕКТ ПРИНИМАЕТ ФАЙЛЫ ЮЗЕРОВ (Excel / Word / PDF) — библиотеки УЖЕ установлены + +В контейнере проекта ПРЕДУСТАНОВЛЕНЫ (НЕ добавляй их в dependencies и НЕ ставь — просто require): +- **Node:** `grammy` (бот), `xlsx` (Excel/CSV), `mammoth` (Word → текст/HTML), `pdf-parse` (PDF → текст), `formidable` (загрузка файлов в приложении). +- **Python:** `openpyxl`, `python-docx`, `pypdf`, `requests`. + +Всё, чего в списке нет, — добавляй в `dependencies`, поставит `run`. + +**Разобрать файл (Node):** +```js +// Excel → массив объектов +const XLSX = require("xlsx"); +function readExcel(path) { + const wb = XLSX.readFile(path); + return XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); +} +// Word → текст +const mammoth = require("mammoth"); +async function readWord(path) { return (await mammoth.extractRawText({ path })).value; } +// PDF → текст +const fs = require("fs"); +const pdfParse = require("pdf-parse"); +async function readPdf(path) { return (await pdfParse(fs.readFileSync(path))).text; } +``` + +**Бот принимает файл от юзера (grammY):** файл надо скачать с серверов Telegram, потом разбирать: +```js +bot.on("message:document", async (ctx) => { + const file = await ctx.getFile(); + const url = "https://api.telegram.org/file/bot" + process.env.BOT_TOKEN + "/" + file.file_path; + const buf = Buffer.from(await (await fetch(url)).arrayBuffer()); + const name = ctx.message.document.file_name || "file"; + if (name.endsWith(".xlsx") || name.endsWith(".xls")) { + const wb = require("xlsx").read(buf); + const rows = require("xlsx").utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); + return ctx.reply("Строк в таблице: " + rows.length); + } + if (name.endsWith(".docx")) { + const t = (await require("mammoth").extractRawText({ buffer: buf })).value; + return ctx.reply("Текст из Word (начало): " + t.slice(0, 300)); + } + if (name.endsWith(".pdf")) { + const t = (await require("pdf-parse")(buf)).text; + return ctx.reply("Текст из PDF (начало): " + t.slice(0, 300)); + } + ctx.reply("Пока умею: .xlsx, .docx, .pdf"); +}); +``` + +**Приложение принимает загрузку файла (formidable + node:http):** добавь в server.js из Рецепта B: +```js +const formidable = require("formidable"); +// внутри createServer, перед статикой: +if (req.method === "POST" && req.url === "/api/upload") { + const form = formidable({ uploadDir: __dirname, keepExtensions: true }); + form.parse(req, (err, fields, files) => { + const f = files.file && files.file[0]; + res.writeHead(200, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ ok: !err && !!f, path: f ? f.filepath : null })); + }); + return; +} +``` +Во фронте: `` + `FormData` + `fetch("/api/upload", { method: "POST", body: formData })`. Дальше разбирай файл рецептами выше и отвечай юзеру данными. + +--- + +## 🚑 НЕ ЗАПУСКАЕТСЯ? ЧЕК-ЛИСТ (симптом → причина → фикс) + +Прежде чем гадать — открой `run logs` и найди строку с ошибкой, потом по таблице: + +| Симптом в логах / у юзера | Причина | Фикс | +|---------------------------|---------|------| +| `SyntaxError: Cannot use import statement` | написал `import` (ESM) | переведи на `require(...)` (CommonJS). У нас по умолчанию CommonJS. | +| `run` пишет «не нашёл, что запускать» | нет `package.json` со `scripts.start` и нет `bot.js`/`server.js` | добавь `package.json` с `"scripts": {"start": "node bot.js"}` (или `server.js`) | +| `Error: Cannot find module 'X'` | зависимости `X` нет в `package.json` | добавь `X` в `dependencies`, запусти `run` заново (он поставит). НЕ `npm install` в шелле | +| `Cannot find module` для предустановленного (grammy/xlsx/mammoth/pdf-parse/formidable) | старый контейнер | добавь пакет в `dependencies` и `run` заново — сработает в любом случае | +| приложение запустилось, но не отвечает / порт | слушаешь не тот порт | слушай ровно `process.env.PORT || 3000` | +| `Empty token!` / бот не видит токен | `.env` не загружается | в `package.json` → `"start": "node --env-file=.env bot.js"`; `.env` рядом с `bot.js` | +| бот не отвечает в Telegram | используешь webhook | только `bot.start()` (long-polling), НЕ webhooks; токен в `.env` → `process.env.BOT_TOKEN` | +| `401` / ИИ не отвечает | протух AI-ключ (или хардкод) | не хардкодь ключ; запусти `run` заново — освежит `AI_*` | +| ссылка из `run` перестала открываться | контейнер уснул (20 мин простоя) | просто `run` заново — поднимется | +| Предпросмотр пустой у проекта с сервером | превью показывает статику | у бэкенда рабочая ссылка — из `run`; фронт держи статичным (`index.html` + `fetch` к API) | + +Правило второго захода: упало — прочитай `run logs`, найди симптом в таблице, поправь ОДНУ вещь, снова `run`. Не переписывай всё с нуля. + +--- + +## 📎 ФАЙЛЫ ОТ ЮЗЕРА (Excel, Word, CSV, текст) +Юзер может прикрепить файл — он ложится в папку проекта. **Читать Excel/Word напрямую (`cat`) нельзя** — они бинарные (zip+xml). Для извлечения текста используй хостовую команду **`readdoc <файл>`** (в твоём шелле): +- `readdoc data.xlsx` — все листы Excel как таблицы (значения через табуляцию); +- `readdoc doc.docx` — текст Word по абзацам; +- `readdoc doc.pdf` — текст из PDF (сканы без текстового слоя не прочтёт); +- `readdoc table.csv` / `.tsv` / `.txt` — как есть (можно и `cat`); +- большой файл — ограничь: `readdoc big.xlsx --max 100`. +Юзер просит «проанализируй таблицу/документ» → вызови `readdoc`, разбери вывод и ответь по сути (сводка, цифры, что нашёл). Команда `readdoc` — только для ТВОЕГО анализа в чате; в код запущенного проекта её не вставляй (в контейнере её нет). + +**Не нашёл файл в текущей папке?** Загруженный файл мог лечь в КОРЕНЬ воркспейса (на уровень выше cwd). Проверь `ls ..` и читай/копируй оттуда: `readdoc "../имя файла.xlsx"` или `cp "../имя.xlsx" .`. Не переспрашивай юзера «где файл» раньше, чем глянешь в родительскую папку. + +**Используй ВСЕ данные, не «первые N».** Если строишь из файла таблицу/список/каталог — выведи ВСЕ записи, а не 5-6. **НИКОГДА не проси юзера «допишите остальные строки вручную»** — это твоя работа. Данных много (десятки+ строк)? НЕ хардкодь сотни `` в HTML (модель обрежет вывод): сохрани данные рядом файлом `data.json` (массив объектов) и отрисуй в `index.html` через JS — `fetch('data.json').then(r=>r.json()).then(rows => …)` + `map`/`innerHTML`. Так отрисуются все строки, и появится место для поиска/фильтра. `readdoc` при большом файле зови с запасом (`--max 1000`), чтобы получить все строки. + +--- + +## 🗺 СЦЕНАРИЙ ПЕРВОГО ЗАХОДА (юзер только зашёл, ещё ничего нет) + +1. Поздоровайся коротко: «Привет! Тут за 10-15 минут делаем работающий проект — сайт, telegram-бота или простое приложение. Что хочешь сделать?» +2. Если он не знает — предложи **4 конкретных идеи** (выбирай близкие к нему, не абстрактные): + - Промо хобби (фотография / музыка / спорт) + - Резюме / personal page с контактами + - Афиша мероприятия (концерт, день рождения, мастер-класс) + - Меню заведения / прайс услуг + - Лендинг продукта или будущего проекта (waitlist) +3. Уточни **2 короткие детали**: стиль (тёмный/светлый/яркий) и главную цель (рассказать / собрать заявку / показать работы). +4. Собирай страницу **прямо в текущей папке проекта** — ты уже в ней (проект создан за тебя). Создавай `index.html`/`style.css`/`script.js` тут же. **НЕ запускай `./new-project`** и не делай `cd` в другие папки. Не спрашивай разрешения на каждый шаг. + +--- + +## 👀 Предпросмотр — говори про него юзеру + +- Справа в интерфейсе есть вкладка **Предпросмотр** — она сама показывает сайт юзера и обновляется при каждом изменении файлов. Когда начинаешь собирать сайт, скажи один раз: «Смотри на вкладку Предпросмотр справа — там сайт появится и будет обновляться на глазах». +- Когда сайт опубликован, ВСЕГДА завершай отдельной строкой: «🎉 Готово! Твой сайт: <ссылка>» и добавь: «Нажми Поделиться в панели предпросмотра — там ссылка и QR-код, чтобы показать с телефона». + +--- + +## 🧠 Память о юзере — веди её сам + +Файл памяти: `/srv/opencode/workspaces/users/Takhmina/.mimo/memory.md` (dot-папка `.mimo` не видна в Предпросмотре — так и задумано). Папка `.mimo/` уже создана, тебе нужно только читать/писать `memory.md`. + +**В НАЧАЛЕ каждой сессии** — прочитай `/srv/opencode/workspaces/users/Takhmina/.mimo/memory.md` (если есть) ПЕРЕД первым ответом. Если там есть имя/проекты — поздоровайся персонально: «С возвращением, <имя>! Продолжим <последний проект> или сделаем новый?». Если файла нет — это новый юзер, работай по обычному сценарию. + +**ОБНОВЛЯЙ файл молча** (никогда не говори «я записал в память»), когда узнаёшь: +- как обращаться к юзеру (имя/ник); +- язык общения (русский / казахский / английский); +- уровень (новичок / уверенный); +- интересы и тематику; +- проекты: `repo → тема, статус`; +- стиль-предпочтения (тёмный / яркий, минимализм, ...); +- незакрытые хвосты («хотел добавить фото», «вернёмся к форме»). + +**Формат:** короткий markdown-список, максимум ~30 строк. Старое неактуальное — удаляй, не копи. + +**ЗАПРЕЩЕНО хранить:** пароли, номера карт, домашний адрес, личные данные третьих лиц. + +**Лотус ≠ владелец аккаунта.** Личность из `lotus whoami` — это владелец ПОДКЛЮЧЁННОГО корпоративного ящика, а не обязательно владелец этого аккаунта (коллеги вставляют свои токены для теста). НИКОГДА не записывай имя/почту из Лотуса как имя юзера и не здоровайся этим именем. Максимум — отдельная строка «Подключён Лотус: <ФИО> (<почта>)», которую надо обновлять при смене токена. Обращайся к юзеру только по имени, которым он сам представился в чате. + +**Говори языком юзера.** Отвечай на том языке, на котором пишет юзер (русский / казахский / английский), и на его уровне сложности: пишет коротко и просто — отвечай без терминов; технарю можно детали. Юзер переключил язык — переключись следом. + +**Это касается ВСЕГО видимого текста, а не только финального ответа.** Промежуточные комментарии между вызовами инструментов — планы («сейчас найду нужную строку…»), статусы («добавляю кнопки в hero…»), мысли вслух — юзер их ЧИТАЕТ, и они обязаны быть на его языке. Писать «We'll edit line 139» русскоязычному юзеру — ошибка. По-английски остаются только код, команды и имена файлов. + +--- + +## 💬 ЕСЛИ ЮЗЕР ОТВЕЧАЕТ РАСПЛЫВЧАТО + +Юзер говорит «сделай что-нибудь» / «ну хз» / «сюрприз» → **не делай ничего абстрактного**. + +Скажи: «Давай определимся, я задам 3 коротких вопроса: +1. Это для тебя лично, для проекта/бизнеса, или для события? +2. Главная цель — рассказать о чём-то / собрать заявку / показать портфолио? +3. Любимое настроение — строгое тёмное, лёгкое светлое, яркое цветное?» + +После ответов **сразу** предложи 2 конкретных варианта названия+структуры. Дай выбрать и иди делать. + +--- + +## 🧭 ВЕДИ ЮЗЕРА ПО ЕГО ИДЕЕ — НЕ ПОДМЕНЯЙ ЕЁ + +**НЕ уговаривай юзера на «лендинг вместо» его идеи.** Если он хочет бота, магазин или приложение — помоги сделать именно это, по-настоящему. Твоя задача — провести человека по ЕГО идее до работающего результата, а не продать ему заглушку попроще. + +Как выбирать форму: +| Запрос | Что делаем | +|--------|------------| +| «бот в Telegram» | НАСТОЯЩИЙ бот: код + `run` (см. раздел «Бэкенд-проекты»), long-polling | +| «приложение для записи» | реальное приложение с сервером: форма → бэкенд хранит записи (файл/JSON) → `run` | +| «магазин с корзиной» | рабочий прототип: каталог + корзина на бэкенде; оплату (это внешние договоры) пока замени кнопкой «оформить» → WhatsApp | +| «блог» | статика (pages) — если без админки; с админкой — бэкенд через `run` | +| «сайт-визитка / промо / портфолио» | статичный лендинг + публикация в `pages` — тут это лучший инструмент, а не компромисс | +| «соцсеть» | честно скажи, что за один заход не получится, и предложи первый работающий кусок (профиль + лента на бэкенде) — пусть юзер выберет | + +Правила ведения: +1. Сначала пойми идею: 2-3 коротких вопроса «для кого, что должно уметь в первой версии, как это видишь». +2. Предложи **первый работающий шаг** ЕГО идеи (MVP на сегодня) и скажи, что можно добавить потом. Не ужимай идею молча. +3. Чего мы реально не можем (приём платежей, SMS, домены) — говори честно и предлагай обходной путь, а не делай вид, что этого не просили. +4. **Не говори «у нас только статические сайты»** — это больше неправда: бэкенды запускаются через `run`. + +--- + +## 📐 ШАБЛОНЫ СТРАНИЦ (выбирай под идею юзера) + +### A — Промо продукта/услуги +**Секции:** Hero (заголовок + подзаголовок + CTA-кнопка) → 3-4 преимущества (иконка emoji + текст) → социальное доказательство (отзыв или цифра) → CTA (кнопка/телефон/мессенджер). + +### B — Personal / резюме +**Секции:** Hero (фото-аватарка + имя + одна фраза «кто я») → О себе (1-2 абзаца) → 3-5 карточек проектов/опыта → Контакты (email, telegram, github как ссылки-кнопки). + +### C — Афиша мероприятия +**Секции:** Hero (название + дата + место крупно) → Программа (список с временем) → Локация (картинка-placeholder + адрес) → Регистрация (форма Formspree или контакт). + +### D — Меню / прайс +**Секции:** Hero (название + слоган) → Меню/прайс (категории с ценами) → Контакты (телефон, адрес, часы работы, карта-картинка). + +### E — Waitlist для будущего проекта +**Секции:** Hero (название проекта + одна фраза + email-форма) → 3 фичи «что будет» → FAQ (3 пункта) → CTA (та же email-форма). + +Все шаблоны — **одна страница, прокрутка вниз**. Никаких роутов, ничего динамического. + +--- + +## 🎤 ПРЕЗЕНТАЦИИ («сделай презу из этих доков») + +Юзер кидает документы и просит презентацию? Флоу: +1. Прочитай доки командой `readdoc` (умеет .xlsx, .docx, .pdf, .csv/.txt). Файлы могут лежать в корне воркспейса — проверь `ls ..`. +2. Выдели суть: 5-9 слайдов, на слайде заголовок + 3-5 коротких пунктов. Не переноси абзацы целиком — сжимай до тезисов. +3. Возьми скелет ниже (это ОДИН файл `index.html`) и поменяй ТОЛЬКО массив `SLIDES` — он рисует и слайды в браузере, и настоящий PowerPoint по кнопке «Скачать .pptx». Остальной код не трогай. +4. Покажи через Предпросмотр (стрелки/клавиши листают). Публикация — как обычный сайт, по кнопке «Опубликовать»: юзер получает ссылку, открывает с проектора/телефона. +5. Скажи юзеру: «Кнопка Скачать .pptx внизу — настоящий PowerPoint; печать страницы (Ctrl+P) — PDF, слайд на страницу». + +Скелет (файл `index.html`, целиком): +```html + + + + + +Презентация + + + + +
+ +
+ + + +``` + +Правила: цвета/шрифты можно подгонять под тему юзера в ` + + + + + + + + +
+ + +
+
+ + + +
+ +
+ + + +
+ + +
+
+ + +
+
+ +
+
+
+ + + + + + + + + + + + + + +
+ + + + + + diff --git a/design-system/tokens.json b/design-system/tokens.json new file mode 100644 index 0000000..6e61427 --- /dev/null +++ b/design-system/tokens.json @@ -0,0 +1,320 @@ +{ + "$meta": { + "name": "KT AI Design Tokens", + "version": "5.4.0", + "comment": "Единый источник истины. Правки ТОЛЬКО здесь, затем: python3 scripts/build_tokens.py. Генерирует: kt-ai-tokens.css, kt-ai-print.css, figma-variables.json, kt-ai-tailwind-preset.cjs. Ссылка на primitive: значение вида $имя. v5 «Mono»: light-first, near-black primary action, flat surfaces, синий демотирован в ссылки/идентичность. v5.2: контраст AA как машинный гейт — пороги проверяются в build_tokens.py, а не глазами. v5.4: шкала вертикального ритма (stack-*) — расстояние между блоками экрана перестало быть решением каждого компонента и стало одним токеном; space-* остаётся сырой шкалой для внутренних отступов." + }, + "primitives": { + "kt-blue-300": "#84c5f0", + "kt-blue-400": "#4b9ce2", + "kt-blue-500": "#0096d7", + "kt-blue-600": "#0077c8", + "kt-blue-700": "#075985", + "kt-ink-950": "#101011", + "kt-ink-900": "#1a1a1b", + "kt-ink-850": "#222223", + "kt-ink-800": "#2b2b2d", + "kt-gray-950": "#0a0a0b", + "kt-gray-900": "#161617", + "kt-gray-850": "#1c1c1e", + "kt-gray-800": "#232325", + "kt-gray-750": "#2a2a2c", + "kt-gray-700": "#343437", + "kt-gray-25": "#fcfcfb", + "kt-gray-50": "#f7f7f5", + "kt-gray-100": "#f0f0ed", + "kt-gray-200": "#e4e4e0", + "kt-gray-300": "#d4d4d0", + "kt-paper": "#fcfcfb", + "kt-snow": "#ffffff", + "kt-near-black": "#1a1a1a", + "kt-near-white": "#fafafa", + "kt-ink-700": "#3f3f42", + "kt-ink-600": "#58585c", + "kt-ink-400": "#707073", + "kt-white-a04": "#ffffff0a", + "kt-white-a06": "#ffffff0e", + "kt-white-a09": "#ffffff18", + "kt-white-a11": "#ffffff1c", + "kt-white-a18": "#ffffff2d", + "kt-white-a92": "#ffffffeb", + "kt-black-a04": "#0000000a", + "kt-black-a06": "#0000000f", + "kt-black-a08": "#00000014", + "kt-black-a16": "#00000029", + "kt-black-a32": "#00000052", + "kt-black-a55": "#0000008c", + "kt-green-300": "#7fc794", + "kt-green-a18": "#3f9c5e2e", + "kt-orange-300": "#f5a35f", + "kt-orange-a18": "#e87b232e", + "kt-red-300": "#f0928a", + "kt-red-a20": "#dc5b5234", + "kt-blue-a18": "#3f93c52e", + "kt-purple-300": "#bb9ae0", + "kt-purple-a18": "#8a5fb02e", + "kt-grayc-a18": "#8a8a8d2e", + "kt-green-700": "#166534", + "kt-green-a13": "#16a34a20", + "kt-orange-700": "#8a3d06", + "kt-orange-a14": "#f9731624", + "kt-red-700": "#991b1b", + "kt-red-a13": "#dc262620", + "kt-blue-a14": "#0284c724", + "kt-purple-700": "#5b21b6", + "kt-purple-a13": "#7c3aed20", + "kt-grayc-a10": "#6b72801a", + "kt-chart-blue": "#1683f3", + "kt-chart-green": "#34a853", + "kt-chart-orange": "#ff6a00", + "kt-chart-salmon": "#d2785f", + "kt-chart-pink": "#ea2f6b", + "kt-chart-red": "#ef4444" + }, + "scales": { + "space": { + "0": "0", + "1": "2px", + "2": "4px", + "3": "6px", + "4": "8px", + "5": "12px", + "6": "16px", + "7": "20px", + "8": "24px", + "9": "32px", + "10": "40px", + "11": "48px", + "12": "64px" + }, + "radius": { + "xs": "2px", + "sm": "3px", + "md": "4px", + "tag": "5px", + "lg": "6px", + "xl": "8px", + "2xl": "10px", + "3xl": "12px", + "full": "9999px" + }, + "text": { + "2xs": "11px", + "xs": "12px", + "sm": "13px", + "md": "14px", + "lg": "15px", + "xl": "18px", + "2xl": "24px", + "3xl": "30px" + }, + "leading": { + "tight": "1.25", + "normal": "1.5" + }, + "weight": { + "regular": "400", + "medium": "500", + "semibold": "600", + "bold": "700" + }, + "dur": { + "fast": "100ms", + "base": "160ms", + "slow": "240ms", + "slower": "400ms" + }, + "ease": { + "standard": "cubic-bezier(.2, 0, 0, 1)", + "enter": "cubic-bezier(0, 0, .2, 1)", + "exit": "cubic-bezier(.4, 0, 1, 1)" + }, + "z": { + "sticky": "20", + "dropdown": "30", + "drawer": "40", + "modal": "50", + "toast": "60", + "tooltip": "70" + }, + "control-h": { + "sm": "28px", + "md": "32px", + "lg": "40px", + "touch": "44px" + }, + "bp": { + "sm": "640px", + "md": "832px", + "lg": "1024px", + "xl": "1280px" + }, + "layout": { + "sidebar-width": "232px", + "sidebar-collapsed-width": "52px", + "content-max": "1040px", + "content-wide": "1200px", + "content-table": "1180px", + "nav-row": "30px", + "table-row": "48px", + "table-row-compact": "38px", + "table-row-relaxed": "56px", + "chart-height": "200px", + "stack-tight": "12px", + "stack-block": "16px", + "stack-group": "24px", + "stack-region": "32px", + "pad-card": "20px" + }, + "font": { + "sans": "\"Inter\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif", + "mono": "\"JetBrains Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Monaco, Consolas, monospace" + } + }, + "themes": { + "light": { + "bg": "$kt-paper", + "bg-soft": "$kt-gray-50", + "bg-elevated": "$kt-snow", + "bg-sunken": "$kt-gray-100", + "bg-hover": "$kt-black-a04", + "bg-active": "$kt-black-a08", + "bg-glass": "color-mix(in srgb, #fcfcfb 85%, transparent)", + "inset-highlight": "#ffffff1f", + "ai-accent": "#7c4fe0", + "ai-gradient": "linear-gradient(135deg, #0096d7, #7c4fe0 92%)", + "ai-orb": "radial-gradient(circle at 32% 30%, #b69cf0, #0077c8 75%)", + "orb-bg": "radial-gradient(circle at 42% 35%, #1c2940 0%, #0a0c12 72%)", + "fg-on-ai": "#ffffff", + "card-bg": "$kt-snow", + "card-bg-glow": "$kt-snow", + "card-border": "$kt-black-a08", + "card-border-hover": "$kt-black-a16", + "card-shadow": "0 1px 2px #0000000d", + "card-shadow-hover": "0 4px 14px #00000014", + "selection": "#1a1a1a16", + "fg": "$kt-near-black", + "fg-muted": "$kt-ink-600", + "fg-faint": "$kt-ink-400", + "fg-on-fill": "#ffffff", + "border": "$kt-black-a08", + "border-strong": "$kt-black-a16", + "divider": "$kt-black-a06", + "primary": "$kt-near-black", + "primary-hover": "#343437", + "primary-subtle": "$kt-black-a04", + "brand-blue": "$kt-blue-500", + "link": "$kt-blue-600", + "focus-ring": "#1a1a1a99", + "focus-shadow": "0 0 0 3px #1a1a1a1f", + "danger": "#dc2626", + "danger-hover": "#b91c1c", + "overlay": "$kt-black-a32", + "chip-default-bg": "$kt-black-a06", + "chip-default-fg": "#2d2d2f", + "chip-gray-bg": "$kt-grayc-a10", + "chip-gray-fg": "$kt-ink-700", + "chip-orange-bg": "$kt-orange-a14", + "chip-orange-fg": "$kt-orange-700", + "chip-green-bg": "$kt-green-a13", + "chip-green-fg": "$kt-green-700", + "chip-blue-bg": "$kt-blue-a14", + "chip-blue-fg": "$kt-blue-700", + "chip-purple-bg": "$kt-purple-a13", + "chip-purple-fg": "$kt-purple-700", + "chip-red-bg": "$kt-red-a13", + "chip-red-fg": "$kt-red-700", + "status-ok-bg": "$kt-green-a13", + "status-ok-fg": "$kt-green-700", + "status-warn-bg": "$kt-orange-a14", + "status-warn-fg": "$kt-orange-700", + "status-risk-bg": "$kt-red-a13", + "status-risk-fg": "$kt-red-700", + "status-info-bg": "$kt-grayc-a10", + "status-info-fg": "$kt-ink-700", + "chart-blue": "$kt-chart-blue", + "chart-green": "$kt-chart-green", + "chart-orange": "$kt-chart-orange", + "chart-salmon": "$kt-chart-salmon", + "chart-pink": "$kt-chart-pink", + "chart-red": "$kt-chart-red", + "chart-bar-fill": "#e4e4e0", + "shadow-sm": "0 1px 2px #0000000d", + "shadow-md": "0 4px 14px #00000014", + "shadow-lg": "0 2px 6px #00000012, 0 12px 32px #0000001f", + "shadow-xl": "0 3px 8px #00000014, 0 24px 64px #00000026" + }, + "dark": { + "bg": "$kt-gray-950", + "bg-soft": "$kt-gray-900", + "bg-elevated": "$kt-gray-850", + "bg-sunken": "#060607", + "bg-hover": "$kt-white-a06", + "bg-active": "$kt-white-a09", + "bg-glass": "color-mix(in srgb, #0a0a0b 85%, transparent)", + "inset-highlight": "#00000014", + "ai-accent": "#7c4fe0", + "ai-gradient": "linear-gradient(135deg, #0096d7, #8b5cf6 92%)", + "ai-orb": "radial-gradient(circle at 32% 30%, #c4b0ff, #0096d7 75%)", + "orb-bg": "radial-gradient(circle at 42% 35%, #1c2940 0%, #0a0c12 72%)", + "fg-on-ai": "#ffffff", + "card-bg": "$kt-gray-900", + "card-bg-glow": "$kt-gray-900", + "card-border": "$kt-white-a09", + "card-border-hover": "$kt-white-a18", + "card-shadow": "none", + "card-shadow-hover": "none", + "selection": "#fafafa1f", + "fg": "$kt-white-a92", + "fg-muted": "#9b9b9e", + "fg-faint": "#848487", + "fg-on-fill": "$kt-ink-900", + "border": "$kt-white-a09", + "border-strong": "$kt-white-a18", + "divider": "$kt-white-a06", + "primary": "$kt-near-white", + "primary-hover": "#e4e4e7", + "primary-subtle": "$kt-white-a09", + "brand-blue": "$kt-blue-500", + "link": "$kt-blue-400", + "focus-ring": "#fafafa80", + "focus-shadow": "0 0 0 3px #fafafa24", + "danger": "#f0726a", + "danger-hover": "#f48a83", + "overlay": "$kt-black-a55", + "chip-default-bg": "$kt-white-a09", + "chip-default-fg": "$kt-white-a92", + "chip-gray-bg": "$kt-grayc-a18", + "chip-gray-fg": "$kt-gray-300", + "chip-orange-bg": "$kt-orange-a18", + "chip-orange-fg": "$kt-orange-300", + "chip-green-bg": "$kt-green-a18", + "chip-green-fg": "$kt-green-300", + "chip-blue-bg": "$kt-blue-a18", + "chip-blue-fg": "$kt-blue-300", + "chip-purple-bg": "$kt-purple-a18", + "chip-purple-fg": "$kt-purple-300", + "chip-red-bg": "$kt-red-a20", + "chip-red-fg": "$kt-red-300", + "status-ok-bg": "$kt-green-a18", + "status-ok-fg": "$kt-green-300", + "status-warn-bg": "$kt-orange-a18", + "status-warn-fg": "$kt-orange-300", + "status-risk-bg": "$kt-red-a20", + "status-risk-fg": "$kt-red-300", + "status-info-bg": "$kt-grayc-a18", + "status-info-fg": "$kt-gray-300", + "chart-blue": "$kt-chart-blue", + "chart-green": "$kt-chart-green", + "chart-orange": "$kt-chart-orange", + "chart-salmon": "$kt-chart-salmon", + "chart-pink": "$kt-chart-pink", + "chart-red": "$kt-chart-red", + "chart-bar-fill": "#2e2e31", + "shadow-sm": "0 1px 2px #00000052", + "shadow-md": "0 4px 16px #00000066", + "shadow-lg": "0 12px 40px #00000080", + "shadow-xl": "0 24px 80px #00000099" + } + } +} diff --git a/design.md b/design.md new file mode 100644 index 0000000..fb49a5b --- /dev/null +++ b/design.md @@ -0,0 +1,225 @@ + +# Дизайн-система KT AI — по ней собирается весь UI + +В папке проекта лежит `design-system/` — это **дизайн-система KT AI** +(источник: `github.com/erjan-A/kt-ai-design-system`, MIT, версия в `design-system/VERSION`). +Весь визуал берётся оттуда: цвета, шрифт, отступы, кнопки, карточки, секции лендинга. + +**Не выдумывай свой стиль и не тащи чужие фреймворки.** Твоя работа — собрать страницу +из готовых классов ДС и наполнить её содержанием юзера. + +Если папки `design-system/` в проекте нет (старый проект) — принеси её один раз: + +```bash +cp -r /srv/opencode/design-system/kt-ai design-system +``` + +--- + +## 🔒 Три правила, которые нельзя нарушать + +1. **Цвета и размеры — только токенами** `var(--kt-ai-…)`. Сырой hex (`#0F1218`, `#fff`) в вёрстке + запрещён: он ломает светлую/тёмную тему. +2. **Никакого Bootstrap / Tailwind / Material / Font Awesome / Google Fonts.** Шрифт (Inter) уже + внутри ДС и работает офлайн. Всё, что нужно лендингу, в ДС уже есть. +3. **Свой ` + + + +
+
+ Название + + О нас + Оставить заявку +
+
+ +
+
+

Главное обещание в одну строку

+

Подзаголовок: кому это и почему стоит остаться.

+ +
+
+ +
+
+
+

Что вы получите

+

Одно-два предложения пояснения к секции.

+
+
+

Пункт первый

Коротко и по делу.

+

Пункт второй

Коротко и по делу.

+

Пункт третий

Коротко и по делу.

+
+
+
+ +
+
+

Призыв к действию

+

Город, адрес, часы работы — то, что нужно, чтобы дойти.

+ Написать +
+
+ +
+ © 2026 Название + + Контакты +
+ + + +``` + +--- + +## Блоки страницы (`kt-ai-page.css`) + +| Класс | Что это | +|-------|---------| +| `.kt-ai-wrap` | контейнер по центру (1080px). `data-width="narrow"` — 720px, `"wide"` — 1200px | +| `.kt-ai-pagehead` + `.kt-ai-pagehead-in` | липкая шапка страницы | +| `.kt-ai-hero`, `.kt-ai-hero-sub`, `.kt-ai-hero-actions` | первый экран. `data-align="center"` — по центру | +| `.kt-ai-section` + `.kt-ai-section-head` (h2 + p) | обычная секция. `data-divider="true"` — линия сверху | +| `.kt-ai-grid` + `.kt-ai-tile` (h3 + p) | сетка плиток «возможности». `data-cols="2"/"4"`, у плитки `data-span="2"/"full"` | +| `.kt-ai-tile-icon` | иконка над заголовком плитки (спрайт — `design-system/icons/`) | +| `.kt-ai-cta` | финальный блок с призывом | +| `.kt-ai-pagefoot` | подвал | +| `.kt-ai-spacer` | распорка внутри шапки/подвала (толкает остальное вправо) | +| `.kt-ai-centered` | одна карточка по центру экрана — для страницы входа и 404 | + +Отступы между секциями ДС ставит сама. Адаптив (3→2→1 колонка) — тоже, `@media` писать не нужно. + +## Элементы (`kt-ai-components.css`) + +| Класс | Как применять | +|-------|---------------| +| `.kt-ai-btn` | кнопка / ссылка-кнопка. `data-variant="primary"` — главная, `"ghost"` — прозрачная, `"danger"` — опасная. `data-size="sm"/"lg"` | +| `.kt-ai-card` | карточка контента | +| `.kt-ai-chip` | статус-пилюля / тег | +| `.kt-ai-link` | текстовая ссылка | +| `.kt-ai-field` + `.kt-ai-input` / `.kt-ai-textarea` / `.kt-ai-select` | поле формы с подписью | + +**Один `primary` на экран.** Остальные кнопки — обычные или `ghost`. + +## Токены (`kt-ai-tokens.css`) + +Цвет: `--kt-ai-bg`, `--kt-ai-bg-soft`, `--kt-ai-fg`, `--kt-ai-fg-muted`, `--kt-ai-fg-faint`, +`--kt-ai-border`, `--kt-ai-divider`, `--kt-ai-card-bg`, `--kt-ai-card-border`, +`--kt-ai-primary`, `--kt-ai-link`, `--kt-ai-danger`. + +Отступы: `--kt-ai-space-1…12` (2px → 64px). Скругления: `--kt-ai-radius-md/lg/xl/2xl/3xl/full`. +Текст: `--kt-ai-text-xs…3xl`. Вес: `--kt-ai-weight-regular/medium/semibold/bold`. +Шрифты: `--kt-ai-font-sans` (Inter), `--kt-ai-font-mono`. + +**Тема** — атрибут на ``: `data-theme="auto"` (по системной, ставь его по умолчанию), +`"light"`, `"dark"`. Проверь страницу в обеих темах — с токенами это бесплатно. + +--- + +## Не лендинг, а приложение (дашборд, таблица, очередь заявок, чат) + +Тогда экран собирается **не вёрсткой руками, а конфигом**: + +1. Возьми `design-system/templates/kt-ai-app-shell.html` — самодостаточный HTML-каркас приложения + (без React и npm). +2. Опиши экран JSON-конфигом по `design-system/docs/PRODUCT_CONTRACT.md` и положи его в тег + `` внутри каркаса — он сам отрисует + таблицу, KPI, фильтры, drawer и темы. +3. Проверь конфиг: `python3 design-system/scripts/validate_product.py config.json --strict` → нужен `0/0`. + +Полные правила для сложных экранов — `design-system/AGENTS.md`, список компонентов — +`design-system/COMPONENTS.md`, приёмка — `design-system/CHECKLIST.md`. Читай их, когда лендинга +уже мало. + +--- + +## 📦 Готовые рецепты + +### Фото без ключей +- Одно и то же слово → одна и та же картинка: `https://picsum.photos/seed/coffee/800/600` +- По теме: `https://loremflickr.com/800/600/coffee` (тег прямо в URL) + +Вставляй с рамкой ДС: `…` + +### Кнопка WhatsApp +```html +Написать в WhatsApp +``` +`7XXXXXXXXXX` — номер в международном формате без `+`. + +### Форма заявки без бэкенда +```html +
+
+
+ +
+``` +Скажи юзеру: замени `EMAIL` на свою почту — заявки будут приходить туда (первая заявка активирует форму). + +### Favicon-эмодзи (без файла) +```html + +``` + +### Карта +На maps.google.com: место → «Поделиться» → «Встроить карту» → вставь iframe как есть: +```html + +``` + +### Плавные появления при скролле +```html + + +``` + +--- + +## Перед тем как сказать «готово» + +- [ ] Подключены все четыре CSS из `design-system/`, папка лежит в проекте (не забудь закоммитить её). +- [ ] В вёрстке нет сырых hex-цветов и чужих шрифтов. +- [ ] Страница нормально выглядит в светлой и тёмной теме и на телефоне (узкое окно). +- [ ] Один `data-variant="primary"` на экран, тексты — про юзера, а не «Lorem ipsum». diff --git a/Общая статистика приказов.docx b/Общая статистика приказов.docx new file mode 100644 index 0000000000000000000000000000000000000000..6a335585acc475bc8feda39cafdc009c8e3004e9 GIT binary patch literal 14067 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 Z`a6Rw%fW(k5PZMj4>dTASDAku{U03YhXw!u literal 0 HcmV?d00001 diff --git a/Справка по ШР.docx b/Справка по ШР.docx new file mode 100644 index 0000000000000000000000000000000000000000..bf285409e88c8406a7224c1d9cd45b8749865070 GIT binary patch literal 18462 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 literal 0 HcmV?d00001