feat: Modern styling, draw.io integration, and C4 architecture docs
Build and Deploy Documentation / build (push) Failing after 8s

Styling:
- Dark-first design (Vercel/Linear inspired) with Geist font
- Custom CSS with full --ifm- token overrides
- Glassmorphic navbar, refined sidebar, modern tables
- Gradient hero with animated glow, app cards, C4 section
- Custom badge, tag, and callout components
- Announcement bar, TOC config, dark scrollbar

draw.io:
- Added docusaurus-plugin-drawio + raw-loader
- SMP full pipeline diagram (smp-pipeline.drawio)
- Embedded in architecture overview

Mermaid:
- Added @docusaurus/theme-mermaid
- C4Context, C4Container, C4Component diagrams
- Sequence and graph diagrams in architecture

C4 Architecture (Stock Market Professional):
- L1 Context: system in the world, actors, external systems
- L2 Containers: Data Ingestor, Analysis Engine, Signal Generator, TimescaleDB, Notifier
- L3 Components: deep component breakdown with mermaid C4Component diagrams

Other:
- Sidebar restructured with C4 section and Modules section
- Homepage rebuilt as modern dark landing page
This commit is contained in:
2026-07-16 20:25:09 +00:00
parent f39c2a1e5f
commit 674ec348ce
14 changed files with 2164 additions and 144 deletions
@@ -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,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>
+48 -28
View File
@@ -6,47 +6,67 @@ sidebar_position: 1
## Overview ## Overview
All SKIC Playground applications share a common infrastructure pattern: All SKIC Playground applications share a common infrastructure built on TrueNAS SCALE and Gitea.
``` ## Full Pipeline — Interactive Diagram
┌─────────────────────────────────────────────────────┐
│ TrueNAS SCALE │ import Drawio from '@theme/Drawio';
│ │ import smpPipeline from '!!raw-loader!./diagrams/smp-pipeline.drawio';
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Gitea │ │ Registry │ │ App Containers │ │ <Drawio content={smpPipeline} />
│ │ + CI/CD │ │ (Zot) │ │ │ │
│ └──────────┘ └──────────┘ └──────────────────┘ │ ## Infrastructure
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ ```mermaid
│ │Keycloak │ │ Hermes │ │ Monitoring │ │ graph TB
│ │ (Auth) │ │ (Agent) │ │ │ │ subgraph TrueNAS["🖥 TrueNAS SCALE — lego-cloud.eu"]
│ └──────────┘ └──────────┘ └──────────────────┘ │ Gitea["🗂 Gitea\nSource + CI/CD"]
└─────────────────────────────────────────────────────┘ Zot["📦 Zot Registry\n:30264"]
Apps["🐳 App Containers"]
Hermes["🤖 Hermes Agent\nAI Operations"]
Keycloak["🔐 Keycloak\nAuth / SSO"]
end
Gitea -->|push image| Zot
Zot -->|pull & deploy| Apps
Hermes -->|automates| Gitea
Hermes -->|manages| Apps
Keycloak -->|secures| Apps
``` ```
## Shared Services ## Shared Services
| Service | URL | Purpose | | Service | URL | Purpose |
|---|---|---| |---|---|---|
| Gitea | gitea.lego-cloud.eu | Source code, CI/CD | | Gitea | gitea.lego-cloud.eu | Source code, CI/CD, issue tracking |
| Keycloak | keycloak.lego-cloud.eu | Authentication, SSO | | Keycloak | keycloak.lego-cloud.eu | Authentication & SSO |
| Zot Registry | :30264 | Container images | | Zot Registry | :30264 | OCI container image registry |
| Hermes Agent | Local | AI operations, automation | | Hermes Agent | Local | AI operations, automation, documentation |
## CI/CD Pattern ## CI/CD Flow
All applications follow the same Gitea Actions workflow: ```mermaid
sequenceDiagram
participant Dev as Developer / Jarvis
participant Git as Gitea
participant CI as Gitea Actions
participant Reg as Zot Registry
participant Host as TrueNAS
1. **Push to main** → Build + Test Dev->>Git: git push main
2. **Build container** → Push to Zot registry Git->>CI: Trigger workflow
3. **Deploy** → Update running container CI->>CI: pnpm install
4. **Docs update** → Rebuild documentation site CI->>CI: pnpm build
CI->>Reg: docker push image
CI->>Host: Deploy updated container
Host-->>Dev: ✅ Live
```
## Repository Structure ## Repository Structure
``` ```
skic-v1-playground/ skic-v1-playground/
├── documentation/ # This docs site ├── documentation/ ← This docs site (Docusaurus)
├── stock-market-pro/ # (Planned) Trading system ├── stock-market-pro/ ← (Planned) Trading system
└── ... # Future applications └── ... ← Future applications
``` ```
+1 -1
View File
@@ -20,7 +20,7 @@ This documentation covers various applications designed, developed, and operated
| Application | Status | Description | | Application | Status | Description |
|---|---|---| |---|---|---|
| [Stock Market Professional](/docs/applications/stock-market-professional) | 🚧 In Development | Forex/stock monitoring, technical analysis, and signal generation | | [Stock Market Professional](../applications/stock-market-professional/index.md) | 🚧 In Development | Forex/stock monitoring, technical analysis, and signal generation |
## Technology Stack ## Technology Stack
+63 -28
View File
@@ -4,7 +4,7 @@ import type * as Preset from '@docusaurus/preset-classic';
const config: Config = { const config: Config = {
title: 'SKIC Playground', title: 'SKIC Playground',
tagline: 'Documentation for SKIC autonomous applications', tagline: 'Autonomous application documentation',
favicon: 'img/favicon.ico', favicon: 'img/favicon.ico',
url: 'https://docs.skic-playground.lego-cloud.eu', url: 'https://docs.skic-playground.lego-cloud.eu',
@@ -14,26 +14,41 @@ const config: Config = {
projectName: 'documentation', projectName: 'documentation',
onBrokenLinks: 'throw', onBrokenLinks: 'throw',
markdown: {
hooks: {
onBrokenMarkdownLinks: 'warn', onBrokenMarkdownLinks: 'warn',
},
mermaid: true,
},
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en'], locales: ['en'],
}, },
themes: [
'@docusaurus/theme-mermaid',
],
plugins: [
'docusaurus-plugin-drawio',
],
presets: [ presets: [
[ [
'classic', 'classic',
{ {
docs: { docs: {
sidebarPath: './sidebars.ts', sidebarPath: './sidebars.ts',
editUrl: editUrl: 'https://gitea.lego-cloud.eu/skic-v1-playground/documentation/src/branch/main/',
'https://gitea.lego-cloud.eu/skic-v1-playground/documentation/src/branch/main/', showLastUpdateTime: true,
showLastUpdateAuthor: true,
}, },
blog: { blog: {
showReadingTime: true, showReadingTime: true,
editUrl: blogTitle: 'Changelog',
'https://gitea.lego-cloud.eu/skic-v1-playground/documentation/src/branch/main/', blogDescription: 'Updates and changes to SKIC applications',
postsPerPage: 'ALL',
}, },
theme: { theme: {
customCss: './src/css/custom.css', customCss: './src/css/custom.css',
@@ -43,58 +58,78 @@ const config: Config = {
], ],
themeConfig: { themeConfig: {
colorMode: {
defaultMode: 'dark',
disableSwitch: false,
respectPrefersColorScheme: true,
},
image: 'img/docusaurus-social-card.jpg',
announcementBar: {
id: 'wip',
content: '🚧 SKIC Playground is under active development — documentation is being built autonomously.',
backgroundColor: '#1a1a2e',
textColor: '#818cf8',
isCloseable: true,
},
navbar: { navbar: {
title: 'SKIC Playground', title: 'SKIC Playground',
hideOnScroll: true,
items: [ items: [
{ {
type: 'docSidebar', type: 'docSidebar',
sidebarId: 'docsSidebar', sidebarId: 'docsSidebar',
position: 'left', position: 'left',
label: 'Documentation', label: 'Docs',
},
{
to: '/blog',
label: 'Changelog',
position: 'left',
}, },
{ {
href: 'https://gitea.lego-cloud.eu/skic-v1-playground', href: 'https://gitea.lego-cloud.eu/skic-v1-playground',
label: 'Gitea',
position: 'right', position: 'right',
className: 'header-gitea-link',
'aria-label': 'Gitea repository',
label: 'Gitea ↗',
}, },
], ],
}, },
footer: { footer: {
style: 'dark', style: 'dark',
links: [ links: [
{ {
title: 'Docs', title: 'Documentation',
items: [ items: [
{ { label: 'Getting Started', to: '/docs/guides/getting-started' },
label: 'Getting Started', { label: 'Stock Market Pro', to: '/docs/applications/stock-market-professional' },
to: '/docs/intro', { label: 'Architecture', to: '/docs/architecture/overview' },
},
{
label: 'Applications',
to: '/docs/applications/stock-market-professional',
},
], ],
}, },
{ {
title: 'Infrastructure', title: 'Infrastructure',
items: [ items: [
{ { label: 'Gitea', href: 'https://gitea.lego-cloud.eu/skic-v1-playground' },
label: 'Gitea', { label: 'Keycloak', href: 'https://keycloak.lego-cloud.eu' },
href: 'https://gitea.lego-cloud.eu/skic-v1-playground',
},
], ],
}, },
], ],
copyright: `Copyright © ${new Date().getFullYear()} SKIC Playground. Built with Docusaurus.`, copyright: `© ${new Date().getFullYear()} SKIC Playground · Built by Jarvis`,
}, },
prism: { prism: {
theme: prismThemes.github, theme: prismThemes.oneDark,
darkTheme: prismThemes.dracula, darkTheme: prismThemes.oneDark,
additionalLanguages: ['bash', 'python', 'sql', 'yaml', 'json', 'typescript', 'docker'],
},
mermaid: {
theme: { light: 'neutral', dark: 'dark' },
},
tableOfContents: {
minHeadingLevel: 2,
maxHeadingLevel: 4,
}, },
} satisfies Preset.ThemeConfig, } satisfies Preset.ThemeConfig,
}; };
+3
View File
@@ -18,9 +18,12 @@
"@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-mermaid": "^3.10.2",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"docusaurus-plugin-drawio": "^0.4.0",
"prism-react-renderer": "^2.3.0", "prism-react-renderer": "^2.3.0",
"raw-loader": "^4.0.2",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0" "react-dom": "^19.0.0"
}, },
+880
View File
File diff suppressed because it is too large Load Diff
+23 -12
View File
@@ -2,20 +2,35 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
const sidebars: SidebarsConfig = { const sidebars: SidebarsConfig = {
docsSidebar: [ docsSidebar: [
'intro/index', {
type: 'doc',
id: 'intro/index',
label: '← Overview',
},
{ {
type: 'category', type: 'category',
label: 'Applications', label: 'Applications',
collapsed: false,
items: [ items: [
{ {
type: 'category', type: 'category',
label: 'Stock Market Professional', label: '📈 Stock Market Pro',
link: { link: { type: 'doc', id: 'applications/stock-market-professional/index' },
type: 'doc', items: [
id: 'applications/stock-market-professional/index', {
type: 'category',
label: 'C4 Architecture',
collapsed: false,
items: [
'applications/stock-market-professional/c4/context',
'applications/stock-market-professional/c4/containers',
'applications/stock-market-professional/c4/components',
],
}, },
{
type: 'category',
label: 'Modules',
items: [ items: [
'applications/stock-market-professional/overview/architecture',
'applications/stock-market-professional/data-monitoring/index', 'applications/stock-market-professional/data-monitoring/index',
'applications/stock-market-professional/technical-analysis/index', 'applications/stock-market-professional/technical-analysis/index',
'applications/stock-market-professional/signal-generation/index', 'applications/stock-market-professional/signal-generation/index',
@@ -23,17 +38,13 @@ const sidebars: SidebarsConfig = {
}, },
], ],
}, },
{
type: 'category',
label: 'Architecture',
items: [
'architecture/overview',
], ],
}, },
{ {
type: 'category', type: 'category',
label: 'Guides', label: 'Platform',
items: [ items: [
'architecture/overview',
'guides/getting-started', 'guides/getting-started',
], ],
}, },
+354 -24
View File
@@ -1,30 +1,360 @@
/** /* ============================================================
* Any CSS included here will be global. The classic template SKIC Playground Docs — Modern Dark-First Technical Theme
* bundles Infima by default. Infima is a CSS framework designed to Inspired by Vercel / Linear design language
* work well for content-centric websites. Font: Geist (by Vercel) + Geist Mono
*/ ============================================================ */
/* You can override the default Infima variables here. */ /* --- Google Fonts / Geist import --- */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
/* Geist via CDN */
@import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-sans/style.css');
@import url('https://cdn.jsdelivr.net/npm/geist@1.3.0/dist/fonts/geist-mono/style.css');
/* ── Design Tokens ───────────────────────────────────────── */
:root { :root {
--ifm-color-primary: #2e8555; /* Brand */
--ifm-color-primary-dark: #29784c; --skic-accent: #6366f1; /* indigo-500 */
--ifm-color-primary-darker: #277148; --skic-accent-light: #818cf8; /* indigo-400 */
--ifm-color-primary-darkest: #205d3b; --skic-accent-dark: #4f46e5; /* indigo-600 */
--ifm-color-primary-light: #33925d; --skic-green: #10b981;
--ifm-color-primary-lighter: #359962; --skic-amber: #f59e0b;
--ifm-color-primary-lightest: #3cad6e; --skic-red: #ef4444;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); /* Docusaurus overrides — Light mode */
--ifm-color-primary: #6366f1;
--ifm-color-primary-dark: #4f46e5;
--ifm-color-primary-darker: #4338ca;
--ifm-color-primary-darkest: #3730a3;
--ifm-color-primary-light: #818cf8;
--ifm-color-primary-lighter: #a5b4fc;
--ifm-color-primary-lightest: #c7d2fe;
--ifm-font-family-base: 'Geist', 'Inter', system-ui, sans-serif;
--ifm-font-family-monospace: 'Geist Mono', 'Fira Code', monospace;
--ifm-font-size-base: 15px;
--ifm-line-height-base: 1.7;
--ifm-font-weight-normal: 400;
--ifm-heading-font-weight: 600;
--ifm-heading-letter-spacing: -0.02em;
--ifm-h1-font-size: 2.25rem;
--ifm-h2-font-size: 1.5rem;
--ifm-h3-font-size: 1.2rem;
--ifm-navbar-height: 60px;
--ifm-navbar-shadow: none;
--ifm-toc-border-color: var(--ifm-color-emphasis-200);
--ifm-code-font-size: 0.85rem;
--ifm-code-border-radius: 6px;
--ifm-code-padding-horizontal: 0.4em;
--ifm-code-padding-vertical: 0.15em;
--ifm-pre-border-radius: 10px;
--ifm-pre-padding: 1.25rem;
--ifm-global-radius: 8px;
--ifm-global-shadow-lw: 0 1px 3px 0 rgba(0,0,0,.08);
--ifm-global-shadow-md: 0 4px 12px 0 rgba(0,0,0,.1);
--docusaurus-highlighted-code-line-bg: rgba(99, 102, 241, 0.08);
} }
/* For readability concerns, you should choose a lighter palette in dark mode. */ /* ── Dark Mode ───────────────────────────────────────────── */
[data-theme='dark'] { [data-theme='dark'] {
--ifm-color-primary: #25c2a0; --ifm-color-primary: #818cf8;
--ifm-color-primary-dark: #21af90; --ifm-color-primary-dark: #6366f1;
--ifm-color-primary-darker: #1fa588; --ifm-color-primary-darker: #4f46e5;
--ifm-color-primary-darkest: #1a8870; --ifm-color-primary-darkest: #4338ca;
--ifm-color-primary-light: #29d5b0; --ifm-color-primary-light: #a5b4fc;
--ifm-color-primary-lighter: #32d8b4; --ifm-color-primary-lighter: #c7d2fe;
--ifm-color-primary-lightest: #4fddbf; --ifm-color-primary-lightest: #e0e7ff;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
--ifm-background-color: #0a0a0a;
--ifm-background-surface-color: #111111;
--ifm-navbar-background-color: rgba(10, 10, 10, 0.85);
--ifm-sidebar-background: #0a0a0a;
--ifm-footer-background-color: #0a0a0a;
--ifm-color-content: #ededed;
--ifm-color-content-secondary:#a1a1a1;
--ifm-color-emphasis-100: #1a1a1a;
--ifm-color-emphasis-200: #222222;
--ifm-color-emphasis-300: #333333;
--ifm-color-emphasis-400: #555555;
--ifm-color-emphasis-600: #999999;
--ifm-color-emphasis-700: #cccccc;
--ifm-color-emphasis-900: #ffffff;
--ifm-toc-border-color: #222222;
--ifm-hr-border-color: #1f1f1f;
--ifm-code-background: #161616;
--ifm-pre-background: #111111;
--ifm-blockquote-border-left-color: var(--skic-accent);
--docusaurus-highlighted-code-line-bg: rgba(129, 140, 248, 0.12);
} }
/* ── Typography ──────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 {
letter-spacing: -0.02em;
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
}
h1 { font-size: 2.25rem; font-weight: 700; }
h2 { font-size: 1.5rem; border-bottom: 1px solid var(--ifm-color-emphasis-200); padding-bottom: 0.4rem; }
h3 { font-size: 1.15rem; }
p { color: var(--ifm-color-content); }
/* ── Navbar ──────────────────────────────────────────────── */
.navbar {
border-bottom: 1px solid var(--ifm-color-emphasis-200);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
[data-theme='dark'] .navbar {
background: rgba(10, 10, 10, 0.85);
}
.navbar__title {
font-weight: 700;
font-size: 1rem;
letter-spacing: -0.02em;
}
.navbar__logo {
height: 24px;
}
.navbar__link {
font-size: 0.875rem;
font-weight: 500;
color: var(--ifm-color-content-secondary);
transition: color 0.15s ease;
}
.navbar__link:hover,
.navbar__link--active {
color: var(--ifm-color-content) !important;
}
/* ── Sidebar ─────────────────────────────────────────────── */
.theme-doc-sidebar-container {
border-right: 1px solid var(--ifm-color-emphasis-200) !important;
}
.menu__link {
font-size: 0.875rem;
border-radius: 6px;
padding: 0.35rem 0.75rem;
color: var(--ifm-color-content-secondary);
transition: all 0.15s ease;
}
.menu__link:hover {
background: var(--ifm-color-emphasis-100);
color: var(--ifm-color-content);
}
.menu__link--active {
background: var(--ifm-color-emphasis-100) !important;
color: var(--ifm-color-primary) !important;
font-weight: 600;
}
.menu__caret::before {
background: var(--ifm-color-content-secondary);
}
/* ── Code Blocks ─────────────────────────────────────────── */
code {
font-size: 0.84em;
border: 1px solid var(--ifm-color-emphasis-200);
background: var(--ifm-color-emphasis-100);
border-radius: 5px;
}
.prism-code {
font-size: 0.84rem;
line-height: 1.6;
}
.codeBlockContainer_node_modules-\@docusaurus-theme-classic-lib-theme-CodeBlock-Container-styles-module {
border: 1px solid var(--ifm-color-emphasis-200);
}
div[class*="codeBlockContainer"] {
border: 1px solid var(--ifm-color-emphasis-200);
box-shadow: none;
}
div[class*="codeBlockTitle"] {
background: var(--ifm-color-emphasis-100);
border-bottom: 1px solid var(--ifm-color-emphasis-200);
font-size: 0.78rem;
font-family: var(--ifm-font-family-monospace);
color: var(--ifm-color-content-secondary);
}
/* ── Tables ──────────────────────────────────────────────── */
table {
border-collapse: collapse;
width: 100%;
font-size: 0.875rem;
border: 1px solid var(--ifm-color-emphasis-200);
border-radius: 8px;
overflow: hidden;
}
thead {
background: var(--ifm-color-emphasis-100);
}
th {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--ifm-color-content-secondary);
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--ifm-color-emphasis-200);
}
td {
padding: 0.7rem 1rem;
border-bottom: 1px solid var(--ifm-color-emphasis-100);
}
tr:last-child td { border-bottom: none; }
tbody tr:hover {
background: var(--ifm-color-emphasis-100);
}
/* ── Admonitions (callouts) ──────────────────────────────── */
.admonition {
border-radius: 8px;
border: 1px solid;
border-left-width: 3px;
font-size: 0.9rem;
}
.admonition-note { border-color: var(--ifm-color-emphasis-300); background: var(--ifm-color-emphasis-100); }
.admonition-tip { border-color: var(--skic-green); background: rgba(16, 185, 129, 0.06); }
.admonition-info { border-color: var(--skic-accent); background: rgba(99, 102, 241, 0.06); }
.admonition-warning { border-color: var(--skic-amber); background: rgba(245, 158, 11, 0.06); }
.admonition-danger { border-color: var(--skic-red); background: rgba(239, 68, 68, 0.06); }
.admonition-heading h5 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; }
/* ── Breadcrumbs ─────────────────────────────────────────── */
.breadcrumbs__link {
font-size: 0.8rem;
}
/* ── TOC ─────────────────────────────────────────────────── */
.table-of-contents__link {
font-size: 0.8rem;
color: var(--ifm-color-content-secondary);
}
.table-of-contents__link:hover,
.table-of-contents__link--active {
color: var(--ifm-color-primary);
}
/* ── Pagination ──────────────────────────────────────────── */
.pagination-nav__link {
border: 1px solid var(--ifm-color-emphasis-200);
border-radius: 8px;
transition: all 0.15s ease;
}
.pagination-nav__link:hover {
border-color: var(--ifm-color-primary);
background: var(--ifm-color-emphasis-100);
}
/* ── Footer ──────────────────────────────────────────────── */
.footer {
border-top: 1px solid var(--ifm-color-emphasis-200);
padding: 2rem 0;
}
.footer__title {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 600;
color: var(--ifm-color-content-secondary);
}
.footer__link-item {
font-size: 0.875rem;
color: var(--ifm-color-content-secondary);
}
/* ── Doc article ─────────────────────────────────────────── */
.markdown {
--ifm-heading-margin-top: 1.75rem;
}
.markdown > h2:first-child { margin-top: 0; }
/* ── Hero (homepage) ─────────────────────────────────────── */
.hero--primary {
background: linear-gradient(135deg, #0a0a0a 0%, #111827 50%, #0a0a0a 100%);
position: relative;
overflow: hidden;
}
.hero--primary::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 60% 40%, rgba(99,102,241,0.15) 0%, transparent 65%);
pointer-events: none;
}
.hero__title {
font-size: 3rem;
font-weight: 800;
letter-spacing: -0.04em;
background: linear-gradient(135deg, #ffffff 0%, #a5b4fc 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero__subtitle {
color: #a1a1a1;
font-size: 1.1rem;
font-weight: 400;
}
/* ── Badge component ─────────────────────────────────────── */
.badge--status {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 0.2rem 0.6rem;
border-radius: 100px;
border: 1px solid;
}
.badge--wip { color: var(--skic-amber); border-color: var(--skic-amber); background: rgba(245,158,11,0.08); }
.badge--stable { color: var(--skic-green); border-color: var(--skic-green); background: rgba(16,185,129,0.08); }
.badge--planned { color: var(--ifm-color-content-secondary); border-color: var(--ifm-color-emphasis-300); background: transparent; }
/* ── Scrollbar (webkit) ──────────────────────────────────── */
[data-theme='dark'] ::-webkit-scrollbar { width: 6px; height: 6px; }
[data-theme='dark'] ::-webkit-scrollbar-track { background: transparent; }
[data-theme='dark'] ::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
[data-theme='dark'] ::-webkit-scrollbar-thumb:hover { background: #555; }
/* ── Selection ───────────────────────────────────────────── */
::selection { background: rgba(99,102,241,0.25); }
+262 -18
View File
@@ -1,23 +1,267 @@
/** /* ── Hero ────────────────────────────────────────────────── */
* CSS files with the .module.css suffix will be treated as CSS modules .hero {
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative; position: relative;
overflow: hidden;
}
@media screen and (max-width: 996px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-height: 480px;
padding: 5rem 1.5rem;
background: #0a0a0a;
overflow: hidden;
}
.heroGlow {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 50% 30%, rgba(99,102,241,0.18) 0%, transparent 70%),
radial-gradient(ellipse 40% 30% at 80% 70%, rgba(16,185,129,0.06) 0%, transparent 60%);
pointer-events: none;
}
.heroContent {
position: relative;
text-align: center;
max-width: 680px;
}
.heroPill {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #818cf8;
background: rgba(99,102,241,0.1);
border: 1px solid rgba(99,102,241,0.25);
border-radius: 100px;
padding: 0.3rem 0.9rem;
margin-bottom: 1.5rem;
}
.heroTitle {
font-size: clamp(2.5rem, 5vw, 3.75rem) !important;
font-weight: 800 !important;
letter-spacing: -0.04em !important;
line-height: 1.1 !important;
background: linear-gradient(135deg, #ffffff 0%, #a5b4fc 60%, #818cf8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 1.25rem !important;
border-bottom: none !important;
}
.heroSubtitle {
font-size: 1.1rem;
color: #a1a1a1;
line-height: 1.65;
margin-bottom: 2rem;
max-width: 520px;
margin-left: auto;
margin-right: auto;
}
.heroCta {
display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
}
.btnPrimary {
background: #6366f1 !important;
color: #fff !important;
border: none !important;
padding: 0.65rem 1.5rem !important;
border-radius: 8px !important;
font-weight: 600 !important;
font-size: 0.9rem !important;
transition: background 0.15s ease, transform 0.15s ease !important;
}
.btnPrimary:hover {
background: #4f46e5 !important;
transform: translateY(-1px);
}
.btnSecondary {
background: transparent !important;
color: #a1a1a1 !important;
border: 1px solid #333 !important;
padding: 0.65rem 1.5rem !important;
border-radius: 8px !important;
font-weight: 500 !important;
font-size: 0.9rem !important;
transition: all 0.15s ease !important;
}
.btnSecondary:hover {
border-color: #555 !important;
color: #fff !important;
}
/* ── Main ────────────────────────────────────────────────── */
.main {
background: #0a0a0a;
}
.section {
max-width: 1100px;
margin: 0 auto;
padding: 4rem 1.5rem;
}
.sectionDivider {
border-top: 1px solid #1a1a1a;
}
.sectionHeader {
margin-bottom: 2.5rem;
}
.sectionTitle {
font-size: 1.6rem !important;
font-weight: 700 !important;
letter-spacing: -0.02em !important;
color: #ededed !important;
border-bottom: none !important;
margin-bottom: 0.5rem !important;
}
.sectionSubtitle {
color: #a1a1a1;
font-size: 0.95rem;
margin: 0;
}
/* ── App Cards ───────────────────────────────────────────── */
.appGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1rem;
}
.appCard {
display: block;
background: #111111;
border: 1px solid #222222;
border-radius: 12px;
padding: 1.5rem;
text-decoration: none !important;
transition: all 0.15s ease;
}
.appCard:hover {
border-color: #6366f1;
background: #161625;
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(99,102,241,0.12);
}
.appCardHeader {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.appCardTitle {
font-size: 1rem !important;
font-weight: 600 !important;
color: #ededed !important;
margin: 0 !important;
border-bottom: none !important;
}
.appCardDesc {
font-size: 0.875rem;
color: #a1a1a1;
line-height: 1.65;
margin-bottom: 1rem;
}
/* ── Badges ──────────────────────────────────────────────── */
.badge {
flex-shrink: 0;
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 0.2rem 0.55rem;
border-radius: 100px;
border: 1px solid;
white-space: nowrap;
}
.badgeStable { color: #10b981; border-color: #10b981; background: rgba(16,185,129,0.08); }
.badgeWip { color: #f59e0b; border-color: #f59e0b; background: rgba(245,158,11,0.08); }
.badgePlanned { color: #666; border-color: #333; background: transparent; }
/* ── Tags ────────────────────────────────────────────────── */
.tagList {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.tag {
font-size: 0.7rem;
font-weight: 500;
color: #666;
background: #1a1a1a;
border: 1px solid #2a2a2a;
border-radius: 5px;
padding: 0.15rem 0.5rem;
font-family: var(--ifm-font-family-monospace);
}
/* ── C4 Grid ─────────────────────────────────────────────── */
.c4Grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
@media (max-width: 768px) {
.c4Grid { grid-template-columns: 1fr; }
.appGrid { grid-template-columns: 1fr; }
}
.c4Card {
background: #111111;
border: 1px solid #222222;
border-radius: 12px;
padding: 1.5rem;
}
.c4Level {
display: inline-block;
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #6366f1;
background: rgba(99,102,241,0.1);
border: 1px solid rgba(99,102,241,0.2);
border-radius: 5px;
padding: 0.15rem 0.5rem;
margin-bottom: 0.75rem;
font-family: var(--ifm-font-family-monospace);
}
.c4Name {
font-size: 1rem !important;
font-weight: 600 !important;
color: #ededed !important;
margin-bottom: 0.5rem !important;
border-bottom: none !important;
}
.c4Desc {
font-size: 0.85rem;
color: #a1a1a1;
line-height: 1.6;
margin: 0;
} }
+103 -27
View File
@@ -1,43 +1,119 @@
import type {ReactNode} from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout'; import Layout from '@theme/Layout';
import HomepageFeatures from '@site/src/components/HomepageFeatures';
import Heading from '@theme/Heading'; import Heading from '@theme/Heading';
import styles from './index.module.css'; import styles from './index.module.css';
function HomepageHeader() { type AppStatus = 'stable' | 'wip' | 'planned';
const {siteConfig} = useDocusaurusContext();
type Application = {
name: string;
description: string;
status: AppStatus;
link: string;
tags: string[];
};
const applications: Application[] = [
{
name: 'Stock Market Professional',
description: 'Monitors forex & stock markets, runs technical analysis to spot patterns, and emits daily trading signals with max 3 positions / 30-minute hold.',
status: 'wip',
link: '/docs/applications/stock-market-professional',
tags: ['Python', 'Forex', 'TA', 'Signals'],
},
];
const statusLabels: Record<AppStatus, string> = {
stable: '✦ Stable',
wip: '◎ In Development',
planned: '◇ Planned',
};
const statusClass: Record<AppStatus, string> = {
stable: styles.badgeStable,
wip: styles.badgeWip,
planned: styles.badgePlanned,
};
function AppCard({ name, description, status, link, tags }: Application) {
return ( return (
<header className={clsx('hero hero--primary', styles.heroBanner)}> <Link to={link} className={styles.appCard}>
<div className="container"> <div className={styles.appCardHeader}>
<Heading as="h1" className="hero__title"> <Heading as="h3" className={styles.appCardTitle}>{name}</Heading>
{siteConfig.title} <span className={clsx(styles.badge, statusClass[status])}>
</Heading> {statusLabels[status]}
<p className="hero__subtitle">{siteConfig.tagline}</p> </span>
<div className={styles.buttons}> </div>
<Link <p className={styles.appCardDesc}>{description}</p>
className="button button--secondary button--lg" <div className={styles.tagList}>
to="/docs/intro"> {tags.map(t => <span key={t} className={styles.tag}>{t}</span>)}
Docusaurus Tutorial - 5min ⏱️ </div>
</Link> </Link>
</div>
</div>
</header>
); );
} }
export default function Home(): ReactNode { function Hero() {
const {siteConfig} = useDocusaurusContext();
return ( return (
<Layout <div className={styles.hero}>
title={`Hello from ${siteConfig.title}`} <div className={styles.heroGlow} />
description="Description will go into a meta tag in <head />"> <div className={styles.heroContent}>
<HomepageHeader /> <div className={styles.heroPill}>Autonomous · Self-Documenting</div>
<main> <Heading as="h1" className={styles.heroTitle}>
<HomepageFeatures /> SKIC Playground
</Heading>
<p className={styles.heroSubtitle}>
Documentation for autonomous applications — designed, built, and operated by Jarvis.
</p>
<div className={styles.heroCta}>
<Link className={clsx('button', styles.btnPrimary)} to="/docs/intro">
Browse Docs
</Link>
<Link className={clsx('button', styles.btnSecondary)} href="https://gitea.lego-cloud.eu/skic-v1-playground">
View Source ↗
</Link>
</div>
</div>
</div>
);
}
export default function Home(): JSX.Element {
const { siteConfig } = useDocusaurusContext();
return (
<Layout title={siteConfig.title} description={siteConfig.tagline}>
<Hero />
<main className={styles.main}>
<section className={styles.section}>
<div className={styles.sectionHeader}>
<Heading as="h2" className={styles.sectionTitle}>Applications</Heading>
<p className={styles.sectionSubtitle}>Production-grade systems built and documented autonomously.</p>
</div>
<div className={styles.appGrid}>
{applications.map(app => <AppCard key={app.name} {...app} />)}
</div>
</section>
<section className={clsx(styles.section, styles.sectionDivider)}>
<div className={styles.sectionHeader}>
<Heading as="h2" className={styles.sectionTitle}>Architecture Approach</Heading>
<p className={styles.sectionSubtitle}>Every application is documented using the C4 model.</p>
</div>
<div className={styles.c4Grid}>
{[
{ level: 'L1', name: 'Context', desc: 'System in the real world — users, external systems, and high-level relationships.' },
{ level: 'L2', name: 'Containers', desc: 'Deployable units — applications, services, databases, and their interactions.' },
{ level: 'L3', name: 'Components', desc: 'Internal structure of each container — modules, classes, and responsibilities.' },
].map(({ level, name, desc }) => (
<div key={level} className={styles.c4Card}>
<span className={styles.c4Level}>{level}</span>
<Heading as="h3" className={styles.c4Name}>{name}</Heading>
<p className={styles.c4Desc}>{desc}</p>
</div>
))}
</div>
</section>
</main> </main>
</Layout> </Layout>
); );
+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>