From 464ea7573056d4a500c3460b3d71399323c12afc Mon Sep 17 00:00:00 2001 From: Jarvis Date: Thu, 16 Jul 2026 20:58:07 +0000 Subject: [PATCH] feat: Sharp style, Tailwind+shadcn, dark navbar, nav restructure, Go/monorepo guidelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Styling: - Tailwind CSS v4 (preflight:false) integrated via PostCSS plugin - shadcn/ui New York style with --radius:0 (sharp corners everywhere) - Zero border-radius on ALL elements: buttons, code blocks, cards, admonitions, badges - Navbar FORCE dark (#0a0a0a) in both light/dark modes via !important - ThemeSynchronizer: bridges Docusaurus data-theme → shadcn .dark class - Active sidebar item gets left border accent instead of rounded bg React UI components (src/components/ui/index.tsx): - Badge (6 variants: wip, stable, planned, success, warning, outline) - Card + CardHeader + CardTitle + CardContent - Callout (note/info/success/warning/danger) - sharp, left-border style - PropertiesTable - monospace typed API reference tables - StatusIndicator - operational/degraded/outage/maintenance Navigation restructure: - Introduction → Platform → Products → Engineering → Runbooks - Old 'applications/' removed, moved to products/stock-market-pro/ - New Platform section: infrastructure overview + CI/CD pipeline docs - New Engineering section: monorepo structure, Go stack, conventions - New Runbooks section: incident severity, common procedures Implementation guidelines (engineering/guidelines.md): - Monorepo layout with go.work workspace - Go as primary language (why + standard library table) - Python as sidecar for data/ML workloads - Code conventions: gofmt, error wrapping, logging, testing - Git conventions: branch naming, conventional commits - Makefile targets per app - How to add a new application (step-by-step) --- components.json | 1 + docs/engineering/guidelines.md | 159 +++++++ docs/intro/index.md | 53 ++- docs/platform/cicd.md | 65 +++ docs/platform/overview.md | 43 ++ docs/platform/smp-pipeline.drawio | 94 ++++ .../stock-market-pro/c4/components.md | 112 +++++ .../stock-market-pro/c4/containers.md | 71 +++ docs/products/stock-market-pro/c4/context.md | 50 ++ .../stock-market-pro/data-monitoring/index.md | 72 +++ docs/products/stock-market-pro/index.md | 50 ++ .../stock-market-pro/overview/architecture.md | 95 ++++ .../signal-generation/index.md | 130 ++++++ .../technical-analysis/index.md | 112 +++++ docs/runbooks/index.md | 61 +++ docusaurus.config.ts | 10 + package.json | 12 +- pnpm-lock.yaml | 433 +++++++++++++++++- sidebars.ts | 55 ++- src/components/ui/index.tsx | 156 +++++++ src/css/custom.css | 364 +++++++-------- src/lib/utils.ts | 6 + src/pages/index.tsx | 2 +- src/theme/Layout/index.tsx | 27 ++ tailwind.config.js | 57 +++ tsconfig.json | 3 +- 26 files changed, 2076 insertions(+), 217 deletions(-) create mode 100644 components.json create mode 100644 docs/engineering/guidelines.md create mode 100644 docs/platform/cicd.md create mode 100644 docs/platform/overview.md create mode 100644 docs/platform/smp-pipeline.drawio create mode 100644 docs/products/stock-market-pro/c4/components.md create mode 100644 docs/products/stock-market-pro/c4/containers.md create mode 100644 docs/products/stock-market-pro/c4/context.md create mode 100644 docs/products/stock-market-pro/data-monitoring/index.md create mode 100644 docs/products/stock-market-pro/index.md create mode 100644 docs/products/stock-market-pro/overview/architecture.md create mode 100644 docs/products/stock-market-pro/signal-generation/index.md create mode 100644 docs/products/stock-market-pro/technical-analysis/index.md create mode 100644 docs/runbooks/index.md create mode 100644 src/components/ui/index.tsx create mode 100644 src/lib/utils.ts create mode 100644 src/theme/Layout/index.tsx create mode 100644 tailwind.config.js diff --git a/components.json b/components.json new file mode 100644 index 0000000..c81ca38 --- /dev/null +++ b/components.json @@ -0,0 +1 @@ +{"$schema":"https://ui.shadcn.com/schema.json","style":"new-york","rsc":false,"tsx":true,"tailwind":{"config":"tailwind.config.js","css":"src/css/custom.css","baseColor":"zinc","cssVariables":true,"prefix":""},"aliases":{"components":"@site/src/components","utils":"@site/src/lib/utils"},"iconLibrary":"none"} diff --git a/docs/engineering/guidelines.md b/docs/engineering/guidelines.md new file mode 100644 index 0000000..1d96a70 --- /dev/null +++ b/docs/engineering/guidelines.md @@ -0,0 +1,159 @@ +--- +sidebar_position: 1 +--- + +# Engineering Guidelines + +This document defines how applications in SKIC Playground are structured, built, and maintained. + +## Monorepo Structure + +All applications live in a **monorepo** at `skic-v1-playground` in Gitea. Each application is a top-level directory with its own build system. + +``` +skic-v1-playground/ +│ +├── documentation/ # This Docusaurus site +│ +├── stock-market-pro/ # Go application +│ ├── cmd/ +│ │ └── server/ +│ │ └── main.go # Entrypoint +│ ├── internal/ +│ │ ├── ingestor/ # Data ingestion +│ │ ├── analysis/ # Technical analysis engine +│ │ ├── signals/ # Signal generation +│ │ └── notifier/ # Discord delivery +│ ├── pkg/ # Shared public packages +│ ├── Dockerfile +│ ├── Makefile +│ └── go.mod +│ +├── shared/ # Cross-app shared libraries (Go modules) +│ ├── discord/ # Discord client +│ ├── config/ # Config loader +│ └── telemetry/ # Logging, metrics, tracing +│ +├── infra/ # Infrastructure-as-code +│ ├── docker-compose.yml # Local dev stack +│ └── gitea-runners/ # CI runner config +│ +└── Makefile # Root-level make targets (build all, test all) +``` + +## Go Workspace (go.work) + +The monorepo uses [Go workspaces](https://go.dev/ref/mod#workspaces) so all Go modules can reference each other without publishing: + +``` +# go.work +go 1.22 + +use ( + ./stock-market-pro + ./shared/discord + ./shared/config + ./shared/telemetry +) +``` + +This means local changes to `shared/` are immediately reflected across all Go apps without `replace` directives in individual `go.mod` files. + +## Tech Stack + +### Applications — Go + +Go is the primary language for all backend services and autonomous applications. + +**Why Go:** +- Statically typed, compiled, single binary deployment +- Excellent concurrency primitives (goroutines, channels) — ideal for market data streams +- Native cross-compilation +- Fast build times, clean toolchain +- Standard library covers most needs (HTTP, JSON, time, crypto) + +**Standard libraries:** + +| Package | Purpose | +|---|---| +| `net/http` | HTTP server / client | +| `encoding/json` | JSON serialization | +| `database/sql` + `modernc.org/sqlite` | SQLite (dev) | +| `github.com/lib/pq` | PostgreSQL / TimescaleDB (prod) | +| `github.com/rs/zerolog` | Structured logging | +| `github.com/spf13/viper` | Config management | +| `github.com/robfig/cron/v3` | Scheduled jobs | +| `golang.org/x/sync` | Concurrency utilities | + +### Data / Analysis — Python + +Python is used for data science and ML-heavy workloads where the Go ecosystem is thin: + +- `pandas`, `numpy` — data manipulation +- `pandas-ta`, `ta-lib` — technical indicators +- `scikit-learn` — pattern recognition / ML +- `statsmodels` — GARCH, time series + +When Python is used, it runs as a **sidecar service** alongside the Go application, exposing a local HTTP/gRPC API. + +### Frontend / Docs — TypeScript + React + +- Docusaurus v3 for documentation +- React + shadcn/ui + Tailwind for interactive components within docs + +## Code Conventions + +### Go + +- **`gofmt`** — enforced in CI +- **Error handling** — always wrap with `fmt.Errorf("context: %w", err)`, never ignore +- **Packages** — flat, purposeful. No circular dependencies. `internal/` for app-private code +- **Config** — environment variables via Viper, validated at startup +- **Logging** — structured JSON via zerolog, no `fmt.Println` in production code +- **Tests** — table-driven tests in `_test.go` files, `testify/assert` for assertions + +```go +// Good — explicit error context +if err := db.Query(ctx, q); err != nil { + return fmt.Errorf("ingestor: fetch candles: %w", err) +} + +// Bad — swallowed error +db.Query(ctx, q) +``` + +### Git + +- **Branch naming**: `feat/description`, `fix/description`, `docs/description` +- **Commit messages**: conventional commits — `feat:`, `fix:`, `docs:`, `chore:`, `refactor:` +- **PRs**: all changes via PR, even from Jarvis — CI must pass before merge +- **Main is always deployable** + +## Makefile Targets (per app) + +```makefile +build: ## Build binary + go build -o bin/server ./cmd/server + +test: ## Run tests + go test ./... -race -cover + +lint: ## Run linter + golangci-lint run + +docker: ## Build Docker image + docker build -t $(APP_NAME):$(VERSION) . + +run: ## Run locally with .env + source .env && go run ./cmd/server +``` + +## Adding a New Application + +1. Create directory `skic-v1-playground//` +2. Initialize Go module: `go mod init gitea.lego-cloud.eu/skic-v1-playground/` +3. Add to `go.work`: `use ./` +4. Create `.gitea/workflows/build.yml` from the CI template +5. Create `Dockerfile` (multi-stage: `golang:1.22-alpine` → `alpine:3.19`) +6. Add documentation under `documentation/docs/products//` following C4 structure +7. Add to homepage application table diff --git a/docs/intro/index.md b/docs/intro/index.md index 959eab4..76d96c9 100644 --- a/docs/intro/index.md +++ b/docs/intro/index.md @@ -3,29 +3,46 @@ sidebar_position: 1 slug: /intro --- -# SKIC Playground Documentation +# SKIC Platform -Welcome to the documentation hub for **SKIC Playground** — a collection of autonomous applications built and maintained by Jarvis (AI agent). +**SKIC Playground** is a self-hosted autonomous platform — a collection of applications designed, built, and operated by Jarvis (AI agent) on top of a private infrastructure stack. -## What is this? +## What lives here -This documentation covers various applications designed, developed, and operated autonomously. Each application has its own section with: +import { Badge, Card, CardHeader, CardTitle, CardContent } from '@site/src/components/ui'; -- **Overview** — What the application does and why -- **Architecture** — Technical design decisions and system diagrams -- **Configuration** — How to set up and configure -- **Operations** — How the application runs day-to-day +
+ + +
+ 📈 Stock Market Professional + In Development +
+
+ + Monitors forex & stock markets, applies technical analysis to spot patterns, and emits daily trading signals (max 3 positions, 30-minute hold, manual execution). + +
+
-## Applications +## How this documentation is structured -| Application | Status | Description | -|---|---|---| -| [Stock Market Professional](../applications/stock-market-professional/index.md) | 🚧 In Development | Forex/stock monitoring, technical analysis, and signal generation | +| Section | Contents | +|---|---| +| **Introduction** | This page — platform overview and navigation guide | +| **Platform** | Infrastructure, CI/CD, networking, auth | +| **Products** | Each autonomous application — architecture, modules, operations | +| **Engineering** | Development conventions, monorepo structure, tech stack | +| **Runbooks** | Operational procedures, incident response, diagnostics | -## Technology Stack +## Technology at a glance -- **Infrastructure**: TrueNAS SCALE, Gitea, Docker -- **CI/CD**: Gitea Actions -- **Documentation**: Docusaurus + pnpm -- **Languages**: TypeScript, Python -- **AI/ML**: Pattern recognition, technical analysis algorithms +| Layer | Technology | +|---|---| +| Infrastructure | TrueNAS SCALE, Docker, Gitea | +| Auth | Keycloak (SSO) | +| Registry | Zot (OCI) | +| CI/CD | Gitea Actions | +| App language | Go (services), Python (data/analysis), TypeScript (frontend) | +| Docs | Docusaurus v3 + pnpm + Tailwind + shadcn/ui | +| AI operations | Hermes Agent (Jarvis) | diff --git a/docs/platform/cicd.md b/docs/platform/cicd.md new file mode 100644 index 0000000..d86d8ce --- /dev/null +++ b/docs/platform/cicd.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 2 +--- + +# CI/CD Pipeline + +All applications follow a single, consistent Gitea Actions pipeline. + +## Flow + +```mermaid +sequenceDiagram + participant J as Jarvis / Dev + participant G as Gitea + participant A as Actions Worker + participant R as Zot Registry + participant H as TrueNAS Host + + J->>G: git push main + G->>A: Trigger workflow + A->>A: pnpm/go build + test + A->>R: docker push image + A->>H: Deploy (restart container) + H-->>J: ✅ Live +``` + +## Workflow Template + +```yaml +# .gitea/workflows/build.yml +name: Build and Deploy + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build + run: make build # or go build / pnpm build + + - name: Test + run: make test + + - name: Docker build & push + if: github.ref == 'refs/heads/main' + run: | + docker build -t $ZOT_REGISTRY/$APP_NAME:$GITHUB_SHA . + docker push $ZOT_REGISTRY/$APP_NAME:$GITHUB_SHA + + - name: Deploy + if: github.ref == 'refs/heads/main' + run: | + ssh deploy@lego-cloud.eu "docker pull && docker restart $APP_NAME" +``` + +## Gitea Actions Runners + +Runners need to be registered on the Gitea instance. See [Gitea Actions documentation](https://docs.gitea.com/usage/actions/quickstart) for setup. diff --git a/docs/platform/overview.md b/docs/platform/overview.md new file mode 100644 index 0000000..04775ab --- /dev/null +++ b/docs/platform/overview.md @@ -0,0 +1,43 @@ +--- +sidebar_position: 1 +--- + +# Platform Overview + +SKIC Playground runs entirely on self-hosted infrastructure at `lego-cloud.eu`. + +## Infrastructure Diagram + +```mermaid +graph TB + subgraph TrueNAS["TrueNAS SCALE 26.0 — lego-cloud.eu"] + direction TB + Gitea["Gitea 1.26\nSource + CI/CD\ngitea.lego-cloud.eu"] + Zot["Zot Registry\n:30264\nOCI images"] + Keycloak["Keycloak\nSSO / Auth\nkeycloak.lego-cloud.eu"] + Hermes["Hermes Agent\nAI Operations\n(Jarvis)"] + Apps["Application\nContainers"] + end + + Dev["Developer / Jarvis"] -->|git push| Gitea + Gitea -->|Actions runner| CI["CI/CD Worker"] + CI -->|docker push| Zot + CI -->|deploy| Apps + Zot -->|pull| Apps + Keycloak -->|auth| Apps + Hermes -->|manages| Gitea + Hermes -->|manages| Apps +``` + +## Services + +| Service | URL | Version | Purpose | +|---|---|---|---| +| Gitea | gitea.lego-cloud.eu | 1.26.2 | Source control, CI/CD, issue tracking | +| Keycloak | keycloak.lego-cloud.eu | — | Authentication, SSO | +| Zot | :30264 | — | OCI-compliant container registry | +| Hermes | local | — | AI agent (Jarvis) — automation, docs, ops | + +## Storage + +All persistent data lives under `/mnt/lego-cloud-v1-2025-11-28/` on TrueNAS. diff --git a/docs/platform/smp-pipeline.drawio b/docs/platform/smp-pipeline.drawio new file mode 100644 index 0000000..1c6da14 --- /dev/null +++ b/docs/platform/smp-pipeline.drawio @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/products/stock-market-pro/c4/components.md b/docs/products/stock-market-pro/c4/components.md new file mode 100644 index 0000000..ce6a3e3 --- /dev/null +++ b/docs/products/stock-market-pro/c4/components.md @@ -0,0 +1,112 @@ +--- +sidebar_position: 3 +--- + +# L3 — Components + +> **C4 Level 3**: Zooms into the key containers and shows their internal modules, classes, and responsibilities. + +:::info C4 Model — Level 3 +The Component diagram answers: *What are the major structural elements inside each container?* +::: + +## Analysis Engine — Component Diagram + +```mermaid +C4Component + title Components — Analysis Engine + + Container_Ext(tsdb, "Market Data Store", "SQLite / TimescaleDB", "OHLCV candles") + Container_Ext(signalGen, "Signal Generator", "Python", "Consumes indicator snapshots") + + Container_Boundary(analyser, "Analysis Engine") { + Component(candleLoader, "Candle Loader", "pandas", "Loads recent OHLCV windows from DB for each symbol and timeframe.") + Component(trendEngine, "Trend Engine", "pandas-ta", "Computes EMA, MACD, ADX, Parabolic SAR.") + Component(momentumEngine, "Momentum Engine", "pandas-ta", "Computes RSI, Stochastic, CCI, Williams %R.") + Component(volatilityEngine, "Volatility Engine", "pandas-ta", "Computes Bollinger Bands, ATR, Keltner Channels.") + Component(volumeEngine, "Volume Engine", "pandas-ta", "Computes VWAP, OBV, Volume Profile.") + Component(patternRecog, "Pattern Recogniser", "ta-lib / custom", "Detects candlestick and chart patterns.") + Component(confluenceScorer, "Confluence Scorer", "Python", "Weights and combines indicator signals into a 0–1 confluence score.") + Component(snapshotWriter, "Snapshot Writer", "pandas / SQLAlchemy", "Persists indicator snapshots to DB for the signal generator.") + } + + Rel(candleLoader, tsdb, "Reads OHLCV", "SQL") + Rel(trendEngine, candleLoader, "Reads candle frame") + Rel(momentumEngine, candleLoader, "Reads candle frame") + Rel(volatilityEngine, candleLoader, "Reads candle frame") + Rel(volumeEngine, candleLoader, "Reads candle frame") + Rel(patternRecog, candleLoader, "Reads candle frame") + Rel(confluenceScorer, trendEngine, "Reads signals") + Rel(confluenceScorer, momentumEngine, "Reads signals") + Rel(confluenceScorer, volatilityEngine, "Reads signals") + Rel(confluenceScorer, volumeEngine, "Reads signals") + Rel(confluenceScorer, patternRecog, "Reads patterns") + Rel(snapshotWriter, confluenceScorer, "Reads scored snapshot") + Rel(snapshotWriter, tsdb, "Writes snapshots", "SQL") + Rel(signalGen, snapshotWriter, "Reads snapshots", "SQL") +``` + +## Signal Generator — Component Diagram + +```mermaid +C4Component + title Components — Signal Generator + + Container_Ext(tsdb, "Market Data Store", "TimescaleDB", "Indicator snapshots") + Container_Ext(newsApi, "Economic Calendar API", "REST", "High-impact events") + Container_Ext(notifier, "Notifier", "Python", "Receives final signals") + + Container_Boundary(signalGen, "Signal Generator") { + Component(snapshotReader, "Snapshot Reader", "SQLAlchemy", "Loads latest indicator snapshots per symbol.") + Component(biasFilter, "Bias Filter", "Python", "Determines directional bias (bullish/bearish/neutral) per pair from higher-TF trend.") + Component(setupScanner, "Setup Scanner", "Python", "Identifies candidate setups where confluence score ≥ 0.65.") + Component(riskCalc, "Risk Calculator", "Python", "Calculates entry, stop-loss, take-profit; validates R:R ≥ 1.5.") + Component(newsFilter, "News Filter", "Python / requests", "Rejects signals within 30 min of high-impact economic events.") + Component(correlFilter, "Correlation Filter", "Python", "Prevents conflicting signals on correlated pairs (e.g. EUR/USD + GBP/USD both long).") + Component(dailyLimitGuard, "Daily Limit Guard", "Python", "Enforces max 3 signals per day hard cap.") + Component(signalAssembler, "Signal Assembler", "Python", "Packages final signals with metadata, reasoning, and time window.") + } + + Rel(snapshotReader, tsdb, "Reads snapshots", "SQL") + Rel(biasFilter, snapshotReader, "Reads higher-TF data") + Rel(setupScanner, snapshotReader, "Reads scored snapshots") + Rel(setupScanner, biasFilter, "Applies directional filter") + Rel(riskCalc, setupScanner, "Calculates levels per setup") + Rel(newsFilter, newsApi, "Fetches upcoming events", "REST") + Rel(newsFilter, riskCalc, "Filters out news-window setups") + Rel(correlFilter, newsFilter, "Deduplicates correlated setups") + Rel(dailyLimitGuard, correlFilter, "Enforces ≤3 cap") + Rel(signalAssembler, dailyLimitGuard, "Assembles final signals") + Rel(notifier, signalAssembler, "Receives signals for delivery") +``` + +## Data Ingestor — Component Breakdown + +| Component | Responsibility | +|---|---| +| **Source Router** | Selects primary/fallback data source per symbol | +| **REST Poller** | Fetches 1m candles from REST API on schedule | +| **WS Stream Client** | Maintains WebSocket connection for real-time tick data | +| **Normaliser** | Maps source-specific field names → unified OHLCV schema | +| **Gap Detector** | Identifies missing candles, triggers backfill | +| **Backfiller** | Fetches historical data to fill detected gaps | +| **Rate Limiter** | Tracks and respects per-API-key rate limits | +| **Health Reporter** | Emits data freshness metrics; alerts on stale data | + +## Confluence Scoring — Detail + +```python +# Scoring weights (sum = 1.0) +WEIGHTS = { + 'trend_alignment': 0.30, # EMA cross, MACD, ADX + 'momentum': 0.25, # RSI, Stochastic, CCI + 'volume': 0.20, # VWAP, OBV + 'pattern': 0.15, # Candlestick / chart patterns + 'key_level': 0.10, # Support/resistance proximity +} + +MINIMUM_CONFLUENCE = 0.65 # Hard threshold +MINIMUM_RR_RATIO = 1.5 # Risk/reward hard floor +MAX_DAILY_SIGNALS = 3 # Hard daily cap +MAX_HOLD_MINUTES = 30 # Hard position time limit +``` diff --git a/docs/products/stock-market-pro/c4/containers.md b/docs/products/stock-market-pro/c4/containers.md new file mode 100644 index 0000000..a82fcde --- /dev/null +++ b/docs/products/stock-market-pro/c4/containers.md @@ -0,0 +1,71 @@ +--- +sidebar_position: 2 +--- + +# L2 — Containers + +> **C4 Level 2**: Zooms into Stock Market Professional and shows its deployable units — services, databases, and schedulers. + +:::info C4 Model — Level 2 +The Container diagram answers: *What are the high-level technical building blocks, and how do they talk to each other?* +::: + +## Container Diagram + +```mermaid +C4Container + title Container Diagram — Stock Market Professional + + Person(trader, "Trader", "Reviews signals, executes manually") + + System_Boundary(smp, "Stock Market Professional") { + Container(ingestor, "Data Ingestor", "Python / APScheduler", "Polls market data APIs on schedule, normalises OHLCV candles, detects gaps.") + Container(analyser, "Analysis Engine", "Python / pandas-ta", "Applies technical indicators and pattern recognition on latest candle data.") + Container(signalGen, "Signal Generator", "Python", "Scores setups by confluence, filters by risk rules, produces ≤3 daily signals.") + Container(scheduler, "Job Scheduler", "APScheduler / cron", "Orchestrates pipeline runs: ingest every minute, analyse every 5 min, generate plan pre-market.") + ContainerDb(tsdb, "Market Data Store", "SQLite / TimescaleDB", "Stores normalised OHLCV candles, indicator snapshots, and signal history.") + Container(notifier, "Notifier", "Python / Discord Webhook", "Formats and delivers the daily trading plan and intraday alerts.") + } + + System_Ext(marketData, "Market Data APIs", "Yahoo Finance, Alpha Vantage, Polygon") + System_Ext(discord, "Discord", "Signal delivery") + System_Ext(newsApi, "Economic Calendar API", "Forex Factory, Investing.com") + + Rel(scheduler, ingestor, "Triggers", "Internal call") + Rel(scheduler, analyser, "Triggers", "Internal call") + Rel(scheduler, signalGen, "Triggers", "Internal call") + Rel(ingestor, marketData, "Fetches OHLCV", "REST / WS") + Rel(ingestor, tsdb, "Writes candles", "SQL") + Rel(analyser, tsdb, "Reads candles", "SQL") + Rel(analyser, tsdb, "Writes indicator snapshots", "SQL") + Rel(signalGen, tsdb, "Reads snapshots", "SQL") + Rel(signalGen, newsApi, "Checks economic calendar", "REST") + Rel(signalGen, notifier, "Passes signals", "In-process") + Rel(notifier, discord, "Posts daily plan", "Webhook") + Rel(trader, discord, "Reads signals", "Discord UI") +``` + +## Containers Inventory + +| Container | Technology | Responsibility | +|---|---|---| +| **Data Ingestor** | Python, requests, websockets | Poll & normalise market data | +| **Analysis Engine** | Python, pandas, pandas-ta, ta-lib | Technical indicators + pattern recognition | +| **Signal Generator** | Python | Confluence scoring, risk rules, signal assembly | +| **Job Scheduler** | APScheduler | Pipeline orchestration & timing | +| **Market Data Store** | SQLite (dev) / TimescaleDB (prod) | Persist OHLCV, indicators, signals | +| **Notifier** | Python, Discord Webhook | Format and deliver signals | + +## Deployment + +All containers run as a single **Python process** in development (scheduler + all modules), and can be split into separate Docker containers in production: + +``` +docker/ +├── ingestor/ # Data poller container +├── analyser/ # Analysis worker container +├── signal-gen/ # Signal generation + notifier +└── timescaledb/ # Database container +``` + +The CI pipeline (Gitea Actions) builds images and pushes to the Zot container registry at `lego-cloud.eu:30264`. diff --git a/docs/products/stock-market-pro/c4/context.md b/docs/products/stock-market-pro/c4/context.md new file mode 100644 index 0000000..9d38377 --- /dev/null +++ b/docs/products/stock-market-pro/c4/context.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 1 +--- + +# L1 — System Context + +> **C4 Level 1**: Shows how Stock Market Professional fits into the world — who uses it, and what external systems it depends on. + +:::info C4 Model — Level 1 +The Context diagram answers: *What does this system do, and who / what interacts with it?* +::: + +## Context Diagram + +```mermaid +C4Context + title System Context — Stock Market Professional + + Person(trader, "Trader (Lego)", "Human operator who reviews signals and executes trades manually.") + + System(smp, "Stock Market Professional", "Monitors markets, runs technical analysis, and emits daily trading signals.") + + System_Ext(marketData, "Market Data Provider", "Real-time & historical price feeds (Yahoo Finance, Alpha Vantage, Polygon.io)") + System_Ext(discord, "Discord", "Signal delivery channel — daily trading plans posted to a server.") + System_Ext(broker, "Broker / Trading Platform", "Where the trader manually executes trades (e.g. Interactive Brokers, MetaTrader).") + System_Ext(newsApi, "Economic Calendar / News API", "High-impact event feed used to filter signals around news releases.") + + Rel(trader, smp, "Reviews signals and trading plan") + Rel(smp, marketData, "Fetches OHLCV data", "REST / WebSocket") + Rel(smp, discord, "Posts daily plan & alerts", "Webhook / Bot API") + Rel(smp, newsApi, "Queries upcoming events", "REST API") + Rel(trader, broker, "Executes trades manually", "Web UI / API") +``` + +## Actors & Systems + +| Entity | Type | Role | +|---|---|---| +| Trader (Lego) | Person | Reviews daily signal plan, executes trades manually | +| Market Data Provider | External System | Source of truth for price data | +| Discord | External System | Primary delivery channel for signals | +| Broker / Trading Platform | External System | Where actual trades happen (out of scope) | +| Economic Calendar API | External System | News filter to avoid high-impact event windows | + +## Key Constraints + +- **No auto-trading** — all order execution is manual +- **Signal limit** — max **3 signals per day** to enforce quality +- **Hold time** — max **30 minutes** per position (scalping / intraday) +- **Manual review** — trader has final say on all entries diff --git a/docs/products/stock-market-pro/data-monitoring/index.md b/docs/products/stock-market-pro/data-monitoring/index.md new file mode 100644 index 0000000..4ced778 --- /dev/null +++ b/docs/products/stock-market-pro/data-monitoring/index.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 2 +--- + +# Data Monitoring + +## Overview + +The data monitoring module is responsible for acquiring, normalizing, and storing market data that feeds into the analysis engine. + +## Requirements + +- **Latency**: Near real-time (< 1 minute delay) +- **Coverage**: Forex major pairs + selected stocks/indices +- **Granularity**: 1-minute candles, tick data where available +- **History**: Minimum 30 days rolling window for backtesting +- **Reliability**: Automatic reconnection, data gap detection + +## Watched Instruments + +### Forex Pairs (Initial) +| Pair | Session | Notes | +|---|---|---| +| EUR/USD | London + NY | Most liquid, tight spreads | +| GBP/USD | London + NY | High volatility windows | +| USD/JPY | Tokyo + London | Trend-following opportunities | +| AUD/USD | Sydney + London | Commodity correlation | + +### Indices (Future) +| Index | Session | Notes | +|---|---|---| +| S&P 500 (SPX) | NY | Broad market sentiment | +| NASDAQ 100 (NDX) | NY | Tech sector proxy | + +## Data Pipeline + +``` +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ API Source │────▶│ Normalizer │────▶│ Storage │ +│ │ │ │ │ │ +│ • REST poll │ │ • OHLCV fmt │ │ • SQLite/TS │ +│ • WebSocket │ │ • Timestamps │ │ • Partitioned│ +│ • Fallback │ │ • Validation │ │ • Indexed │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +## Data Schema + +```sql +CREATE TABLE candles ( + id INTEGER PRIMARY KEY, + symbol TEXT NOT NULL, + timeframe TEXT NOT NULL, -- '1m', '5m', '15m', '1h' + timestamp DATETIME NOT NULL, + open REAL NOT NULL, + high REAL NOT NULL, + low REAL NOT NULL, + close REAL NOT NULL, + volume REAL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + UNIQUE(symbol, timeframe, timestamp) +); + +CREATE INDEX idx_candles_symbol_time ON candles(symbol, timeframe, timestamp DESC); +``` + +## Health Checks + +- Data freshness monitoring (alert if > 5 min stale) +- Gap detection and backfill +- Source failover (primary → secondary API) +- Rate limit tracking per API key diff --git a/docs/products/stock-market-pro/index.md b/docs/products/stock-market-pro/index.md new file mode 100644 index 0000000..d2aa759 --- /dev/null +++ b/docs/products/stock-market-pro/index.md @@ -0,0 +1,50 @@ +--- +sidebar_position: 1 +--- + +# Stock Market Professional + +> Autonomous stock/forex monitoring, technical analysis, and signal generation system. + +## Purpose + +Stock Market Professional is designed to: + +1. **Monitor** real-time market data (stocks, forex pairs) +2. **Analyze** price action using technical and statistical methods to spot patterns +3. **Signal** consumers with daily trading plans + +## Trading Strategy Constraints + +| Parameter | Value | +|---|---| +| Max daily purchases | **3** | +| Max hold time | **30 minutes** | +| Execution | **Manual** (signals only — no auto-trading) | +| Market focus | Forex / Short-term stock positions | + +## Architecture Overview + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ Data Ingestion │────▶│ Analysis Engine │────▶│ Signal Generator│ +│ │ │ │ │ │ +│ • Market feeds │ │ • Technical ind. │ │ • Daily plans │ +│ • Price streams │ │ • Pattern recog. │ │ • Entry/Exit │ +│ • Volume data │ │ • Statistical │ │ • Risk mgmt │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + │ + ▼ + ┌─────────────┐ + │ Consumers │ + │ (Discord/ │ + │ Webhook) │ + └─────────────┘ +``` + +## Modules + +- [Architecture & Design](./overview/architecture) +- [Data Monitoring](./data-monitoring/) +- [Technical Analysis](./technical-analysis/) +- [Signal Generation](./signal-generation/) diff --git a/docs/products/stock-market-pro/overview/architecture.md b/docs/products/stock-market-pro/overview/architecture.md new file mode 100644 index 0000000..cc8d364 --- /dev/null +++ b/docs/products/stock-market-pro/overview/architecture.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 1 +--- + +# Architecture & Design + +## System Design + +Stock Market Professional follows a **pipeline architecture** with three distinct stages: + +### 1. Data Layer (Ingestion) + +Responsible for acquiring and normalizing market data from multiple sources. + +**Requirements:** +- Real-time or near-real-time price data (1-minute candles minimum) +- Support for multiple forex pairs and stock tickers +- Historical data for backtesting (minimum 30 days) +- Volume and order book data where available + +**Potential Data Sources:** +- Yahoo Finance API (free tier) +- Alpha Vantage API +- Twelve Data API +- Interactive Brokers TWS API +- Polygon.io + +### 2. Analysis Layer (Processing) + +Applies technical indicators, statistical models, and pattern recognition. + +**Technical Indicators:** +- Moving Averages (SMA, EMA, WMA) +- RSI (Relative Strength Index) +- MACD (Moving Average Convergence Divergence) +- Bollinger Bands +- Fibonacci Retracements +- Volume-Weighted Average Price (VWAP) +- Stochastic Oscillator + +**Pattern Recognition:** +- Candlestick patterns (Doji, Hammer, Engulfing, etc.) +- Chart patterns (Head & Shoulders, Double Top/Bottom, Triangles) +- Support/Resistance levels +- Breakout detection + +**Statistical Analysis:** +- Volatility clustering (GARCH models) +- Mean reversion detection +- Momentum scoring +- Correlation analysis between pairs + +### 3. Signal Layer (Output) + +Generates actionable trading signals with strict constraints. + +**Signal Format:** +```json +{ + "date": "2026-07-16", + "signals": [ + { + "pair": "EUR/USD", + "direction": "BUY", + "entry_price": 1.0892, + "stop_loss": 1.0875, + "take_profit": 1.0920, + "confidence": 0.82, + "reasoning": "Bullish engulfing + RSI oversold bounce + VWAP support", + "time_window": "09:00-09:30 UTC", + "max_hold_minutes": 30 + } + ], + "max_signals_per_day": 3 +} +``` + +## Technology Choices + +| Component | Technology | Reason | +|---|---|---| +| Language | Python 3.11+ | Rich ecosystem for finance/ML | +| Data | pandas, numpy | Industry standard for time series | +| Indicators | ta-lib, pandas-ta | Comprehensive TA libraries | +| ML/Stats | scikit-learn, statsmodels | Pattern recognition, GARCH | +| Scheduling | APScheduler / cron | Regular data pulls and analysis | +| Delivery | Discord webhook / API | Direct consumer notification | +| Storage | SQLite / TimescaleDB | Time-series optimized | + +## Deployment + +- Containerized (Docker) +- Runs on TrueNAS infrastructure +- CI/CD via Gitea Actions +- Health monitoring and alerting diff --git a/docs/products/stock-market-pro/signal-generation/index.md b/docs/products/stock-market-pro/signal-generation/index.md new file mode 100644 index 0000000..9c48ffc --- /dev/null +++ b/docs/products/stock-market-pro/signal-generation/index.md @@ -0,0 +1,130 @@ +--- +sidebar_position: 4 +--- + +# Signal Generation + +## Overview + +The signal generation module produces actionable trading plans delivered to consumers daily. Signals are constrained by strict risk management rules. + +## Constraints + +| Rule | Value | Rationale | +|---|---|---| +| Max signals per day | **3** | Quality over quantity | +| Max hold time | **30 minutes** | Scalping/day-trade focus | +| Execution | **Manual** | Human confirms and executes | +| Min confluence score | **0.65** | High-probability setups only | + +## Signal Structure + +Each signal contains: + +```typescript +interface TradingSignal { + // Identification + id: string; + timestamp: string; + pair: string; + + // Direction + direction: 'BUY' | 'SELL'; + + // Levels + entry_price: number; + stop_loss: number; + take_profit: number; + + // Meta + confidence: number; // 0.0 - 1.0 + reasoning: string[]; // List of confirming factors + time_window: string; // Optimal entry window + max_hold_minutes: number; // Always ≤ 30 + + // Risk + risk_reward_ratio: number; // Minimum 1:1.5 + position_size_pct: number; // % of capital suggested +} +``` + +## Daily Plan Format + +Delivered each trading day before market open: + +```markdown +## 📊 Daily Trading Plan — 2026-07-16 + +### Market Bias +- EUR/USD: Bullish (1H EMA trend up) +- GBP/USD: Neutral (ranging) + +### Signals (Max 3) + +#### Signal 1: EUR/USD BUY +- **Entry**: 1.0892 (on pullback to VWAP) +- **Stop Loss**: 1.0875 (-17 pips) +- **Take Profit**: 1.0920 (+28 pips) +- **R:R**: 1:1.65 +- **Window**: 09:00-09:30 UTC +- **Confidence**: 82% +- **Reasoning**: + - Bullish engulfing on 5m + - RSI bouncing from 35 + - VWAP support holding + - London session momentum + +#### Signal 2: ... + +### Key Levels to Watch +- EUR/USD: Support 1.0870, Resistance 1.0935 +- GBP/USD: Support 1.2640, Resistance 1.2710 + +### Risk Notes +- ⚠️ NFP data release at 13:30 UTC — avoid new positions 30min before +- Volume expected to increase London/NY overlap (13:00-16:00 UTC) +``` + +## Delivery Channels + +| Channel | Method | Timing | +|---|---|---| +| Discord | Webhook/Bot message | Pre-market (06:00 UTC) | +| API | REST endpoint | On-demand | +| Email | SMTP notification | Optional digest | + +## Signal Validation Rules + +Before a signal is emitted, it must pass: + +1. **Confluence check** — Score ≥ 0.65 +2. **Risk/Reward check** — R:R ≥ 1:1.5 +3. **Time filter** — Within active session hours +4. **Correlation filter** — No conflicting signals on correlated pairs +5. **News filter** — No high-impact events within hold window +6. **Daily limit** — Max 3 signals not exceeded +7. **Spread check** — Current spread within acceptable range + +## Performance Tracking + +Track all signals for continuous improvement: + +```sql +CREATE TABLE signal_performance ( + signal_id TEXT PRIMARY KEY, + pair TEXT, + direction TEXT, + entry_price REAL, + exit_price REAL, + pnl_pips REAL, + hold_time_minutes INTEGER, + outcome TEXT, -- 'TP_HIT', 'SL_HIT', 'TIME_EXIT', 'MANUAL_EXIT' + created_at DATETIME +); +``` + +**Target Metrics:** +- Win rate: > 55% +- Average R:R achieved: > 1.3 +- Max consecutive losses: < 5 +- Monthly Sharpe: > 1.5 diff --git a/docs/products/stock-market-pro/technical-analysis/index.md b/docs/products/stock-market-pro/technical-analysis/index.md new file mode 100644 index 0000000..d35b5c5 --- /dev/null +++ b/docs/products/stock-market-pro/technical-analysis/index.md @@ -0,0 +1,112 @@ +--- +sidebar_position: 3 +--- + +# Technical Analysis + +## Overview + +The technical analysis module processes raw market data through multiple analytical lenses to identify high-probability trading setups within the 30-minute hold constraint. + +## Analysis Categories + +### 1. Trend Indicators + +| Indicator | Parameters | Signal | +|---|---|---| +| EMA Cross | 9/21 periods | Bullish/Bearish cross | +| MACD | 12/26/9 | Histogram divergence | +| ADX | 14 periods | Trend strength > 25 | +| Parabolic SAR | 0.02/0.2 | Trend reversal dots | + +### 2. Momentum Indicators + +| Indicator | Parameters | Signal | +|---|---|---| +| RSI | 14 periods | Oversold < 30, Overbought > 70 | +| Stochastic | 14/3/3 | %K/%D crossover | +| CCI | 20 periods | Extreme readings ±200 | +| Williams %R | 14 periods | Reversal zones | + +### 3. Volatility Indicators + +| Indicator | Parameters | Signal | +|---|---|---| +| Bollinger Bands | 20/2σ | Band squeeze/expansion | +| ATR | 14 periods | Position sizing, stop distance | +| Keltner Channels | 20/1.5 | Breakout confirmation | + +### 4. Volume Analysis + +| Indicator | Parameters | Signal | +|---|---|---| +| VWAP | Session | Price vs fair value | +| OBV | Cumulative | Divergence from price | +| Volume Profile | Session | Key levels, POC | + +## Pattern Recognition + +### Candlestick Patterns (1-5 bar) +- **Reversal**: Hammer, Shooting Star, Engulfing, Morning/Evening Star +- **Continuation**: Three White Soldiers, Rising/Falling Three Methods +- **Indecision**: Doji, Spinning Top, Harami + +### Chart Patterns (Multi-bar) +- Double Top/Bottom +- Head & Shoulders +- Ascending/Descending Triangles +- Bull/Bear Flags +- Wedges + +## Confluence Scoring + +Signals are scored based on **confluence** — multiple indicators agreeing: + +```python +def calculate_confluence_score(signals: list[IndicatorSignal]) -> float: + """ + Score 0.0 - 1.0 based on how many indicators agree. + + Weights: + - Trend alignment: 0.30 + - Momentum confirmation: 0.25 + - Volume confirmation: 0.20 + - Pattern match: 0.15 + - Key level proximity: 0.10 + """ + weights = { + 'trend': 0.30, + 'momentum': 0.25, + 'volume': 0.20, + 'pattern': 0.15, + 'level': 0.10 + } + + score = sum( + weights[s.category] * s.strength + for s in signals + if s.direction == consensus_direction(signals) + ) + return min(score, 1.0) +``` + +**Minimum confluence threshold for signal generation: 0.65** + +## Timeframe Analysis + +Given the 30-minute max hold constraint: + +| Timeframe | Purpose | +|---|---| +| 1-minute | Entry timing, micro-structure | +| 5-minute | Primary signal generation | +| 15-minute | Trend context, S/R levels | +| 1-hour | Bias direction (trend filter) | + +## Backtesting Requirements + +- Minimum 30 days historical data +- Walk-forward optimization +- Out-of-sample validation +- Maximum drawdown tracking +- Sharpe ratio > 1.5 target diff --git a/docs/runbooks/index.md b/docs/runbooks/index.md new file mode 100644 index 0000000..a6f027d --- /dev/null +++ b/docs/runbooks/index.md @@ -0,0 +1,61 @@ +--- +sidebar_position: 1 +--- + +# Runbooks + +Operational procedures for SKIC Playground services. + +:::note +Runbooks are living documents. Update them when procedures change or new issues are discovered. +::: + +## Incident Severity + +| Level | Definition | Response Time | +|---|---|---| +| P0 | Platform down / all services unreachable | Immediate | +| P1 | Single critical service down | < 15 min | +| P2 | Degraded performance / non-critical service down | < 1 hour | +| P3 | Minor issue / cosmetic | Next working session | + +## Common Procedures + +### Restart a container + +```bash +# SSH to TrueNAS +ssh root@lego-cloud.eu + +# List running containers +docker ps + +# Restart +docker restart + +# Check logs +docker logs -f --tail 100 +``` + +### Check Gitea Actions runner status + +```bash +# On the runner host +systemctl status gitea-runner + +# Restart if needed +systemctl restart gitea-runner +``` + +### Force re-deploy from latest image + +```bash +docker pull /: +docker stop +docker rm +docker run -d --name ... : +``` + +## Application-specific runbooks + +Individual application runbooks will be added here as applications go into production. diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 65ec2da..5622bcb 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -32,6 +32,16 @@ const config: Config = { plugins: [ 'docusaurus-plugin-drawio', + async function tailwindPlugin() { + return { + name: 'docusaurus-tailwindcss', + configurePostCSS(postcssOptions: { plugins: unknown[] }) { + postcssOptions.plugins.push(require('tailwindcss')); + postcssOptions.plugins.push(require('autoprefixer')); + return postcssOptions; + }, + }; + }, ], presets: [ diff --git a/package.json b/package.json index 4983a99..e6109c8 100644 --- a/package.json +++ b/package.json @@ -18,20 +18,28 @@ "@docusaurus/core": "3.10.2", "@docusaurus/faster": "3.10.2", "@docusaurus/preset-classic": "3.10.2", + "@docusaurus/theme-common": "^3.10.2", "@docusaurus/theme-mermaid": "^3.10.2", "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", + "@radix-ui/react-slot": "^1.3.0", + "@radix-ui/react-tooltip": "^1.2.12", + "clsx": "^2.1.1", "docusaurus-plugin-drawio": "^0.4.0", "prism-react-renderer": "^2.3.0", "raw-loader": "^4.0.2", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "server-only": "^0.0.1", + "tailwind-merge": "^3.6.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/tsconfig": "3.10.2", "@docusaurus/types": "3.10.2", "@types/react": "^19.0.0", + "autoprefixer": "^10.5.3", + "postcss": "^8.5.19", + "tailwindcss": "^4.3.2", "typescript": "~6.0.2" }, "browserslist": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be20d4c..2bedb3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,14 +17,23 @@ importers: '@docusaurus/preset-classic': specifier: 3.10.2 version: 3.10.2(@algolia/client-search@5.56.0)(@docusaurus/faster@3.10.2(@docusaurus/types@3.10.2(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(postcss@8.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.7))(@rspack/core@1.7.12)(@swc/core@1.15.43)(@types/react@19.2.17)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(search-insights@2.17.3)(typescript@6.0.3) + '@docusaurus/theme-common': + specifier: ^3.10.2 + version: 3.10.2(@docusaurus/plugin-content-docs@3.10.2(@docusaurus/faster@3.10.2(@docusaurus/types@3.10.2(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(postcss@8.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.7))(@rspack/core@1.7.12)(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@docusaurus/theme-mermaid': specifier: ^3.10.2 version: 3.10.2(@docusaurus/faster@3.10.2(@docusaurus/types@3.10.2(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(postcss@8.5.19))(@docusaurus/plugin-content-docs@3.10.2(@docusaurus/faster@3.10.2(@docusaurus/types@3.10.2(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(postcss@8.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.7))(@rspack/core@1.7.12)(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3))(@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.7))(@rspack/core@1.7.12)(@swc/core@1.15.43)(postcss@8.5.19)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) '@mdx-js/react': specifier: ^3.0.0 version: 3.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-slot': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-tooltip': + specifier: ^1.2.12 + version: 1.2.12(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) clsx: - specifier: ^2.0.0 + specifier: ^2.1.1 version: 2.1.1 docusaurus-plugin-drawio: specifier: ^0.4.0 @@ -41,6 +50,12 @@ importers: react-dom: specifier: ^19.0.0 version: 19.2.7(react@19.2.7) + server-only: + specifier: ^0.0.1 + version: 0.0.1 + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 devDependencies: '@docusaurus/module-type-aliases': specifier: 3.10.2 @@ -54,6 +69,15 @@ importers: '@types/react': specifier: ^19.0.0 version: 19.2.17 + autoprefixer: + specifier: ^10.5.3 + version: 10.5.3(postcss@8.5.19) + postcss: + specifier: ^8.5.19 + version: 8.5.19 + tailwindcss: + specifier: ^4.3.2 + version: 4.3.2 typescript: specifier: ~6.0.2 version: 6.0.3 @@ -1274,6 +1298,21 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -1537,6 +1576,206 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@radix-ui/primitive@1.1.5': + resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==} + + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.2.0': + resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.15': + resolution: {integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popper@1.3.3': + resolution: {integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.7': + resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tooltip@1.2.12': + resolution: {integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + '@rspack/binding-darwin-arm64@1.7.12': resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==} cpu: [arm64] @@ -5276,6 +5515,9 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -5481,6 +5723,12 @@ packages: '@swc/core': ^1.2.147 webpack: '>=2' + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.2: + resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} + tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} @@ -8234,6 +8482,23 @@ snapshots: tslib: 2.8.1 optional: true + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.8.0 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.12': {} + '@hapi/hoek@9.3.0': {} '@hapi/topo@5.1.0': @@ -8609,6 +8874,166 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@radix-ui/primitive@1.1.5': {} + + '@radix-ui/react-arrow@1.1.11(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.15(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-popper@1.3.3(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-portal@1.1.13(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-presence@1.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-primitive@2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-tooltip@1.2.12(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.7(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/rect@1.1.2': {} + '@rspack/binding-darwin-arm64@1.7.12': optional: true @@ -12910,6 +13335,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-only@0.0.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -13134,6 +13561,10 @@ snapshots: '@swc/counter': 0.1.3 webpack: 5.108.4(@swc/core@1.15.43)(postcss@8.5.19) + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.2: {} + tapable@2.3.3: {} terser-webpack-plugin@5.6.1(@swc/core@1.15.43)(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.19))(html-minifier-terser@7.2.0)(postcss@8.5.19)(webpack@5.108.4(@swc/core@1.15.43)(postcss@8.5.19)): diff --git a/sidebars.ts b/sidebars.ts index 2d70fd8..462dcfb 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -2,50 +2,75 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; const sidebars: SidebarsConfig = { docsSidebar: [ + /* ── Introduction ────────────────────────────────────── */ { type: 'doc', id: 'intro/index', - label: '← Overview', + label: 'Introduction', }, + + /* ── Platform ────────────────────────────────────────── */ { type: 'category', - label: 'Applications', + label: 'Platform', + collapsed: false, + items: [ + 'platform/overview', + 'platform/cicd', + ], + }, + + /* ── Products ────────────────────────────────────────── */ + { + type: 'category', + label: 'Products', collapsed: false, items: [ { type: 'category', - label: '📈 Stock Market Pro', - link: { type: 'doc', id: 'applications/stock-market-professional/index' }, + label: 'Stock Market Pro', + link: { type: 'doc', id: 'products/stock-market-pro/index' }, items: [ { type: 'category', - label: 'C4 Architecture', - collapsed: false, + label: 'Architecture (C4)', items: [ - 'applications/stock-market-professional/c4/context', - 'applications/stock-market-professional/c4/containers', - 'applications/stock-market-professional/c4/components', + 'products/stock-market-pro/c4/context', + 'products/stock-market-pro/c4/containers', + 'products/stock-market-pro/c4/components', ], }, { type: 'category', label: 'Modules', items: [ - 'applications/stock-market-professional/data-monitoring/index', - 'applications/stock-market-professional/technical-analysis/index', - 'applications/stock-market-professional/signal-generation/index', + 'products/stock-market-pro/data-monitoring/index', + 'products/stock-market-pro/technical-analysis/index', + 'products/stock-market-pro/signal-generation/index', ], }, ], }, ], }, + + /* ── Engineering ─────────────────────────────────────── */ { type: 'category', - label: 'Platform', + label: 'Engineering', + collapsed: false, items: [ - 'architecture/overview', - 'guides/getting-started', + 'engineering/guidelines', + ], + }, + + /* ── Runbooks ────────────────────────────────────────── */ + { + type: 'category', + label: 'Runbooks', + collapsed: true, + items: [ + 'runbooks/index', ], }, ], diff --git a/src/components/ui/index.tsx b/src/components/ui/index.tsx new file mode 100644 index 0000000..2add7e7 --- /dev/null +++ b/src/components/ui/index.tsx @@ -0,0 +1,156 @@ +import * as React from 'react'; +import { cn } from '@site/src/lib/utils'; + +/* ── Badge ───────────────────────────────────────────────── */ +type BadgeVariant = 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning' | 'wip' | 'stable' | 'planned'; + +interface BadgeProps extends React.HTMLAttributes { + variant?: BadgeVariant; +} + +const badgeVariants: Record = { + default: 'bg-primary text-primary-foreground', + secondary: 'bg-secondary text-secondary-foreground', + outline: 'border border-border text-foreground', + destructive: 'bg-destructive text-destructive-foreground', + success: 'border border-emerald-500 text-emerald-500 bg-emerald-500/8', + warning: 'border border-amber-500 text-amber-500 bg-amber-500/8', + wip: 'border border-amber-500 text-amber-500 bg-amber-500/8', + stable: 'border border-emerald-500 text-emerald-500 bg-emerald-500/8', + planned: 'border border-border text-muted-foreground', +}; + +export function Badge({ variant = 'default', className, children, ...props }: BadgeProps) { + return ( + + {children} + + ); +} + +/* ── Card ────────────────────────────────────────────────── */ +export function Card({ className, children, ...props }: React.HTMLAttributes) { + return ( +
+ {children} +
+ ); +} + +export function CardHeader({ className, children, ...props }: React.HTMLAttributes) { + return
{children}
; +} + +export function CardTitle({ className, children, ...props }: React.HTMLAttributes) { + return ( +

+ {children} +

+ ); +} + +export function CardContent({ className, children, ...props }: React.HTMLAttributes) { + return
{children}
; +} + +/* ── Callout ─────────────────────────────────────────────── */ +type CalloutVariant = 'info' | 'warning' | 'danger' | 'success' | 'note'; + +interface CalloutProps { + variant?: CalloutVariant; + title?: string; + children: React.ReactNode; + className?: string; +} + +const calloutStyles: Record = { + note: { border: 'border-border', bg: 'bg-muted/40', title: 'text-foreground', icon: '◆' }, + info: { border: 'border-indigo-500', bg: 'bg-indigo-500/8', title: 'text-indigo-400', icon: 'ℹ' }, + success: { border: 'border-emerald-500',bg: 'bg-emerald-500/8', title: 'text-emerald-400', icon: '✓' }, + warning: { border: 'border-amber-500', bg: 'bg-amber-500/8', title: 'text-amber-400', icon: '⚠' }, + danger: { border: 'border-red-500', bg: 'bg-red-500/8', title: 'text-red-400', icon: '✕' }, +}; + +export function Callout({ variant = 'note', title, children, className }: CalloutProps) { + const s = calloutStyles[variant]; + return ( +
+ {title && ( +
+ {s.icon} + {title} +
+ )} +
{children}
+
+ ); +} + +/* ── PropertiesTable ─────────────────────────────────────── */ +interface Property { + name: string; + type: string; + default?: string; + description: string; +} + +interface PropertiesTableProps { + properties: Property[]; +} + +export function PropertiesTable({ properties }: PropertiesTableProps) { + return ( +
+ + + + + + + + + + + {properties.map((p, i) => ( + + + + + + + ))} + +
NameTypeDefaultDescription
{p.name}{p.type}{p.default ?? '—'}{p.description}
+
+ ); +} + +/* ── StatusIndicator ─────────────────────────────────────── */ +type Status = 'operational' | 'degraded' | 'outage' | 'maintenance'; + +const statusConfig: Record = { + operational: { color: 'bg-emerald-500', label: 'Operational' }, + degraded: { color: 'bg-amber-500', label: 'Degraded' }, + outage: { color: 'bg-red-500', label: 'Outage' }, + maintenance: { color: 'bg-indigo-500', label: 'Maintenance' }, +}; + +export function StatusIndicator({ status }: { status: Status }) { + const { color, label } = statusConfig[status]; + return ( + + + {label} + + ); +} diff --git a/src/css/custom.css b/src/css/custom.css index 768eaf3..240ba93 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1,27 +1,74 @@ /* ============================================================ - SKIC Playground Docs — Modern Dark-First Technical Theme - Inspired by Vercel / Linear design language - Font: Geist (by Vercel) + Geist Mono + SKIC Playground Docs — Dark-first, Sharp, Technical Theme + Stack: Docusaurus + Tailwind (preflight off) + shadcn/ui (New York / Zinc) + Design: no rounded corners, Vercel/Linear aesthetic ============================================================ */ -/* --- Google Fonts / Geist import --- */ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; -/* Geist via CDN */ +/* --- Fonts --- */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); @import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/style.css'); @import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/style.css'); -/* ── Design Tokens ───────────────────────────────────────── */ -:root { - /* Brand */ - --skic-accent: #6366f1; /* indigo-500 */ - --skic-accent-light: #818cf8; /* indigo-400 */ - --skic-accent-dark: #4f46e5; /* indigo-600 */ - --skic-green: #10b981; - --skic-amber: #f59e0b; - --skic-red: #ef4444; +/* ── shadcn/ui CSS variables (New York / Zinc, radius = 0) ── */ +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; + --radius: 0rem; /* SHARP — no rounded corners */ + /* brand accent */ + --skic-accent: 239 84% 67%; + --skic-green: 160 84% 39%; + --skic-amber: 38 92% 50%; + --skic-red: 0 84% 60%; + } - /* Docusaurus overrides — Light mode */ + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + --radius: 0rem; + } +} + +/* ── Docusaurus / Infima token overrides ─────────────────── */ +:root { --ifm-color-primary: #6366f1; --ifm-color-primary-dark: #4f46e5; --ifm-color-primary-darker: #4338ca; @@ -34,7 +81,6 @@ --ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace; --ifm-font-size-base: 15px; --ifm-line-height-base: 1.7; - --ifm-font-weight-normal: 400; --ifm-heading-font-weight: 600; --ifm-heading-letter-spacing: -0.02em; @@ -42,25 +88,27 @@ --ifm-h2-font-size: 1.5rem; --ifm-h3-font-size: 1.2rem; - --ifm-navbar-height: 60px; + --ifm-navbar-height: 56px; --ifm-navbar-shadow: none; + --ifm-navbar-background-color: #0a0a0a; - --ifm-toc-border-color: var(--ifm-color-emphasis-200); + /* SHARP — zero radius everywhere */ + --ifm-global-radius: 0; + --ifm-code-border-radius: 0; + --ifm-pre-border-radius: 0; + --ifm-alert-border-radius: 0; + --ifm-card-border-radius: 0; + --ifm-button-border-radius: 0; + --ifm-pagination-nav-border-radius: 0; + --ifm-badge-border-radius: 0; + --ifm-toc-border-color: #1f1f1f; --ifm-code-font-size: 0.85rem; - --ifm-code-border-radius: 6px; - --ifm-code-padding-horizontal: 0.4em; - --ifm-code-padding-vertical: 0.15em; - --ifm-pre-border-radius: 10px; --ifm-pre-padding: 1.25rem; - --ifm-global-radius: 8px; - --ifm-global-shadow-lw: 0 1px 3px 0 rgba(0,0,0,.08); - --ifm-global-shadow-md: 0 4px 12px 0 rgba(0,0,0,.1); - --docusaurus-highlighted-code-line-bg: rgba(99, 102, 241, 0.08); } -/* ── Dark Mode ───────────────────────────────────────────── */ +/* Dark mode Infima overrides */ [data-theme='dark'] { --ifm-color-primary: #818cf8; --ifm-color-primary-dark: #6366f1; @@ -70,88 +118,96 @@ --ifm-color-primary-lighter: #c7d2fe; --ifm-color-primary-lightest: #e0e7ff; - --ifm-background-color: #0a0a0a; - --ifm-background-surface-color: #111111; - --ifm-navbar-background-color: rgba(10, 10, 10, 0.85); - --ifm-sidebar-background: #0a0a0a; - --ifm-footer-background-color: #0a0a0a; + --ifm-background-color: #0a0a0a; + --ifm-background-surface-color: #111111; + --ifm-navbar-background-color: #0a0a0a; + --ifm-sidebar-background: #0a0a0a; + --ifm-footer-background-color: #0a0a0a; - --ifm-color-content: #ededed; - --ifm-color-content-secondary:#a1a1a1; - --ifm-color-emphasis-100: #1a1a1a; - --ifm-color-emphasis-200: #222222; - --ifm-color-emphasis-300: #333333; - --ifm-color-emphasis-400: #555555; - --ifm-color-emphasis-600: #999999; - --ifm-color-emphasis-700: #cccccc; - --ifm-color-emphasis-900: #ffffff; + --ifm-color-content: #ededed; + --ifm-color-content-secondary: #a1a1a1; + --ifm-color-emphasis-100: #161616; + --ifm-color-emphasis-200: #1f1f1f; + --ifm-color-emphasis-300: #2a2a2a; + --ifm-color-emphasis-400: #444444; + --ifm-color-emphasis-600: #888888; + --ifm-color-emphasis-700: #cccccc; + --ifm-color-emphasis-900: #ffffff; - --ifm-toc-border-color: #222222; - --ifm-hr-border-color: #1f1f1f; - - --ifm-code-background: #161616; - --ifm-pre-background: #111111; - --ifm-blockquote-border-left-color: var(--skic-accent); + --ifm-hr-border-color: #1f1f1f; + --ifm-code-background: #161616; + --ifm-pre-background: #111111; --docusaurus-highlighted-code-line-bg: rgba(129, 140, 248, 0.12); } -/* ── Typography ──────────────────────────────────────────── */ -h1, h2, h3, h4, h5, h6 { - letter-spacing: -0.02em; - font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; -} - -h1 { font-size: 2.25rem; font-weight: 700; } -h2 { font-size: 1.5rem; border-bottom: 1px solid var(--ifm-color-emphasis-200); padding-bottom: 0.4rem; } -h3 { font-size: 1.15rem; } - -p { color: var(--ifm-color-content); } - -/* ── Navbar ──────────────────────────────────────────────── */ +/* ── Force dark navbar in BOTH modes ─────────────────────── */ +/* This is the key fix — lock navbar dark regardless of color scheme */ .navbar { - border-bottom: 1px solid var(--ifm-color-emphasis-200); + background-color: #0a0a0a !important; + border-bottom: 1px solid #1f1f1f !important; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); -} - -[data-theme='dark'] .navbar { - background: rgba(10, 10, 10, 0.85); + box-shadow: none !important; } .navbar__title { font-weight: 700; - font-size: 1rem; + font-size: 0.95rem; letter-spacing: -0.02em; -} - -.navbar__logo { - height: 24px; + color: #ededed !important; } .navbar__link { font-size: 0.875rem; font-weight: 500; - color: var(--ifm-color-content-secondary); - transition: color 0.15s ease; + color: #a1a1a1 !important; + transition: color 0.12s ease; + border-radius: 0 !important; } .navbar__link:hover, .navbar__link--active { - color: var(--ifm-color-content) !important; + color: #ededed !important; + background: transparent !important; +} + +.navbar__logo { height: 22px; } + +.navbar-sidebar { + background-color: #0a0a0a !important; +} + +.navbar-sidebar__brand { + background-color: #0a0a0a !important; + border-bottom: 1px solid #1f1f1f !important; +} + +/* ── Typography ──────────────────────────────────────────── */ +h1, h2, h3, h4, h5, h6 { + letter-spacing: -0.02em; +} + +h2 { + border-bottom: 1px solid var(--ifm-color-emphasis-200); + padding-bottom: 0.35rem; } /* ── Sidebar ─────────────────────────────────────────────── */ .theme-doc-sidebar-container { - border-right: 1px solid var(--ifm-color-emphasis-200) !important; + border-right: 1px solid #1f1f1f !important; +} + +[data-theme='dark'] .theme-doc-sidebar-container { + background-color: #0a0a0a; } .menu__link { font-size: 0.875rem; - border-radius: 6px; - padding: 0.35rem 0.75rem; + border-radius: 0 !important; + padding: 0.3rem 0.75rem; color: var(--ifm-color-content-secondary); - transition: all 0.15s ease; + transition: all 0.12s ease; } .menu__link:hover { @@ -163,40 +219,43 @@ p { color: var(--ifm-color-content); } background: var(--ifm-color-emphasis-100) !important; color: var(--ifm-color-primary) !important; font-weight: 600; + border-left: 2px solid var(--ifm-color-primary); } -.menu__caret::before { - background: var(--ifm-color-content-secondary); -} +.menu__caret { border-radius: 0 !important; } -/* ── Code Blocks ─────────────────────────────────────────── */ +/* ── Code blocks ─────────────────────────────────────────── */ code { font-size: 0.84em; border: 1px solid var(--ifm-color-emphasis-200); background: var(--ifm-color-emphasis-100); - border-radius: 5px; + border-radius: 0 !important; } .prism-code { font-size: 0.84rem; line-height: 1.6; + border-radius: 0 !important; } -.codeBlockContainer_node_modules-\@docusaurus-theme-classic-lib-theme-CodeBlock-Container-styles-module { +div[class*='codeBlockContainer'] { border: 1px solid var(--ifm-color-emphasis-200); + box-shadow: none !important; + border-radius: 0 !important; } -div[class*="codeBlockContainer"] { - border: 1px solid var(--ifm-color-emphasis-200); - box-shadow: none; -} - -div[class*="codeBlockTitle"] { +div[class*='codeBlockTitle'] { background: var(--ifm-color-emphasis-100); border-bottom: 1px solid var(--ifm-color-emphasis-200); font-size: 0.78rem; font-family: var(--ifm-font-family-monospace); color: var(--ifm-color-content-secondary); + border-radius: 0 !important; +} + +div[class*='copyButton'], +div[class*='codeButton'] { + border-radius: 0 !important; } /* ── Tables ──────────────────────────────────────────────── */ @@ -205,55 +264,45 @@ table { width: 100%; font-size: 0.875rem; border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 8px; + border-radius: 0 !important; overflow: hidden; } -thead { - background: var(--ifm-color-emphasis-100); -} +thead { background: var(--ifm-color-emphasis-100); } th { - font-size: 0.75rem; + font-size: 0.72rem; font-weight: 600; text-transform: uppercase; - letter-spacing: 0.06em; + letter-spacing: 0.07em; color: var(--ifm-color-content-secondary); - padding: 0.75rem 1rem; + padding: 0.65rem 1rem; border-bottom: 1px solid var(--ifm-color-emphasis-200); } td { - padding: 0.7rem 1rem; + padding: 0.65rem 1rem; border-bottom: 1px solid var(--ifm-color-emphasis-100); } tr:last-child td { border-bottom: none; } -tbody tr:hover { - background: var(--ifm-color-emphasis-100); -} +tbody tr:hover { background: var(--ifm-color-emphasis-100); } -/* ── Admonitions (callouts) ──────────────────────────────── */ +/* ── Admonitions ─────────────────────────────────────────── */ .admonition { - border-radius: 8px; + border-radius: 0 !important; border: 1px solid; - border-left-width: 3px; + border-left-width: 2px; font-size: 0.9rem; } .admonition-note { border-color: var(--ifm-color-emphasis-300); background: var(--ifm-color-emphasis-100); } -.admonition-tip { border-color: var(--skic-green); background: rgba(16, 185, 129, 0.06); } -.admonition-info { border-color: var(--skic-accent); background: rgba(99, 102, 241, 0.06); } -.admonition-warning { border-color: var(--skic-amber); background: rgba(245, 158, 11, 0.06); } -.admonition-danger { border-color: var(--skic-red); background: rgba(239, 68, 68, 0.06); } - -.admonition-heading h5 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; } - -/* ── Breadcrumbs ─────────────────────────────────────────── */ -.breadcrumbs__link { - font-size: 0.8rem; -} +.admonition-tip { border-color: #10b981; background: rgba(16,185,129,0.05); } +.admonition-info { border-color: #6366f1; background: rgba(99,102,241,0.05); } +.admonition-warning { border-color: #f59e0b; background: rgba(245,158,11,0.05); } +.admonition-danger { border-color: #ef4444; background: rgba(239,68,68,0.05); } +.admonition-heading h5 { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em; } /* ── TOC ─────────────────────────────────────────────────── */ .table-of-contents__link { @@ -261,15 +310,13 @@ tbody tr:hover { color: var(--ifm-color-content-secondary); } .table-of-contents__link:hover, -.table-of-contents__link--active { - color: var(--ifm-color-primary); -} +.table-of-contents__link--active { color: var(--ifm-color-primary); } /* ── Pagination ──────────────────────────────────────────── */ .pagination-nav__link { border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 8px; - transition: all 0.15s ease; + border-radius: 0 !important; + transition: all 0.12s ease; } .pagination-nav__link:hover { border-color: var(--ifm-color-primary); @@ -278,12 +325,12 @@ tbody tr:hover { /* ── Footer ──────────────────────────────────────────────── */ .footer { - border-top: 1px solid var(--ifm-color-emphasis-200); - padding: 2rem 0; + border-top: 1px solid #1f1f1f; + background-color: #0a0a0a !important; } .footer__title { - font-size: 0.75rem; + font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; @@ -295,66 +342,25 @@ tbody tr:hover { color: var(--ifm-color-content-secondary); } -/* ── Doc article ─────────────────────────────────────────── */ -.markdown { - --ifm-heading-margin-top: 1.75rem; +/* ── Buttons ─────────────────────────────────────────────── */ +.button { + border-radius: 0 !important; } +/* ── Doc content ─────────────────────────────────────────── */ +.markdown { --ifm-heading-margin-top: 1.75rem; } .markdown > h2:first-child { margin-top: 0; } -/* ── Hero (homepage) ─────────────────────────────────────── */ -.hero--primary { - background: linear-gradient(135deg, #0a0a0a 0%, #111827 50%, #0a0a0a 100%); - position: relative; - overflow: hidden; -} - -.hero--primary::before { - content: ''; - position: absolute; - inset: 0; - background: radial-gradient(ellipse at 60% 40%, rgba(99,102,241,0.15) 0%, transparent 65%); - pointer-events: none; -} - -.hero__title { - font-size: 3rem; - font-weight: 800; - letter-spacing: -0.04em; - background: linear-gradient(135deg, #ffffff 0%, #a5b4fc 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.hero__subtitle { - color: #a1a1a1; - font-size: 1.1rem; - font-weight: 400; -} - -/* ── Badge component ─────────────────────────────────────── */ -.badge--status { - display: inline-flex; - align-items: center; - gap: 0.35rem; - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - padding: 0.2rem 0.6rem; - border-radius: 100px; - border: 1px solid; -} -.badge--wip { color: var(--skic-amber); border-color: var(--skic-amber); background: rgba(245,158,11,0.08); } -.badge--stable { color: var(--skic-green); border-color: var(--skic-green); background: rgba(16,185,129,0.08); } -.badge--planned { color: var(--ifm-color-content-secondary); border-color: var(--ifm-color-emphasis-300); background: transparent; } - -/* ── Scrollbar (webkit) ──────────────────────────────────── */ -[data-theme='dark'] ::-webkit-scrollbar { width: 6px; height: 6px; } -[data-theme='dark'] ::-webkit-scrollbar-track { background: transparent; } -[data-theme='dark'] ::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; } -[data-theme='dark'] ::-webkit-scrollbar-thumb:hover { background: #555; } +/* ── Scrollbar ───────────────────────────────────────────── */ +::-webkit-scrollbar { width: 5px; height: 5px; } +::-webkit-scrollbar-track { background: transparent; } +::-webkit-scrollbar-thumb { background: #2a2a2a; } +::-webkit-scrollbar-thumb:hover { background: #444; } /* ── Selection ───────────────────────────────────────────── */ ::selection { background: rgba(99,102,241,0.25); } + +/* ── Announcement bar ────────────────────────────────────── */ +div[class*='announcementBar'] { + border-radius: 0 !important; +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 90fe5ed..2177786 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -20,7 +20,7 @@ const applications: Application[] = [ name: 'Stock Market Professional', description: 'Monitors forex & stock markets, runs technical analysis to spot patterns, and emits daily trading signals with max 3 positions / 30-minute hold.', status: 'wip', - link: '/docs/applications/stock-market-professional', + link: '/docs/products/stock-market-pro', tags: ['Python', 'Forex', 'TA', 'Signals'], }, ]; diff --git a/src/theme/Layout/index.tsx b/src/theme/Layout/index.tsx new file mode 100644 index 0000000..1f38a61 --- /dev/null +++ b/src/theme/Layout/index.tsx @@ -0,0 +1,27 @@ +import React, { useEffect } from 'react'; +import Layout from '@theme-original/Layout'; +import type { Props } from '@theme/Layout'; +import { useColorMode } from '@docusaurus/theme-common'; + +/** + * ThemeSynchronizer: bridges Docusaurus's data-theme="dark" attribute + * with shadcn/ui's required `.dark` CSS class on . + */ +function ThemeSynchronizer(): null { + const { colorMode } = useColorMode(); + useEffect(() => { + document.documentElement.classList.toggle('dark', colorMode === 'dark'); + }, [colorMode]); + return null; +} + +export default function LayoutWrapper(props: Props): React.JSX.Element { + return ( + <> + + + {props.children} + + + ); +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..e9d8ef0 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,57 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './src/**/*.{js,jsx,ts,tsx,mdx}', + './docs/**/*.{md,mdx}', + ], + darkMode: ['class'], // ThemeSynchronizer bridges Docusaurus data-theme → .dark class + theme: { + extend: { + fontFamily: { + sans: ['Geist', 'Inter', 'system-ui', 'sans-serif'], + mono: ['Geist Mono', 'Fira Code', 'monospace'], + }, + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + // All set to 0 — Lego wants sharp corners everywhere + lg: '0px', + md: '0px', + sm: '0px', + none: '0px', + }, + }, + }, + corePlugins: { + preflight: false, // MUST be false — prevents fighting Docusaurus Infima CSS + }, +}; diff --git a/tsconfig.json b/tsconfig.json index 405d777..203bb1f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "compilerOptions": { "baseUrl": ".", "ignoreDeprecations": "6.0", - "strict": true + "strict": true, + "jsx": "react-jsx" }, "exclude": [".docusaurus", "build"] }