Files
Flamy-Trade_svelte/README.md
T
2026-05-28 21:03:35 +05:00

317 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Flamy Trade — Frontend
Публичный лендинг и дашборд ML-прогнозов для крипторынка. Построен на **SvelteKit 5** (Runes API), **Tailwind v4**, **TypeScript** и **lightweight-charts v5**.
---
## Стек
| Слой | Технология |
|---|---|
| Фреймворк | SvelteKit 5 (runes, SSR) |
| Язык | TypeScript (strict) |
| Стили | Tailwind CSS v4 (Vite plugin, `@theme`) |
| Графики | lightweight-charts v5 (TradingView) |
| Сервер | `@sveltejs/adapter-node` (Node.js) |
| Пакетный менеджер | pnpm |
---
## Быстрый старт
**Требования:** Node.js ≥ 20, pnpm ≥ 9
```bash
# 1. Установить зависимости
pnpm install
# 2. Запустить dev-сервер
pnpm dev
# 3. Открыть в браузере
# http://localhost:5173
```
Остальные команды:
```bash
pnpm build # Сборка для продакшена (папка build/)
pnpm preview # Превью продакшен-сборки
pnpm check # Svelte + TypeScript проверка типов
pnpm lint # ESLint + Prettier проверка
pnpm format # Автоформатирование
```
---
## Структура проекта
```
src/
├── lib/
│ ├── components/
│ │ ├── Chart.svelte # lightweight-charts обёртка
│ │ ├── home/
│ │ │ ├── DashboardMockup.svelte # 3D-макет на главной
│ │ │ └── DashboardStatus.svelte
│ │ ├── layout/
│ │ │ ├── Header.svelte # Фиксированная шапка + мобильное меню
│ │ │ └── Footer.svelte
│ │ └── ui/
│ │ ├── Button.svelte # Универсальная кнопка/ссылка
│ │ └── Logo.svelte
│ ├── blog/
│ │ └── posts.ts # Статьи блога (статические данные)
│ ├── stores/
│ │ └── chartStore.ts # Типы + генератор фейковых данных
│ ├── utils.ts # cn() — утилита для className
│ └── index.ts
├── routes/
│ ├── +layout.svelte # Корневой layout: Header, Footer, шрифты
│ ├── +page.svelte # Главная страница (секции)
│ ├── +error.svelte # Страница ошибки
│ ├── (sections)/ # Секции главной страницы
│ │ ├── Hero.svelte
│ │ ├── Ticker.svelte
│ │ ├── About.svelte
│ │ ├── Features.svelte
│ │ ├── Stats.svelte
│ │ ├── BlogPreview.svelte
│ │ └── Cta.svelte
│ ├── about/
│ │ ├── +page.svelte
│ │ └── _data.ts
│ ├── blog/
│ │ ├── +page.svelte # Список статей
│ │ └── [slug]/
│ │ ├── +page.ts # Загрузка статьи по slug
│ │ └── +page.svelte # Статья
│ └── dashboard/
│ └── +page.svelte # Интерактивный дашборд с графиком
└── routes/layout.css # Tailwind @theme + глобальные стили
```
---
## Дизайн-система
### Цвета (`layout.css`)
```css
--color-primary: #fe4b07 /* Акцент — оранжевый */
--color-primary-h:#c83e06 /* Hover-состояние */
--color-bg: #09080a /* Фон страницы */
--color-bg-e: #0f0e10 /* Elevated (шапка, карточки) */
--color-bg-c: #141318 /* Card background */
--color-bg-h: #1a191e /* Hover / бордеры */
--color-title: #f0ede6 /* Основной текст */
--color-desc: #8a887f /* Вторичный текст */
```
В Tailwind используются как `bg-primary`, `text-desc`, `border-bg-h` и т.д.
### Шрифты
| Переменная | Семейство | Применение |
|---|---|---|
| `font-display` | Unbounded | Заголовки, кнопки, тикеры |
| `font-sans` | DM Sans | Основной текст |
| `font-mono` | JetBrains Mono | Цены, метки, метаданные |
Подключаются через Google Fonts в `+layout.svelte`.
### Компонент `Button.svelte`
```svelte
<!-- Первичная (ссылка) -->
<Button href="/dashboard">Открыть дашборд</Button>
<!-- Вторичная (кнопка) -->
<Button variant="secondary" onclick={handler}>Действие</Button>
```
Props: `href?`, `target?`, `variant?: 'primary' | 'secondary'`, `class?`
---
## Дашборд и Charts
### Архитектура данных
```
chartStore.ts → generateChartData(symbolId, timeframeId)
└── возвращает ChartData
├── candles: CandleBar[] // 160 свечей
├── prediction: PredPoint[] // 19 прогнозных точек
├── signal: Signal // direction, confidence, entry/tp/sl
├── currentPrice: number
└── change24h(Pct): number
```
`generateChartData`**заглушка с детерминированными фейковыми данными**. При подключении реального бекенда её нужно заменить на API-запрос, сохранив возвращаемые типы:
```typescript
// chartStore.ts — заменить эту функцию на реальный запрос:
export async function fetchChartData(symbolId: string, tfId: TimeframeId): Promise<ChartData> {
const res = await fetch(`/api/chart/${symbolId}?tf=${tfId}`);
return res.json();
}
```
### Компонент `Chart.svelte`
```svelte
<Chart data={chartData} decimals={2} height={520} />
```
| Prop | Тип | Описание |
|---|---|---|
| `data` | `ChartData` | Свечи + прогноз + сигнал |
| `decimals` | `number` | Кол-во знаков после запятой для цены |
| `height` | `number` | Высота canvas в пикселях (default: 520) |
Что рендерится:
- **CandlestickSeries** — исторические зелёные/красные свечи
- **LineSeries** — пунктирная оранжевая линия ML-прогноза (19 свечей вперёд)
- **PriceLine** × 3 — горизонтальные линии Entry / TP / SL с подписями на шкале
- **OHLC тултип** — кастомный HTML-оверлей при наведении (показывает O/H/L/C и ML-значение)
- **ResizeObserver** — автоматически подстраивает ширину и высоту при изменении окна
### Таймфреймы и символы
Определены в `chartStore.ts`:
```typescript
SYMBOLS // 8 монет: BTC ETH SOL BNB XRP ADA DOGE LINK
TIMEFRAMES // 6 таймфреймов: 1m 5m 15m 1h 4h 1d
```
---
## Контракт API (для бекенда)
Дашборд ожидает от бекенда следующую форму ответа:
```typescript
type ChartData = {
candles: Array<{
time: number; // Unix timestamp (UTC seconds)
open: number;
high: number;
low: number;
close: number;
}>;
prediction: Array<{
time: number; // Unix timestamp будущих свечей
value: number; // Прогнозная цена (mid/close)
}>;
signal: {
direction: 'long' | 'short';
confidence: number; // 0100
entry: number;
tp: number;
sl: number;
};
currentPrice: number;
change24h: number;
change24hPct: number;
};
```
Эндпоинт (предполагаемый):
```
GET /api/chart/:symbol?tf=5m
```
---
## Блог
Статьи хранятся в `src/lib/blog/posts.ts` как статический массив `Post[]`.
```typescript
type Post = {
id: number;
slug: string; // URL: /blog/:slug
date: string; // ISO 8601
title: string;
description: string;
tags: string[];
content: string; // HTML-строка
};
```
Для подключения CMS — заменить `posts` на API-запрос в `+page.ts` / `+page.server.ts`.
---
## Конфигурация
### SvelteKit (`svelte.config.js`)
- Адаптер: `adapter-node` (деплой как Node.js-сервер)
- Runes: принудительно включены для всех файлов вне `node_modules`
### Tailwind v4 (`layout.css`)
Tailwind подключается через Vite-плагин (`@tailwindcss/vite`), конфиг CSS-первый — все кастомные токены в блоке `@theme` в `src/routes/layout.css`.
**Добавить новый токен:**
```css
/* layout.css */
@theme {
--color-accent: #your-color;
}
```
После этого доступен как `bg-accent`, `text-accent` и т.д.
---
## Деплой
Сборка производит Node.js-сервер:
```bash
pnpm build
node build/index.js
```
Переменные окружения:
```
PORT=3000 # порт (default: 3000)
HOST=0.0.0.0 # хост
ORIGIN=https://... # обязательно при deploy за проксей
```
Docker (минимальный `Dockerfile`):
```dockerfile
FROM node:22-alpine
WORKDIR /app
COPY build/ ./build/
COPY package.json .
RUN npm install --omit=dev --ignore-scripts
CMD ["node", "build/index.js"]
```
---
## Соглашения по коду
- **Svelte 5 Runes** везде: `$state`, `$derived`, `$effect`, `$props` — никакого legacy API
- **Нет `export let`** — только деструктуризация `$props()`
- **Tailwind-first** — inline-стили только там, где Tailwind не справляется (динамические значения, chart dimensions)
- **cn()** из `$lib/utils` для условного объединения классов
- **Нет комментариев** если смысл очевиден из имён; комментарий — только для неочевидного инварианта
- **SSR-safe** — всё что требует `window`/`document` — только в `onMount`
- **Cleanup** — каждый `onMount` возвращает функцию очистки (removeEventListener, disconnect, remove)
---
## Известные ограничения
- Данные в дашборде — **фейковые** (детерминированный генератор). Требует подключения реального API бекенда через замену `generateChartData` в `chartStore.ts`
- Прогнозная линия — условная визуализация; реальная модель возвращает `prediction[]` из бекенда
- Блог — статические данные в коде; для production рекомендуется CMS или headless API