design: redesign ai-dev with expressive type and project grid

This commit is contained in:
Nikolay Mamadaev committed 2026-10-07 19:57:40 +02:00
1 parent be7da3d0ec
commit f4404edaca
6 files changed
+267 -318

No files matched your search

+7
View File
@@ -186,6 +186,13 @@
| `.skill-task-card` | Карточка навыка (задача + решения) |
| `.contact-card` | Контактная карточка (ссылка или действие) |
### Сетка проектов
| Класс | Назначение |
|---|---|
| `.project-grid` | Сетка карточек проекта: 1 колонка на мобильном, 2 от `680px`, 3 от `1120px`; адаптивные интервалы между карточками |
| `.project-grid > li` | Элемент списка без собственного оформления; внутри используется компонент карточки, например `.mp-card` |
### Блоки лонгрида
| Класс | Назначение |
+108 -159
View File
@@ -219,63 +219,7 @@
<link rel="stylesheet" href="/vendor/plyr/plyr.css" />
<style>
/* Локальные правила страницы. Всё остальное — компоненты дизайн-системы:
.contact-grid (сетка), .mp-card (карточка), .filter-chips (переключатель),
.mp-card__tags/.mp-card__tag (метаданные), .case-links-grid (ссылки),
.hero__tags/.hero__tag (список навыков). */
.ai-page {
max-width: 52rem;
margin: 0 auto;
}
.ai-page h2 {
margin: 2.25rem 0 0.85rem;
font-size: clamp(1.15rem, 2vw, 1.35rem);
font-weight: 700;
color: var(--text);
}
.ai-page .section__prose {
margin-bottom: 0.75rem;
color: var(--text);
}
.ai-page a:not(.btn) {
color: var(--accent);
}
/* Сетка проектов: контракт .contact-grid, но 2 колонки и шире минимальная
колонка — карточкам нужен воздух. Сама сетка и её адаптив из системы. */
.ai-projects {
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
margin-bottom: 0;
}
/* Обложка- заглушка внутри карточки. */
.ai-projects__media {
border-radius: var(--radius-sm);
overflow: hidden;
border: 1px solid var(--line);
margin-bottom: 0.7rem;
background: var(--bg-elevated);
}
.ai-projects__media img {
display: block;
width: 100%;
height: auto;
aspect-ratio: 16 / 10;
object-fit: cover;
}
/* Стек проекта — моноширинный, как .filter-chip__count в системе. */
.ai-projects__stack {
margin: 0;
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.76rem;
line-height: 1.4;
color: var(--text-muted);
}
/* У mp-card--compact теги уходят вниз карточки (order 2) — оставляем
стек под заголовком, поэтому поднимаем его наравне с описанием. */
.mp-card--compact .ai-projects__stack {
order: 1;
}
</style>
</head>
<body>
<a class="skip-link" href="#case-main">К содержимому кейса</a>
@@ -374,47 +318,52 @@
</div>
<main id="case-main" class="section section--tight">
<div class="section__inner ai-page">
<p class="hero__eyebrow">AI-разработка · идея, код, деплой</p>
<h1 class="case-page__h1">Делаю целые продукты</h1>
<p class="case-page__sub">Telegram-боты, MCP-серверы, плагины и утилиты. Схема, код, деплой</p>
<main id="case-main">
<section class="section">
<div class="section__inner hero__grid">
<div>
<p class="hero__eyebrow">AI-разработка · идея, код, деплой</p>
<h1 class="hero__title">От идеи<br />до продукта.</h1>
</div>
<div>
<p class="case-page__sub">Проектирую, собираю и запускаю цифровые продукты.</p>
<p class="section__prose">
Больше десяти лет я проектировал интерфейсы. Теперь отвечаю за весь путь:
продуктовую логику, код, базу и инфраструктуру. Код часто пишет ИИ-агент.
Архитектуру и результат проверяю я.
</p>
<ul class="hero__tags" aria-label="Что я делаю">
<li class="hero__tag">Продуктовая логика и API</li>
<li class="hero__tag">Telegram-боты</li>
<li class="hero__tag">MCP-серверы</li>
<li class="hero__tag">Плагины и автоматизация</li>
</ul>
</div>
</div>
</section>
<p class="section__prose">
Больше десяти лет я проектировал интерфейсы. Теперь довожу идеи до работающего
кода: бэкенд, база данных, деплой. Инфраструктура тоже моя часть работы.
В большинстве проектов код пишет ИИ-агент. Я задаю архитектуру, проверяю
результат и отвечаю за то, что продукт вообще работает.
</p>
<h2 id="roles">Что я делаю</h2>
<ul class="section__prose hero__tags" aria-label="Что я делаю">
<li class="hero__tag">Продуктовая логика и API</li>
<li class="hero__tag">Telegram-боты на aiogram 3</li>
<li class="hero__tag">MCP-серверы</li>
<li class="hero__tag">Плагины Figma и Obsidian</li>
<li class="hero__tag">Автоматизация и агентные пайплайны</li>
</ul>
<h2 id="projects">Проекты</h2>
<section class="section section--tight">
<div class="section__inner">
<p class="hero__eyebrow">01 / Сделано и запущено</p>
<h2 id="projects" class="hero__title">Проекты</h2>
<ul class="filter-chips" data-filter-chips data-filter-scope="#ai-projects" aria-label="Фильтр проектов по типу">
<li><button type="button" class="filter-chip" data-filter-value="all" aria-pressed="true">Все</button></li>
<li><button type="button" class="filter-chip" data-filter-value="bots" aria-pressed="false">Бots</button></li>
<li><button type="button" class="filter-chip" data-filter-value="bots" aria-pressed="false">Боты</button></li>
<li><button type="button" class="filter-chip" data-filter-value="mcp" aria-pressed="false">MCP</button></li>
<li><button type="button" class="filter-chip" data-filter-value="plugins" aria-pressed="false">Плагины</button></li>
<li><button type="button" class="filter-chip" data-filter-value="web" aria-pressed="false">Веб</button></li>
<li><button type="button" class="filter-chip" data-filter-value="infra" aria-pressed="false">Инфраструктура</button></li>
</ul>
<ol class="contact-grid ai-projects" id="ai-projects">
<ol class="project-grid" id="ai-projects">
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Personal Assistant</h3>
<p class="ai-projects__stack">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Личный ассистент в Telegram: напоминания, планирование дел и работа с LLM в одном диалоге.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -423,13 +372,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Image Gen Bot</h3>
<p class="ai-projects__stack">Python · aiogram 3 · Redis · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · Redis · PostgreSQL</p>
<p class="mp-card__desc">Бот для генерации изображений: баланс, реферальная программа, админ-логи в отдельные топики супергруппы.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -438,13 +387,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="mcp bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">MCP Image Server</h3>
<p class="ai-projects__stack">JavaScript · MCP · OpenRouter</p>
<p class="mp-card__desc">JavaScript · MCP · OpenRouter</p>
<p class="mp-card__desc">Собственный MCP-сервер для генерации картинок. Подключается к агентам как инструмент.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">MCP</li>
@@ -453,13 +402,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">AI assistant</h3>
<p class="ai-projects__stack">TypeScript · Figma Plugin API</p>
<p class="mp-card__desc">TypeScript · Figma Plugin API</p>
<p class="mp-card__desc">Плагин для Figma, который исполняет скрипты прямо в редакторе. ИИ-ассистент для любого тарифа.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -468,13 +417,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Card Columns</h3>
<p class="ai-projects__stack">TypeScript · Obsidian API</p>
<p class="mp-card__desc">TypeScript · Obsidian API</p>
<p class="mp-card__desc">Вью для заметок в стиле канбан-доски: группировка по колонкам с фильтрами по тегам.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -483,13 +432,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Hermes Desktop Plugins</h3>
<p class="ai-projects__stack">JavaScript · TypeScript · Electron</p>
<p class="mp-card__desc">JavaScript · TypeScript · Electron</p>
<p class="mp-card__desc">Плагины для рабочего стола Hermes: чип баланса API, звук завершения задачи и контроль трат.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -498,13 +447,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Social Post Writer</h3>
<p class="ai-projects__stack">Python · aiogram 3 · SQLite</p>
<p class="mp-card__desc">Python · aiogram 3 · SQLite</p>
<p class="mp-card__desc">Бот, который превращает тезисы в готовый пост под нужную площадку и тон, учитывая голос автора.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -513,13 +462,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="web">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">AI Design Assistant</h3>
<p class="ai-projects__stack">HTML · CSS · GLSL · WebGL</p>
<p class="mp-card__desc">HTML · CSS · GLSL · WebGL</p>
<p class="mp-card__desc">Этот сайт целиком: структура, дизайн-система, шейдеры, CI/CD. Весь цикл создания вместе с ИИ-агентом.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Веб</li>
@@ -528,13 +477,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Секреты и менеджер паролей</h3>
<p class="ai-projects__stack">Vaultwarden · Docker · Coolify</p>
<p class="mp-card__desc">Vaultwarden · Docker · Coolify</p>
<p class="mp-card__desc">Своё хранилище паролей с веб-доступом. Отдельный сервис, доступ к API закрыт ключом.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -543,13 +492,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Прокси к хранилищу</h3>
<p class="ai-projects__stack">Go · Vaultwarden API · all-cipher</p>
<p class="mp-card__desc">Go · Vaultwarden API · all-cipher</p>
<p class="mp-card__desc">Форк Vaultwarden API с поддержкой всех типов шифрования. Нужен, чтобы читать секреты из агентов и скиллов.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -558,13 +507,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Уведомления</h3>
<p class="ai-projects__stack">Go · coturn · Matrix · ntfy</p>
<p class="mp-card__desc">Go · coturn · Matrix · ntfy</p>
<p class="mp-card__desc">Своя доставка уведомлений: ntfy для себя, Matrix-комната для агентов и WebRTC для звонков.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -573,13 +522,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="../assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Бот для новостей</h3>
<p class="ai-projects__stack">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Telegram-бот, который присылает дайджест новостей по подпискам.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -589,49 +538,48 @@
</article>
</li>
</ol>
</div>
</section>
<h2 id="stack">Стек</h2>
<ul class="section__prose hero__tags" aria-label="Стек">
<li class="hero__tag">Python</li>
<li class="hero__tag">TypeScript</li>
<li class="hero__tag">JavaScript</li>
<li class="hero__tag">Go</li>
<li class="hero__tag">HTML/CSS</li>
<li class="hero__tag">GLSL</li>
<li class="hero__tag">aiogram 3</li>
<li class="hero__tag">FastAPI</li>
<li class="hero__tag">PostgreSQL</li>
<li class="hero__tag">Redis</li>
<li class="hero__tag">SQLite</li>
<li class="hero__tag">Docker</li>
<li class="hero__tag">Coolify</li>
<li class="hero__tag">Gitea</li>
<li class="hero__tag">MCP</li>
</ul>
<section class="section">
<div class="section__inner hero__grid">
<div>
<p class="hero__eyebrow">02 / Среда, которую я собрал</p>
<h2 id="infra" class="hero__title">Своя<br />инфраструктура.</h2>
</div>
<div>
<p class="case-page__sub">Два VPS. Сервисы под моим контролем.</p>
<p class="section__prose">
Разворачиваю проекты в Docker через Coolify. Traefik ведёт домены и TLS;
Gitea хранит код и запускает деплой. WireGuard соединяет серверы в закрытую
сеть. PostgreSQL и Redis обслуживают ботов и приложения.
</p>
<ul class="hero__tags" aria-label="Инструменты инфраструктуры">
<li class="hero__tag">Coolify</li>
<li class="hero__tag">Traefik</li>
<li class="hero__tag">Gitea</li>
<li class="hero__tag">WireGuard</li>
<li class="hero__tag">Docker</li>
<li class="hero__tag">PostgreSQL</li>
<li class="hero__tag">Redis</li>
<li class="hero__tag">XRay</li>
<li class="hero__tag">Python · aiogram</li>
<li class="hero__tag">TypeScript · JavaScript</li>
<li class="hero__tag">Go</li>
<li class="hero__tag">GLSL · WebGL</li>
</ul>
<p class="section__prose">
У сервисов отдельные схемы в общей базе. Между серверами ходит трафик
через WireGuard, наружу открываются только нужные точки входа.
</p>
</div>
</div>
</section>
<h2 id="infra">Инфраструктура</h2>
<p class="section__prose">
Всё, что я делаю, живёт на двух VPS и не зависит от внешних PaaS. Деплой
автоматический: пуш в ветку поднимает контейнер через Coolify, дальше
реверс-прокси и TLS берёт на себя Traefik.
</p>
<ul class="mp-card__tags" aria-label="Состав инфраструктуры">
<li class="mp-card__tag">Coolify · self-hosted панель деплоя</li>
<li class="mp-card__tag">Traefik · прокси и сертификаты</li>
<li class="mp-card__tag">Gitea · свой git и CI/CD</li>
<li class="mp-card__tag">WireGuard · меш из нескольких серверов</li>
<li class="mp-card__tag">XRay · VPN для доступа к API</li>
<li class="mp-card__tag">Docker · изоляция сервисов</li>
<li class="mp-card__tag">PostgreSQL · общая база по схемам</li>
<li class="mp-card__tag">Redis · очереди и состояние FSM</li>
</ul>
<p class="section__prose">
База одна на несколько проектов: каждый бот берёт свою схему, поэтому
сервисы не мешают друг другу, но состояние видно целиком. Доступ между
серверами закрыт WireGuard-туннелями, публичные адреса выдаёт Traefik.
</p>
<h2 id="links">Где я это выкладываю</h2>
<section class="section section--tight">
<div class="section__inner">
<p class="hero__eyebrow">03 / Код и проекты</p>
<h2 id="links" class="case-page__h1">Смотреть дальше</h2>
<ul class="case-links-grid">
<li>
<a class="case-link-card" href="https://github.com/mamadaevv" target="_blank" rel="noopener noreferrer">
@@ -666,7 +614,8 @@
</a>
</li>
</ul>
</div>
</div>
</section>
</main>
</body>
</html>
+9
View File
@@ -1053,6 +1053,15 @@
</ul>
<p class="sg-label">.filter-chips / .filter-chip[aria-pressed] + [data-filter-item][data-filter-tag]</p>
<h3>Сетка проектов</h3>
<p class="section__prose">Адаптивная сетка карточек: одна колонка на узком экране, две от 680 px и три от 1120 px. Внутри списка используйте существующий компонент карточки.</p>
<ul class="project-grid" aria-label="Демо сетки проектов">
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="../../assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект A</h3><p class="mp-card__desc">Карточка в адаптивной сетке.</p></div></article></li>
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="../../assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект B</h3><p class="mp-card__desc">Сетка подстраивается под экран.</p></div></article></li>
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="../../assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект C</h3><p class="mp-card__desc">Карточки сохраняют заданный ритм.</p></div></article></li>
</ul>
<p class="sg-label">.project-grid + .mp-card--stack</p>
<h3>Карточка портфолио</h3>
<div class="sg-portfolio-wrap">
<a class="mp-card mp-card--link mp-card--stack-bottom" href="#components" tabindex="-1" aria-hidden="true">
+26
View File
@@ -4709,6 +4709,32 @@ html[data-radius="strong"] .skill-task-card__solution {
margin-top: 0.4rem;
}
.project-grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: clamp(1.25rem, 2.5vw, 2.25rem);
margin: 0;
padding: 0;
list-style: none;
}
@media (min-width: 680px) {
.project-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1120px) {
.project-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: clamp(1.5rem, 3vw, 3rem);
}
}
.project-grid > li {
min-width: 0;
}
.contact-grid {
list-style: none;
margin: 0;
+108 -159
View File
@@ -10,108 +10,57 @@
<title>AI-разработка · Николай Мамадаев</title>
{{> head-links}}
{{> head-plyr}}
<style>
/* Локальные правила страницы. Всё остальное — компоненты дизайн-системы:
.contact-grid (сетка), .mp-card (карточка), .filter-chips (переключатель),
.mp-card__tags/.mp-card__tag (метаданные), .case-links-grid (ссылки),
.hero__tags/.hero__tag (список навыков). */
.ai-page {
max-width: 52rem;
margin: 0 auto;
}
.ai-page h2 {
margin: 2.25rem 0 0.85rem;
font-size: clamp(1.15rem, 2vw, 1.35rem);
font-weight: 700;
color: var(--text);
}
.ai-page .section__prose {
margin-bottom: 0.75rem;
color: var(--text);
}
.ai-page a:not(.btn) {
color: var(--accent);
}
/* Сетка проектов: контракт .contact-grid, но 2 колонки и шире минимальная
колонка — карточкам нужен воздух. Сама сетка и её адаптив из системы. */
.ai-projects {
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
margin-bottom: 0;
}
/* Обложка- заглушка внутри карточки. */
.ai-projects__media {
border-radius: var(--radius-sm);
overflow: hidden;
border: 1px solid var(--line);
margin-bottom: 0.7rem;
background: var(--bg-elevated);
}
.ai-projects__media img {
display: block;
width: 100%;
height: auto;
aspect-ratio: 16 / 10;
object-fit: cover;
}
/* Стек проекта — моноширинный, как .filter-chip__count в системе. */
.ai-projects__stack {
margin: 0;
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.76rem;
line-height: 1.4;
color: var(--text-muted);
}
/* У mp-card--compact теги уходят вниз карточки (order 2) — оставляем
стек под заголовком, поэтому поднимаем его наравне с описанием. */
.mp-card--compact .ai-projects__stack {
order: 1;
}
</style>
</head>
<body>
{{> header-case}}
{{> fab-stack}}
<main id="case-main" class="section section--tight">
<div class="section__inner ai-page">
<p class="hero__eyebrow">AI-разработка · идея, код, деплой</p>
<h1 class="case-page__h1">Делаю целые продукты</h1>
<p class="case-page__sub">Telegram-боты, MCP-серверы, плагины и утилиты. Схема, код, деплой</p>
<main id="case-main">
<section class="section">
<div class="section__inner hero__grid">
<div>
<p class="hero__eyebrow">AI-разработка · идея, код, деплой</p>
<h1 class="hero__title">От идеи<br />до продукта.</h1>
</div>
<div>
<p class="case-page__sub">Проектирую, собираю и запускаю цифровые продукты.</p>
<p class="section__prose">
Больше десяти лет я проектировал интерфейсы. Теперь отвечаю за весь путь:
продуктовую логику, код, базу и инфраструктуру. Код часто пишет ИИ-агент.
Архитектуру и результат проверяю я.
</p>
<ul class="hero__tags" aria-label="Что я делаю">
<li class="hero__tag">Продуктовая логика и API</li>
<li class="hero__tag">Telegram-боты</li>
<li class="hero__tag">MCP-серверы</li>
<li class="hero__tag">Плагины и автоматизация</li>
</ul>
</div>
</div>
</section>
<p class="section__prose">
Больше десяти лет я проектировал интерфейсы. Теперь довожу идеи до работающего
кода: бэкенд, база данных, деплой. Инфраструктура тоже моя часть работы.
В большинстве проектов код пишет ИИ-агент. Я задаю архитектуру, проверяю
результат и отвечаю за то, что продукт вообще работает.
</p>
<h2 id="roles">Что я делаю</h2>
<ul class="section__prose hero__tags" aria-label="Что я делаю">
<li class="hero__tag">Продуктовая логика и API</li>
<li class="hero__tag">Telegram-боты на aiogram 3</li>
<li class="hero__tag">MCP-серверы</li>
<li class="hero__tag">Плагины Figma и Obsidian</li>
<li class="hero__tag">Автоматизация и агентные пайплайны</li>
</ul>
<h2 id="projects">Проекты</h2>
<section class="section section--tight">
<div class="section__inner">
<p class="hero__eyebrow">01 / Сделано и запущено</p>
<h2 id="projects" class="hero__title">Проекты</h2>
<ul class="filter-chips" data-filter-chips data-filter-scope="#ai-projects" aria-label="Фильтр проектов по типу">
<li><button type="button" class="filter-chip" data-filter-value="all" aria-pressed="true">Все</button></li>
<li><button type="button" class="filter-chip" data-filter-value="bots" aria-pressed="false">Бots</button></li>
<li><button type="button" class="filter-chip" data-filter-value="bots" aria-pressed="false">Боты</button></li>
<li><button type="button" class="filter-chip" data-filter-value="mcp" aria-pressed="false">MCP</button></li>
<li><button type="button" class="filter-chip" data-filter-value="plugins" aria-pressed="false">Плагины</button></li>
<li><button type="button" class="filter-chip" data-filter-value="web" aria-pressed="false">Веб</button></li>
<li><button type="button" class="filter-chip" data-filter-value="infra" aria-pressed="false">Инфраструктура</button></li>
</ul>
<ol class="contact-grid ai-projects" id="ai-projects">
<ol class="project-grid" id="ai-projects">
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Personal Assistant</h3>
<p class="ai-projects__stack">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Личный ассистент в Telegram: напоминания, планирование дел и работа с LLM в одном диалоге.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -120,13 +69,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Image Gen Bot</h3>
<p class="ai-projects__stack">Python · aiogram 3 · Redis · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · Redis · PostgreSQL</p>
<p class="mp-card__desc">Бот для генерации изображений: баланс, реферальная программа, админ-логи в отдельные топики супергруппы.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -135,13 +84,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="mcp bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">MCP Image Server</h3>
<p class="ai-projects__stack">JavaScript · MCP · OpenRouter</p>
<p class="mp-card__desc">JavaScript · MCP · OpenRouter</p>
<p class="mp-card__desc">Собственный MCP-сервер для генерации картинок. Подключается к агентам как инструмент.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">MCP</li>
@@ -150,13 +99,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">AI assistant</h3>
<p class="ai-projects__stack">TypeScript · Figma Plugin API</p>
<p class="mp-card__desc">TypeScript · Figma Plugin API</p>
<p class="mp-card__desc">Плагин для Figma, который исполняет скрипты прямо в редакторе. ИИ-ассистент для любого тарифа.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -165,13 +114,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Card Columns</h3>
<p class="ai-projects__stack">TypeScript · Obsidian API</p>
<p class="mp-card__desc">TypeScript · Obsidian API</p>
<p class="mp-card__desc">Вью для заметок в стиле канбан-доски: группировка по колонкам с фильтрами по тегам.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -180,13 +129,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="plugins">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Hermes Desktop Plugins</h3>
<p class="ai-projects__stack">JavaScript · TypeScript · Electron</p>
<p class="mp-card__desc">JavaScript · TypeScript · Electron</p>
<p class="mp-card__desc">Плагины для рабочего стола Hermes: чип баланса API, звук завершения задачи и контроль трат.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Плагины</li>
@@ -195,13 +144,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Social Post Writer</h3>
<p class="ai-projects__stack">Python · aiogram 3 · SQLite</p>
<p class="mp-card__desc">Python · aiogram 3 · SQLite</p>
<p class="mp-card__desc">Бот, который превращает тезисы в готовый пост под нужную площадку и тон, учитывая голос автора.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Боты</li>
@@ -210,13 +159,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="web">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">AI Design Assistant</h3>
<p class="ai-projects__stack">HTML · CSS · GLSL · WebGL</p>
<p class="mp-card__desc">HTML · CSS · GLSL · WebGL</p>
<p class="mp-card__desc">Этот сайт целиком: структура, дизайн-система, шейдеры, CI/CD. Весь цикл создания вместе с ИИ-агентом.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Веб</li>
@@ -225,13 +174,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Секреты и менеджер паролей</h3>
<p class="ai-projects__stack">Vaultwarden · Docker · Coolify</p>
<p class="mp-card__desc">Vaultwarden · Docker · Coolify</p>
<p class="mp-card__desc">Своё хранилище паролей с веб-доступом. Отдельный сервис, доступ к API закрыт ключом.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -240,13 +189,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-01.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Прокси к хранилищу</h3>
<p class="ai-projects__stack">Go · Vaultwarden API · all-cipher</p>
<p class="mp-card__desc">Go · Vaultwarden API · all-cipher</p>
<p class="mp-card__desc">Форк Vaultwarden API с поддержкой всех типов шифрования. Нужен, чтобы читать секреты из агентов и скиллов.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -255,13 +204,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-02.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Уведомления</h3>
<p class="ai-projects__stack">Go · coturn · Matrix · ntfy</p>
<p class="mp-card__desc">Go · coturn · Matrix · ntfy</p>
<p class="mp-card__desc">Своя доставка уведомлений: ntfy для себя, Matrix-комната для агентов и WebRTC для звонков.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -270,13 +219,13 @@
</article>
</li>
<li data-filter-item data-filter-tag="infra bots">
<article class="mp-card mp-card--compact" data-glass="false">
<div class="mp-card__media ai-projects__media" aria-hidden="true">
<article class="mp-card mp-card--stack" data-glass="false">
<div class="mp-card__media" aria-hidden="true">
<img class="mp-card__image" src="{{assetPrefix}}assets/ai-dev/placeholder-03.webp" alt="" width="640" height="400" loading="lazy" decoding="async" />
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Бот для новостей</h3>
<p class="ai-projects__stack">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Python · aiogram 3 · PostgreSQL</p>
<p class="mp-card__desc">Telegram-бот, который присылает дайджест новостей по подпискам.</p>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Инфраструктура</li>
@@ -286,49 +235,48 @@
</article>
</li>
</ol>
</div>
</section>
<h2 id="stack">Стек</h2>
<ul class="section__prose hero__tags" aria-label="Стек">
<li class="hero__tag">Python</li>
<li class="hero__tag">TypeScript</li>
<li class="hero__tag">JavaScript</li>
<li class="hero__tag">Go</li>
<li class="hero__tag">HTML/CSS</li>
<li class="hero__tag">GLSL</li>
<li class="hero__tag">aiogram 3</li>
<li class="hero__tag">FastAPI</li>
<li class="hero__tag">PostgreSQL</li>
<li class="hero__tag">Redis</li>
<li class="hero__tag">SQLite</li>
<li class="hero__tag">Docker</li>
<li class="hero__tag">Coolify</li>
<li class="hero__tag">Gitea</li>
<li class="hero__tag">MCP</li>
</ul>
<section class="section">
<div class="section__inner hero__grid">
<div>
<p class="hero__eyebrow">02 / Среда, которую я собрал</p>
<h2 id="infra" class="hero__title">Своя<br />инфраструктура.</h2>
</div>
<div>
<p class="case-page__sub">Два VPS. Сервисы под моим контролем.</p>
<p class="section__prose">
Разворачиваю проекты в Docker через Coolify. Traefik ведёт домены и TLS;
Gitea хранит код и запускает деплой. WireGuard соединяет серверы в закрытую
сеть. PostgreSQL и Redis обслуживают ботов и приложения.
</p>
<ul class="hero__tags" aria-label="Инструменты инфраструктуры">
<li class="hero__tag">Coolify</li>
<li class="hero__tag">Traefik</li>
<li class="hero__tag">Gitea</li>
<li class="hero__tag">WireGuard</li>
<li class="hero__tag">Docker</li>
<li class="hero__tag">PostgreSQL</li>
<li class="hero__tag">Redis</li>
<li class="hero__tag">XRay</li>
<li class="hero__tag">Python · aiogram</li>
<li class="hero__tag">TypeScript · JavaScript</li>
<li class="hero__tag">Go</li>
<li class="hero__tag">GLSL · WebGL</li>
</ul>
<p class="section__prose">
У сервисов отдельные схемы в общей базе. Между серверами ходит трафик
через WireGuard, наружу открываются только нужные точки входа.
</p>
</div>
</div>
</section>
<h2 id="infra">Инфраструктура</h2>
<p class="section__prose">
Всё, что я делаю, живёт на двух VPS и не зависит от внешних PaaS. Деплой
автоматический: пуш в ветку поднимает контейнер через Coolify, дальше
реверс-прокси и TLS берёт на себя Traefik.
</p>
<ul class="mp-card__tags" aria-label="Состав инфраструктуры">
<li class="mp-card__tag">Coolify · self-hosted панель деплоя</li>
<li class="mp-card__tag">Traefik · прокси и сертификаты</li>
<li class="mp-card__tag">Gitea · свой git и CI/CD</li>
<li class="mp-card__tag">WireGuard · меш из нескольких серверов</li>
<li class="mp-card__tag">XRay · VPN для доступа к API</li>
<li class="mp-card__tag">Docker · изоляция сервисов</li>
<li class="mp-card__tag">PostgreSQL · общая база по схемам</li>
<li class="mp-card__tag">Redis · очереди и состояние FSM</li>
</ul>
<p class="section__prose">
База одна на несколько проектов: каждый бот берёт свою схему, поэтому
сервисы не мешают друг другу, но состояние видно целиком. Доступ между
серверами закрыт WireGuard-туннелями, публичные адреса выдаёт Traefik.
</p>
<h2 id="links">Где я это выкладываю</h2>
<section class="section section--tight">
<div class="section__inner">
<p class="hero__eyebrow">03 / Код и проекты</p>
<h2 id="links" class="case-page__h1">Смотреть дальше</h2>
<ul class="case-links-grid">
<li>
<a class="case-link-card" href="https://github.com/mamadaevv" target="_blank" rel="noopener noreferrer">
@@ -363,7 +311,8 @@
</a>
</li>
</ul>
</div>
</div>
</section>
</main>
</body>
</html>
+9
View File
@@ -744,6 +744,15 @@
</ul>
<p class="sg-label">.filter-chips / .filter-chip[aria-pressed] + [data-filter-item][data-filter-tag]</p>
<h3>Сетка проектов</h3>
<p class="section__prose">Адаптивная сетка карточек: одна колонка на узком экране, две от 680 px и три от 1120 px. Внутри списка используйте существующий компонент карточки.</p>
<ul class="project-grid" aria-label="Демо сетки проектов">
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="{{assetPrefix}}assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект A</h3><p class="mp-card__desc">Карточка в адаптивной сетке.</p></div></article></li>
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="{{assetPrefix}}assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект B</h3><p class="mp-card__desc">Сетка подстраивается под экран.</p></div></article></li>
<li><article class="mp-card mp-card--stack" data-glass="false"><div class="mp-card__media" aria-hidden="true"><img class="mp-card__image" src="{{assetPrefix}}assets/shared/placeholder.webp" alt="" width="400" height="225" loading="lazy" decoding="async" /></div><div class="mp-card__body"><h3 class="mp-card__title">Проект C</h3><p class="mp-card__desc">Карточки сохраняют заданный ритм.</p></div></article></li>
</ul>
<p class="sg-label">.project-grid + .mp-card--stack</p>
<h3>Карточка портфолио</h3>
<div class="sg-portfolio-wrap">
<a class="mp-card mp-card--link mp-card--stack-bottom" href="#components" tabindex="-1" aria-hidden="true">