feat: Sharp style, Tailwind+shadcn, dark navbar, nav restructure, Go/monorepo guidelines
Build and Deploy Documentation / build (push) Failing after 9s

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)
This commit is contained in:
2026-07-16 20:58:07 +00:00
parent 674ec348ce
commit 464ea75730
26 changed files with 2076 additions and 217 deletions
+1
View File
@@ -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"}
+159
View File
@@ -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/<app-name>/`
2. Initialize Go module: `go mod init gitea.lego-cloud.eu/skic-v1-playground/<app-name>`
3. Add to `go.work`: `use ./<app-name>`
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/<app-name>/` following C4 structure
7. Add to homepage application table
+35 -18
View File
@@ -3,29 +3,46 @@ sidebar_position: 1
slug: /intro 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 <div className="grid grid-cols-1 gap-3 my-6">
- **Architecture** — Technical design decisions and system diagrams <Card>
- **Configuration** — How to set up and configure <CardHeader>
- **Operations** — How the application runs day-to-day <div className="flex items-center justify-between">
<CardTitle>📈 Stock Market Professional</CardTitle>
<Badge variant="wip">In Development</Badge>
</div>
</CardHeader>
<CardContent>
Monitors forex & stock markets, applies technical analysis to spot patterns, and emits daily trading signals (max 3 positions, 30-minute hold, manual execution).
</CardContent>
</Card>
</div>
## Applications ## How this documentation is structured
| Application | Status | Description | | Section | Contents |
|---|---|---| |---|---|
| [Stock Market Professional](../applications/stock-market-professional/index.md) | 🚧 In Development | Forex/stock monitoring, technical analysis, and signal generation | | **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 | Layer | Technology |
- **CI/CD**: Gitea Actions |---|---|
- **Documentation**: Docusaurus + pnpm | Infrastructure | TrueNAS SCALE, Docker, Gitea |
- **Languages**: TypeScript, Python | Auth | Keycloak (SSO) |
- **AI/ML**: Pattern recognition, technical analysis algorithms | 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) |
+65
View File
@@ -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.
+43
View File
@@ -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.
+94
View File
@@ -0,0 +1,94 @@
<mxfile host="Electron" modified="2026-07-16T00:00:00.000Z" agent="Jarvis" version="24.0.0" type="device">
<diagram id="smp-pipeline" name="SMP Data Pipeline">
<mxGraphModel dx="1422" dy="762" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- Title -->
<mxCell id="title" value="Stock Market Professional — Data Pipeline" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;fontColor=#6366f1;" vertex="1" parent="1">
<mxGeometry x="200" y="20" width="760" height="40" as="geometry" />
</mxCell>
<!-- Market Data APIs -->
<mxCell id="api" value="Market Data APIs&#xa;(Yahoo / Alpha Vantage / Polygon)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#1a1a2e;strokeColor=#6366f1;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="80" y="120" width="200" height="60" as="geometry" />
</mxCell>
<!-- Data Ingestor -->
<mxCell id="ingestor" value="Data Ingestor&#xa;Python / APScheduler" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#111827;strokeColor=#818cf8;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="360" y="120" width="200" height="60" as="geometry" />
</mxCell>
<!-- TimescaleDB -->
<mxCell id="tsdb" value="Market Data Store&#xa;SQLite / TimescaleDB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#0f172a;strokeColor=#6366f1;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="640" y="110" width="160" height="80" as="geometry" />
</mxCell>
<!-- Analysis Engine -->
<mxCell id="analyser" value="Analysis Engine&#xa;pandas-ta / ta-lib" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#111827;strokeColor=#818cf8;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="360" y="260" width="200" height="60" as="geometry" />
</mxCell>
<!-- Signal Generator -->
<mxCell id="signalgen" value="Signal Generator&#xa;Max 3/day · 30min hold" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#1e1b4b;strokeColor=#818cf8;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="360" y="400" width="200" height="60" as="geometry" />
</mxCell>
<!-- News API -->
<mxCell id="news" value="Economic Calendar&#xa;Forex Factory / Investing.com" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#1a1a2e;strokeColor=#f59e0b;fontColor=#ffffff;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="80" y="400" width="200" height="60" as="geometry" />
</mxCell>
<!-- Notifier -->
<mxCell id="notifier" value="Notifier&#xa;Discord Webhook" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#111827;strokeColor=#818cf8;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="640" y="400" width="160" height="60" as="geometry" />
</mxCell>
<!-- Discord -->
<mxCell id="discord" value="Discord&#xa;Daily Trading Plan" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#1a1a2e;strokeColor=#6366f1;fontColor=#ffffff;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="880" y="400" width="160" height="60" as="geometry" />
</mxCell>
<!-- Trader -->
<mxCell id="trader" value="👤 Trader&#xa;Manual Execution" style="ellipse;whiteSpace=wrap;html=1;fillColor=#0f172a;strokeColor=#10b981;fontColor=#ffffff;fontSize=11;fontStyle=1;" vertex="1" parent="1">
<mxGeometry x="893" y="540" width="140" height="60" as="geometry" />
</mxCell>
<!-- Arrows -->
<mxCell id="e1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;exitX=1;exitY=0.5;entryX=0;entryY=0.5;strokeColor=#6366f1;fontColor=#a5b4fc;fontSize=10;" edge="1" source="api" target="ingestor" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e1l" value="OHLCV" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="e1"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#818cf8;fontSize=10;" edge="1" source="ingestor" target="tsdb" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e2l" value="write candles" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e2"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#818cf8;fontSize=10;" edge="1" source="tsdb" target="analyser" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e3l" value="read candles" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e3"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#818cf8;fontSize=10;" edge="1" source="analyser" target="signalgen" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e4l" value="scored snapshots" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e4"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#f59e0b;fontSize=10;" edge="1" source="news" target="signalgen" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e5l" value="news filter" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e5"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#818cf8;fontSize=10;" edge="1" source="signalgen" target="notifier" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e6l" value="≤3 signals" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e6"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#6366f1;fontSize=10;" edge="1" source="notifier" target="discord" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e7l" value="daily plan" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e7"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
<mxCell id="e8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;strokeColor=#10b981;fontSize=10;" edge="1" source="discord" target="trader" parent="1">
<mxGeometry relative="1" as="geometry" />
<mxCell id="e8l" value="reads signals" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;fontSize=10;" vertex="1" connectable="0" parent="e8"><mxGeometry relative="0.5" as="geometry"/></mxCell>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
@@ -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
```
@@ -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`.
@@ -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
@@ -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
+50
View File
@@ -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/)
@@ -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
@@ -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
@@ -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
+61
View File
@@ -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 <container-name>
# Check logs
docker logs -f --tail 100 <container-name>
```
### 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 <registry>/<image>:<tag>
docker stop <container>
docker rm <container>
docker run -d --name <container> ... <image>:<tag>
```
## Application-specific runbooks
Individual application runbooks will be added here as applications go into production.
+10
View File
@@ -32,6 +32,16 @@ const config: Config = {
plugins: [ plugins: [
'docusaurus-plugin-drawio', '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: [ presets: [
+10 -2
View File
@@ -18,20 +18,28 @@
"@docusaurus/core": "3.10.2", "@docusaurus/core": "3.10.2",
"@docusaurus/faster": "3.10.2", "@docusaurus/faster": "3.10.2",
"@docusaurus/preset-classic": "3.10.2", "@docusaurus/preset-classic": "3.10.2",
"@docusaurus/theme-common": "^3.10.2",
"@docusaurus/theme-mermaid": "^3.10.2", "@docusaurus/theme-mermaid": "^3.10.2",
"@mdx-js/react": "^3.0.0", "@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", "docusaurus-plugin-drawio": "^0.4.0",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"react": "^19.0.0", "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": { "devDependencies": {
"@docusaurus/module-type-aliases": "3.10.2", "@docusaurus/module-type-aliases": "3.10.2",
"@docusaurus/tsconfig": "3.10.2", "@docusaurus/tsconfig": "3.10.2",
"@docusaurus/types": "3.10.2", "@docusaurus/types": "3.10.2",
"@types/react": "^19.0.0", "@types/react": "^19.0.0",
"autoprefixer": "^10.5.3",
"postcss": "^8.5.19",
"tailwindcss": "^4.3.2",
"typescript": "~6.0.2" "typescript": "~6.0.2"
}, },
"browserslist": { "browserslist": {
+432 -1
View File
@@ -17,14 +17,23 @@ importers:
'@docusaurus/preset-classic': '@docusaurus/preset-classic':
specifier: 3.10.2 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) 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': '@docusaurus/theme-mermaid':
specifier: ^3.10.2 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) 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': '@mdx-js/react':
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.1.1(@types/react@19.2.17)(react@19.2.7) 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: clsx:
specifier: ^2.0.0 specifier: ^2.1.1
version: 2.1.1 version: 2.1.1
docusaurus-plugin-drawio: docusaurus-plugin-drawio:
specifier: ^0.4.0 specifier: ^0.4.0
@@ -41,6 +50,12 @@ importers:
react-dom: react-dom:
specifier: ^19.0.0 specifier: ^19.0.0
version: 19.2.7(react@19.2.7) 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: devDependencies:
'@docusaurus/module-type-aliases': '@docusaurus/module-type-aliases':
specifier: 3.10.2 specifier: 3.10.2
@@ -54,6 +69,15 @@ importers:
'@types/react': '@types/react':
specifier: ^19.0.0 specifier: ^19.0.0
version: 19.2.17 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: typescript:
specifier: ~6.0.2 specifier: ~6.0.2
version: 6.0.3 version: 6.0.3
@@ -1274,6 +1298,21 @@ packages:
'@emnapi/wasi-threads@1.2.2': '@emnapi/wasi-threads@1.2.2':
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} 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': '@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@@ -1537,6 +1576,206 @@ packages:
'@polka/url@1.0.0-next.29': '@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} 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': '@rspack/binding-darwin-arm64@1.7.12':
resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==} resolution: {integrity: sha512-rbFprJaJiqrmfy8SHth8EsoRS0wg4bXcucwj9NiMzpGFq14Opw8c04iQ6H9BECYzgmN0PKZ9rh41LdVvhdZe4A==}
cpu: [arm64] cpu: [arm64]
@@ -5276,6 +5515,9 @@ packages:
resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
set-function-length@1.2.2: set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -5481,6 +5723,12 @@ packages:
'@swc/core': ^1.2.147 '@swc/core': ^1.2.147
webpack: '>=2' 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: tapable@2.3.3:
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -8234,6 +8482,23 @@ snapshots:
tslib: 2.8.1 tslib: 2.8.1
optional: true 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/hoek@9.3.0': {}
'@hapi/topo@5.1.0': '@hapi/topo@5.1.0':
@@ -8609,6 +8874,166 @@ snapshots:
'@polka/url@1.0.0-next.29': {} '@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': '@rspack/binding-darwin-arm64@1.7.12':
optional: true optional: true
@@ -12910,6 +13335,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
server-only@0.0.1: {}
set-function-length@1.2.2: set-function-length@1.2.2:
dependencies: dependencies:
define-data-property: 1.1.4 define-data-property: 1.1.4
@@ -13134,6 +13561,10 @@ snapshots:
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
webpack: 5.108.4(@swc/core@1.15.43)(postcss@8.5.19) 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: {} 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)): 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)):
+40 -15
View File
@@ -2,50 +2,75 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
const sidebars: SidebarsConfig = { const sidebars: SidebarsConfig = {
docsSidebar: [ docsSidebar: [
/* ── Introduction ────────────────────────────────────── */
{ {
type: 'doc', type: 'doc',
id: 'intro/index', id: 'intro/index',
label: '← Overview', label: 'Introduction',
}, },
/* ── Platform ────────────────────────────────────────── */
{ {
type: 'category', type: 'category',
label: 'Applications', label: 'Platform',
collapsed: false,
items: [
'platform/overview',
'platform/cicd',
],
},
/* ── Products ────────────────────────────────────────── */
{
type: 'category',
label: 'Products',
collapsed: false, collapsed: false,
items: [ items: [
{ {
type: 'category', type: 'category',
label: '📈 Stock Market Pro', label: 'Stock Market Pro',
link: { type: 'doc', id: 'applications/stock-market-professional/index' }, link: { type: 'doc', id: 'products/stock-market-pro/index' },
items: [ items: [
{ {
type: 'category', type: 'category',
label: 'C4 Architecture', label: 'Architecture (C4)',
collapsed: false,
items: [ items: [
'applications/stock-market-professional/c4/context', 'products/stock-market-pro/c4/context',
'applications/stock-market-professional/c4/containers', 'products/stock-market-pro/c4/containers',
'applications/stock-market-professional/c4/components', 'products/stock-market-pro/c4/components',
], ],
}, },
{ {
type: 'category', type: 'category',
label: 'Modules', label: 'Modules',
items: [ items: [
'applications/stock-market-professional/data-monitoring/index', 'products/stock-market-pro/data-monitoring/index',
'applications/stock-market-professional/technical-analysis/index', 'products/stock-market-pro/technical-analysis/index',
'applications/stock-market-professional/signal-generation/index', 'products/stock-market-pro/signal-generation/index',
], ],
}, },
], ],
}, },
], ],
}, },
/* ── Engineering ─────────────────────────────────────── */
{ {
type: 'category', type: 'category',
label: 'Platform', label: 'Engineering',
collapsed: false,
items: [ items: [
'architecture/overview', 'engineering/guidelines',
'guides/getting-started', ],
},
/* ── Runbooks ────────────────────────────────────────── */
{
type: 'category',
label: 'Runbooks',
collapsed: true,
items: [
'runbooks/index',
], ],
}, },
], ],
+156
View File
@@ -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<HTMLSpanElement> {
variant?: BadgeVariant;
}
const badgeVariants: Record<BadgeVariant, string> = {
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 (
<span
className={cn(
'inline-flex items-center gap-1 text-[0.65rem] font-semibold uppercase tracking-widest px-2 py-0.5',
badgeVariants[variant],
className,
)}
{...props}
>
{children}
</span>
);
}
/* ── Card ────────────────────────────────────────────────── */
export function Card({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('border border-border bg-card text-card-foreground', className)}
{...props}
>
{children}
</div>
);
}
export function CardHeader({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn('flex flex-col gap-1.5 p-5', className)} {...props}>{children}</div>;
}
export function CardTitle({ className, children, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
return (
<h3 className={cn('text-base font-semibold tracking-tight leading-none', className)} {...props}>
{children}
</h3>
);
}
export function CardContent({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn('p-5 pt-0', className)} {...props}>{children}</div>;
}
/* ── Callout ─────────────────────────────────────────────── */
type CalloutVariant = 'info' | 'warning' | 'danger' | 'success' | 'note';
interface CalloutProps {
variant?: CalloutVariant;
title?: string;
children: React.ReactNode;
className?: string;
}
const calloutStyles: Record<CalloutVariant, { border: string; bg: string; title: string; icon: string }> = {
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 (
<div className={cn('border-l-2 border px-4 py-3 my-4 text-sm', s.border, s.bg, className)}>
{title && (
<div className={cn('flex items-center gap-1.5 font-semibold uppercase tracking-widest text-[0.7rem] mb-2', s.title)}>
<span>{s.icon}</span>
{title}
</div>
)}
<div className="text-muted-foreground leading-relaxed">{children}</div>
</div>
);
}
/* ── PropertiesTable ─────────────────────────────────────── */
interface Property {
name: string;
type: string;
default?: string;
description: string;
}
interface PropertiesTableProps {
properties: Property[];
}
export function PropertiesTable({ properties }: PropertiesTableProps) {
return (
<div className="not-prose my-4 border border-border overflow-hidden text-sm">
<table className="w-full border-collapse">
<thead>
<tr className="bg-muted border-b border-border">
<th className="text-left px-4 py-2.5 font-semibold text-[0.7rem] uppercase tracking-wider text-muted-foreground w-36">Name</th>
<th className="text-left px-4 py-2.5 font-semibold text-[0.7rem] uppercase tracking-wider text-muted-foreground w-36">Type</th>
<th className="text-left px-4 py-2.5 font-semibold text-[0.7rem] uppercase tracking-wider text-muted-foreground w-24">Default</th>
<th className="text-left px-4 py-2.5 font-semibold text-[0.7rem] uppercase tracking-wider text-muted-foreground">Description</th>
</tr>
</thead>
<tbody>
{properties.map((p, i) => (
<tr key={p.name} className={cn('border-b border-border last:border-0', i % 2 === 0 ? '' : 'bg-muted/30')}>
<td className="px-4 py-2.5 font-mono text-xs text-indigo-400">{p.name}</td>
<td className="px-4 py-2.5 font-mono text-xs text-amber-400">{p.type}</td>
<td className="px-4 py-2.5 font-mono text-xs text-muted-foreground">{p.default ?? '—'}</td>
<td className="px-4 py-2.5 text-muted-foreground text-xs leading-relaxed">{p.description}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
/* ── StatusIndicator ─────────────────────────────────────── */
type Status = 'operational' | 'degraded' | 'outage' | 'maintenance';
const statusConfig: Record<Status, { color: string; label: string }> = {
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 (
<span className="inline-flex items-center gap-2 text-xs text-muted-foreground">
<span className={cn('w-1.5 h-1.5 inline-block', color)} />
{label}
</span>
);
}
+173 -167
View File
@@ -1,27 +1,74 @@
/* ============================================================ /* ============================================================
SKIC Playground Docs — Modern Dark-First Technical Theme SKIC Playground Docs — Dark-first, Sharp, Technical Theme
Inspired by Vercel / Linear design language Stack: Docusaurus + Tailwind (preflight off) + shadcn/ui (New York / Zinc)
Font: Geist (by Vercel) + Geist Mono Design: no rounded corners, Vercel/Linear aesthetic
============================================================ */ ============================================================ */
/* --- Google Fonts / Geist import --- */ @tailwind base;
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); @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-sans/style.css');
@import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/style.css'); @import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/style.css');
/* ── Design Tokens ───────────────────────────────────────── */ /* ── shadcn/ui CSS variables (New York / Zinc, radius = 0) ── */
@layer base {
:root { :root {
/* Brand */ --background: 0 0% 100%;
--skic-accent: #6366f1; /* indigo-500 */ --foreground: 240 10% 3.9%;
--skic-accent-light: #818cf8; /* indigo-400 */ --card: 0 0% 100%;
--skic-accent-dark: #4f46e5; /* indigo-600 */ --card-foreground: 240 10% 3.9%;
--skic-green: #10b981; --popover: 0 0% 100%;
--skic-amber: #f59e0b; --popover-foreground: 240 10% 3.9%;
--skic-red: #ef4444; --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: #6366f1;
--ifm-color-primary-dark: #4f46e5; --ifm-color-primary-dark: #4f46e5;
--ifm-color-primary-darker: #4338ca; --ifm-color-primary-darker: #4338ca;
@@ -34,7 +81,6 @@
--ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace; --ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace;
--ifm-font-size-base: 15px; --ifm-font-size-base: 15px;
--ifm-line-height-base: 1.7; --ifm-line-height-base: 1.7;
--ifm-font-weight-normal: 400;
--ifm-heading-font-weight: 600; --ifm-heading-font-weight: 600;
--ifm-heading-letter-spacing: -0.02em; --ifm-heading-letter-spacing: -0.02em;
@@ -42,25 +88,27 @@
--ifm-h2-font-size: 1.5rem; --ifm-h2-font-size: 1.5rem;
--ifm-h3-font-size: 1.2rem; --ifm-h3-font-size: 1.2rem;
--ifm-navbar-height: 60px; --ifm-navbar-height: 56px;
--ifm-navbar-shadow: none; --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-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-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); --docusaurus-highlighted-code-line-bg: rgba(99, 102, 241, 0.08);
} }
/* ── Dark Mode ───────────────────────────────────────────── */ /* Dark mode Infima overrides */
[data-theme='dark'] { [data-theme='dark'] {
--ifm-color-primary: #818cf8; --ifm-color-primary: #818cf8;
--ifm-color-primary-dark: #6366f1; --ifm-color-primary-dark: #6366f1;
@@ -72,86 +120,94 @@
--ifm-background-color: #0a0a0a; --ifm-background-color: #0a0a0a;
--ifm-background-surface-color: #111111; --ifm-background-surface-color: #111111;
--ifm-navbar-background-color: rgba(10, 10, 10, 0.85); --ifm-navbar-background-color: #0a0a0a;
--ifm-sidebar-background: #0a0a0a; --ifm-sidebar-background: #0a0a0a;
--ifm-footer-background-color: #0a0a0a; --ifm-footer-background-color: #0a0a0a;
--ifm-color-content: #ededed; --ifm-color-content: #ededed;
--ifm-color-content-secondary: #a1a1a1; --ifm-color-content-secondary: #a1a1a1;
--ifm-color-emphasis-100: #1a1a1a; --ifm-color-emphasis-100: #161616;
--ifm-color-emphasis-200: #222222; --ifm-color-emphasis-200: #1f1f1f;
--ifm-color-emphasis-300: #333333; --ifm-color-emphasis-300: #2a2a2a;
--ifm-color-emphasis-400: #555555; --ifm-color-emphasis-400: #444444;
--ifm-color-emphasis-600: #999999; --ifm-color-emphasis-600: #888888;
--ifm-color-emphasis-700: #cccccc; --ifm-color-emphasis-700: #cccccc;
--ifm-color-emphasis-900: #ffffff; --ifm-color-emphasis-900: #ffffff;
--ifm-toc-border-color: #222222;
--ifm-hr-border-color: #1f1f1f; --ifm-hr-border-color: #1f1f1f;
--ifm-code-background: #161616; --ifm-code-background: #161616;
--ifm-pre-background: #111111; --ifm-pre-background: #111111;
--ifm-blockquote-border-left-color: var(--skic-accent);
--docusaurus-highlighted-code-line-bg: rgba(129, 140, 248, 0.12); --docusaurus-highlighted-code-line-bg: rgba(129, 140, 248, 0.12);
} }
/* ── Typography ──────────────────────────────────────────── */ /* ── Force dark navbar in BOTH modes ─────────────────────── */
h1, h2, h3, h4, h5, h6 { /* This is the key fix — lock navbar dark regardless of color scheme */
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 ──────────────────────────────────────────────── */
.navbar { .navbar {
border-bottom: 1px solid var(--ifm-color-emphasis-200); background-color: #0a0a0a !important;
border-bottom: 1px solid #1f1f1f !important;
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
} box-shadow: none !important;
[data-theme='dark'] .navbar {
background: rgba(10, 10, 10, 0.85);
} }
.navbar__title { .navbar__title {
font-weight: 700; font-weight: 700;
font-size: 1rem; font-size: 0.95rem;
letter-spacing: -0.02em; letter-spacing: -0.02em;
} color: #ededed !important;
.navbar__logo {
height: 24px;
} }
.navbar__link { .navbar__link {
font-size: 0.875rem; font-size: 0.875rem;
font-weight: 500; font-weight: 500;
color: var(--ifm-color-content-secondary); color: #a1a1a1 !important;
transition: color 0.15s ease; transition: color 0.12s ease;
border-radius: 0 !important;
} }
.navbar__link:hover, .navbar__link:hover,
.navbar__link--active { .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 ─────────────────────────────────────────────── */ /* ── Sidebar ─────────────────────────────────────────────── */
.theme-doc-sidebar-container { .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 { .menu__link {
font-size: 0.875rem; font-size: 0.875rem;
border-radius: 6px; border-radius: 0 !important;
padding: 0.35rem 0.75rem; padding: 0.3rem 0.75rem;
color: var(--ifm-color-content-secondary); color: var(--ifm-color-content-secondary);
transition: all 0.15s ease; transition: all 0.12s ease;
} }
.menu__link:hover { .menu__link:hover {
@@ -163,40 +219,43 @@ p { color: var(--ifm-color-content); }
background: var(--ifm-color-emphasis-100) !important; background: var(--ifm-color-emphasis-100) !important;
color: var(--ifm-color-primary) !important; color: var(--ifm-color-primary) !important;
font-weight: 600; font-weight: 600;
border-left: 2px solid var(--ifm-color-primary);
} }
.menu__caret::before { .menu__caret { border-radius: 0 !important; }
background: var(--ifm-color-content-secondary);
}
/* ── Code Blocks ─────────────────────────────────────────── */ /* ── Code blocks ─────────────────────────────────────────── */
code { code {
font-size: 0.84em; font-size: 0.84em;
border: 1px solid var(--ifm-color-emphasis-200); border: 1px solid var(--ifm-color-emphasis-200);
background: var(--ifm-color-emphasis-100); background: var(--ifm-color-emphasis-100);
border-radius: 5px; border-radius: 0 !important;
} }
.prism-code { .prism-code {
font-size: 0.84rem; font-size: 0.84rem;
line-height: 1.6; 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); border: 1px solid var(--ifm-color-emphasis-200);
box-shadow: none !important;
border-radius: 0 !important;
} }
div[class*="codeBlockContainer"] { div[class*='codeBlockTitle'] {
border: 1px solid var(--ifm-color-emphasis-200);
box-shadow: none;
}
div[class*="codeBlockTitle"] {
background: var(--ifm-color-emphasis-100); background: var(--ifm-color-emphasis-100);
border-bottom: 1px solid var(--ifm-color-emphasis-200); border-bottom: 1px solid var(--ifm-color-emphasis-200);
font-size: 0.78rem; font-size: 0.78rem;
font-family: var(--ifm-font-family-monospace); font-family: var(--ifm-font-family-monospace);
color: var(--ifm-color-content-secondary); color: var(--ifm-color-content-secondary);
border-radius: 0 !important;
}
div[class*='copyButton'],
div[class*='codeButton'] {
border-radius: 0 !important;
} }
/* ── Tables ──────────────────────────────────────────────── */ /* ── Tables ──────────────────────────────────────────────── */
@@ -205,55 +264,45 @@ table {
width: 100%; width: 100%;
font-size: 0.875rem; font-size: 0.875rem;
border: 1px solid var(--ifm-color-emphasis-200); border: 1px solid var(--ifm-color-emphasis-200);
border-radius: 8px; border-radius: 0 !important;
overflow: hidden; overflow: hidden;
} }
thead { thead { background: var(--ifm-color-emphasis-100); }
background: var(--ifm-color-emphasis-100);
}
th { th {
font-size: 0.75rem; font-size: 0.72rem;
font-weight: 600; font-weight: 600;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.06em; letter-spacing: 0.07em;
color: var(--ifm-color-content-secondary); color: var(--ifm-color-content-secondary);
padding: 0.75rem 1rem; padding: 0.65rem 1rem;
border-bottom: 1px solid var(--ifm-color-emphasis-200); border-bottom: 1px solid var(--ifm-color-emphasis-200);
} }
td { td {
padding: 0.7rem 1rem; padding: 0.65rem 1rem;
border-bottom: 1px solid var(--ifm-color-emphasis-100); border-bottom: 1px solid var(--ifm-color-emphasis-100);
} }
tr:last-child td { border-bottom: none; } tr:last-child td { border-bottom: none; }
tbody tr:hover { tbody tr:hover { background: var(--ifm-color-emphasis-100); }
background: var(--ifm-color-emphasis-100);
}
/* ── Admonitions (callouts) ──────────────────────────────── */ /* ── Admonitions ─────────────────────────────────────────── */
.admonition { .admonition {
border-radius: 8px; border-radius: 0 !important;
border: 1px solid; border: 1px solid;
border-left-width: 3px; border-left-width: 2px;
font-size: 0.9rem; font-size: 0.9rem;
} }
.admonition-note { border-color: var(--ifm-color-emphasis-300); background: var(--ifm-color-emphasis-100); } .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-tip { border-color: #10b981; background: rgba(16,185,129,0.05); }
.admonition-info { border-color: var(--skic-accent); background: rgba(99, 102, 241, 0.06); } .admonition-info { border-color: #6366f1; background: rgba(99,102,241,0.05); }
.admonition-warning { border-color: var(--skic-amber); background: rgba(245, 158, 11, 0.06); } .admonition-warning { border-color: #f59e0b; background: rgba(245,158,11,0.05); }
.admonition-danger { border-color: var(--skic-red); background: rgba(239, 68, 68, 0.06); } .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; }
.admonition-heading h5 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; }
/* ── Breadcrumbs ─────────────────────────────────────────── */
.breadcrumbs__link {
font-size: 0.8rem;
}
/* ── TOC ─────────────────────────────────────────────────── */ /* ── TOC ─────────────────────────────────────────────────── */
.table-of-contents__link { .table-of-contents__link {
@@ -261,15 +310,13 @@ tbody tr:hover {
color: var(--ifm-color-content-secondary); color: var(--ifm-color-content-secondary);
} }
.table-of-contents__link:hover, .table-of-contents__link:hover,
.table-of-contents__link--active { .table-of-contents__link--active { color: var(--ifm-color-primary); }
color: var(--ifm-color-primary);
}
/* ── Pagination ──────────────────────────────────────────── */ /* ── Pagination ──────────────────────────────────────────── */
.pagination-nav__link { .pagination-nav__link {
border: 1px solid var(--ifm-color-emphasis-200); border: 1px solid var(--ifm-color-emphasis-200);
border-radius: 8px; border-radius: 0 !important;
transition: all 0.15s ease; transition: all 0.12s ease;
} }
.pagination-nav__link:hover { .pagination-nav__link:hover {
border-color: var(--ifm-color-primary); border-color: var(--ifm-color-primary);
@@ -278,12 +325,12 @@ tbody tr:hover {
/* ── Footer ──────────────────────────────────────────────── */ /* ── Footer ──────────────────────────────────────────────── */
.footer { .footer {
border-top: 1px solid var(--ifm-color-emphasis-200); border-top: 1px solid #1f1f1f;
padding: 2rem 0; background-color: #0a0a0a !important;
} }
.footer__title { .footer__title {
font-size: 0.75rem; font-size: 0.72rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.08em; letter-spacing: 0.08em;
font-weight: 600; font-weight: 600;
@@ -295,66 +342,25 @@ tbody tr:hover {
color: var(--ifm-color-content-secondary); color: var(--ifm-color-content-secondary);
} }
/* ── Doc article ─────────────────────────────────────────── */ /* ── Buttons ─────────────────────────────────────────────── */
.markdown { .button {
--ifm-heading-margin-top: 1.75rem; border-radius: 0 !important;
} }
/* ── Doc content ─────────────────────────────────────────── */
.markdown { --ifm-heading-margin-top: 1.75rem; }
.markdown > h2:first-child { margin-top: 0; } .markdown > h2:first-child { margin-top: 0; }
/* ── Hero (homepage) ─────────────────────────────────────── */ /* ── Scrollbar ───────────────────────────────────────────── */
.hero--primary { ::-webkit-scrollbar { width: 5px; height: 5px; }
background: linear-gradient(135deg, #0a0a0a 0%, #111827 50%, #0a0a0a 100%); ::-webkit-scrollbar-track { background: transparent; }
position: relative; ::-webkit-scrollbar-thumb { background: #2a2a2a; }
overflow: hidden; ::-webkit-scrollbar-thumb:hover { background: #444; }
}
.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; }
/* ── Selection ───────────────────────────────────────────── */ /* ── Selection ───────────────────────────────────────────── */
::selection { background: rgba(99,102,241,0.25); } ::selection { background: rgba(99,102,241,0.25); }
/* ── Announcement bar ────────────────────────────────────── */
div[class*='announcementBar'] {
border-radius: 0 !important;
}
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
+1 -1
View File
@@ -20,7 +20,7 @@ const applications: Application[] = [
name: 'Stock Market Professional', 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.', 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', status: 'wip',
link: '/docs/applications/stock-market-professional', link: '/docs/products/stock-market-pro',
tags: ['Python', 'Forex', 'TA', 'Signals'], tags: ['Python', 'Forex', 'TA', 'Signals'],
}, },
]; ];
+27
View File
@@ -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 <html>.
*/
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 (
<>
<Layout {...props}>
<ThemeSynchronizer />
{props.children}
</Layout>
</>
);
}
+57
View File
@@ -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
},
};
+2 -1
View File
@@ -6,7 +6,8 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"ignoreDeprecations": "6.0", "ignoreDeprecations": "6.0",
"strict": true "strict": true,
"jsx": "react-jsx"
}, },
"exclude": [".docusaurus", "build"] "exclude": [".docusaurus", "build"]
} }