Add humanoid robotics guide, SVG diagrams, and NO ASCII rule

- Add comprehensive humanoid robotics guide (humanoid.md) with:
  - Supported robot kits (Unitree, UBTECH, Poppy, InMoov, ROBOTIS)
  - BASIC keywords for servo/movement control
  - Computer vision integration (face, object, gesture detection)
  - LLM-driven conversational movement
  - Build guides for beginner to advanced
  - Safety considerations and complete reception robot example

- Create humanoid-architecture.svg with theme transparency

- Update PROMPT.md with NO ASCII DIAGRAMS rule

- Update devices README to include humanoid robotics section

- SVG diagrams for chapter 13 devices

- Update documentation style guide with theme transparency
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-12-12 20:36:34 -03:00
parent 0f803a2c7f
commit 91a6710c52
22 changed files with 2626 additions and 312 deletions

131
PROMPT.md
View file

@ -328,18 +328,135 @@ When documenting features, verify against actual source:
---
## Diagram Guidelines
## NO ASCII DIAGRAMS — MANDATORY
For SVG diagrams in `src/assets/`:
**NEVER use ASCII art diagrams in documentation. ALL diagrams must be SVG.**
### Prohibited ASCII Patterns
```
- Transparent background
- Dual-theme support (light/dark CSS)
- Width: 1040-1400px
- Font: Arial, sans-serif
- Colors: Blue #4A90E2, Orange #F5A623, Purple #BD10E0, Green #7ED321
❌ NEVER USE:
┌─────────┐ ╔═══════╗ +-------+
│ Box │ ║ Box ║ | Box |
└─────────┘ ╚═══════╝ +-------+
├── folder/ │ → ▶ ──►
│ └── file ▼ ← ◀ ◄──
```
### What to Use Instead
| Instead of... | Use... |
|---------------|--------|
| ASCII box diagrams | SVG diagrams in `assets/` |
| ASCII flow charts | SVG with arrows and boxes |
| ASCII directory trees | Markdown tables or SVG |
| ASCII tables with borders | Markdown tables |
| ASCII progress indicators | SVG or text description |
### Directory Trees → Tables
```markdown
❌ WRONG:
mybot.gbai/
├── mybot.gbdialog/
│ └── start.bas
└── mybot.gbot/
└── config.csv
✅ CORRECT:
| Path | Description |
|------|-------------|
| `mybot.gbai/` | Root package |
| `mybot.gbdialog/` | BASIC scripts |
| `mybot.gbdialog/start.bas` | Entry point |
| `mybot.gbot/` | Configuration |
| `mybot.gbot/config.csv` | Bot settings |
```
### Rationale
- ASCII breaks in different fonts/editors
- Not accessible for screen readers
- Cannot adapt to dark/light themes
- Looks unprofessional in modern docs
- SVGs scale perfectly at any size
---
## SVG Diagram Guidelines — Theme Transparency
All SVG diagrams MUST be theme-agnostic and work with light/dark modes.
### Required Structure
Every SVG must include:
1. **CSS classes for text** (not hardcoded colors)
2. **Dark mode media query** in `<style>` block
3. **Standard gradient definitions** in `<defs>`
### Required CSS Block
```xml
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
```
### Standard Gradients (use these IDs)
| Gradient ID | Colors | Purpose |
|-------------|--------|---------|
| `primaryGrad` | `#6366F1``#8B5CF6` | Primary/purple elements |
| `cyanGrad` | `#06B6D4``#0EA5E9` | Cyan/info elements |
| `greenGrad` | `#10B981``#34D399` | Success/green elements |
| `orangeGrad` | `#F59E0B``#FBBF24` | Warning/orange elements |
### Background Rules
- Use `fill="#FAFBFC"` for main background (not pure white)
- Add dot pattern overlay: `fill="url(#dots)"` with `opacity="0.08"`
- Cards use `fill="#FFFFFF"` with `stroke="#E2E8F0"` for definition
- Use `filter="url(#cardShadow)"` for card depth
### DO NOT
- ❌ Hardcode text colors without CSS class
- ❌ Use pure black (`#000000`) for text
- ❌ Forget the `@media (prefers-color-scheme: dark)` block
- ❌ Create new gradient IDs when standard ones exist
- ❌ Use ASCII art diagrams when SVG is appropriate
### DO
- ✅ Use CSS classes for ALL text elements
- ✅ Include dark mode media query in every SVG
- ✅ Use standard gradient IDs consistently
- ✅ Test SVGs in both light and dark browser modes
- ✅ Convert ASCII diagrams to proper SVGs
- ✅ Place SVGs in `src/assets/chapter-XX/` directories
### Dimensions
- Width: 600-1200px (responsive)
- Use `style="max-width: 100%; height: auto;"` when embedding
### Reference
See `src/16-appendix-docs-style/svg.md` for complete guidelines.
---
## Translation Workflow

View file

@ -10,26 +10,7 @@ A strongly-typed, self-hosted conversational platform focused on convention over
## 🏗️ Architecture
```
┌─────────────────────────────────────────────────────────────────────────┐
│ General Bots Platform │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ botapp │ │ botui │ │ botserver │ │
│ │ (Tauri) │───▶│ (Pure Web) │───▶│ (API) │ │
│ │ Desktop │ │ HTMX/HTML │ │ Rust │ │
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ ┌─────▼─────┐ ┌──────▼─────┐ │
│ │ botlib │ │ botbook │ │
│ │ (Shared) │ │ (Docs) │ │
│ └───────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
<img src="src/assets/platform-architecture.svg" alt="General Bots Platform Architecture" style="max-width: 100%; height: auto;">
---

View file

@ -6,24 +6,7 @@
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📚 ADD DOCUMENTS TO KNOWLEDGE BASE │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │Prepare │ │ Upload │ │ Index │ │ Test │ │ │
│ │ │ Docs │ │ Files │ │ KB │ │ KB │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
<img src="../../assets/chapter-04/step-flow-4-steps.svg" alt="Add Documents to KB - 4 Step Process" style="max-width: 100%; height: auto;">
---
@ -57,36 +40,7 @@ Before you begin, make sure you have:
A **Knowledge Base (KB)** is a collection of documents that your bot uses to answer questions. When a user asks something, the bot searches through these documents to find relevant information.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW KNOWLEDGE BASE WORKS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User asks: "What is our refund policy?" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔍 Semantic Search │ │
│ │ Searches through all documents in the knowledge base │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │policies │ │ FAQ │ │ terms │ │
│ │ .pdf │ │ .docx │ │ .md │ │
│ └────┬────┘ └─────────┘ └─────────┘ │
│ │ │
│ ▼ Found match! │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ "Refunds are available within 30 days of purchase..." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Bot answers with context from the document │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
<img src="../../assets/chapter-04/kb-semantic-search-flow.svg" alt="Knowledge Base Semantic Search Flow" style="max-width: 100%; height: auto;">
---

View file

@ -6,24 +6,7 @@
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📱 CONNECT WHATSAPP TO YOUR BOT │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │ Meta │ │ Create │ │Configure│ │ Test │ │ │
│ │ │ Account │ │ App │ │ Bot │ │ Channel │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
<img src="../../assets/chapter-04/step-flow-4-steps.svg" alt="Connect WhatsApp - 4 Step Process" style="max-width: 100%; height: auto;">
---
@ -56,38 +39,7 @@ Before you begin, make sure you have:
## Understanding WhatsApp Integration
```
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW WHATSAPP INTEGRATION WORKS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User sends message on WhatsApp │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ WhatsApp Cloud API │ │
│ │ (Meta's servers receive message) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ │ Webhook │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ General Bots Server │ │
│ │ (Your bot processes the message) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ │ API Call │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ WhatsApp Cloud API │ │
│ │ (Sends reply to user) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ User receives bot response on WhatsApp │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
<img src="../../assets/chapter-04/whatsapp-integration-flow.svg" alt="WhatsApp Integration Flow" style="max-width: 100%; height: auto;">
---

View file

@ -13,26 +13,7 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
- **Education** - Classroom assistants, lab equipment interfaces
- **Healthcare** - Patient check-in, medication reminders
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Embedded GB Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Display │ │ botserver │ │ llama.cpp │ │
│ │ LCD/OLED │────▶│ (Rust) │────▶│ (Local) │ │
│ │ TFT/HDMI │ │ Port 8088 │ │ Port 8080 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ Keyboard │ │ SQLite │ │ TinyLlama │ │
│ │ Buttons │ │ (Data) │ │ GGUF │ │
│ │ Touch │ │ │ │ (~700MB) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
<img src="../assets/chapter-13/embedded-architecture.svg" alt="Embedded GB Architecture" style="max-width: 100%; height: auto;">
## What's in This Chapter
@ -43,6 +24,10 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
- [Supported Hardware](./hardware.md) - SBCs, displays, and peripherals
- [Quick Start](./quick-start.md) - Deploy in 5 minutes
- [Local LLM](./local-llm.md) - Offline AI with llama.cpp
- [Buying Guide](./buying-guide.md) - Choose your first SBC
### Robotics
- [Humanoid Robots](./humanoid.md) - Build intelligent humanoids with CV, LLM, and movement control
### Deployment Options
@ -52,3 +37,4 @@ General Bots can run on any device, from mobile phones to minimal embedded hardw
| **Raspberry Pi** | IoT, displays, terminals | 1GB+ RAM |
| **Orange Pi** | Full offline AI | 4GB+ RAM for LLM |
| **Industrial** | Factory, retail, healthcare | Any ARM/x86 SBC |
| **Humanoid Robots** | Service, reception, research | Servo kit + compute board |

View file

@ -16,24 +16,7 @@ A Single Board Computer (SBC) is a complete computer on a single circuit board.
### Decision Flowchart
```
┌─────────────────────────┐
│ What's your budget? │
└───────────┬─────────────┘
┌───────────────────┼───────────────────┐
▼ ▼ ▼
Under $30 $30-80 $80-150
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Orange Pi │ │ Raspberry Pi │ │ Orange Pi 5 │
│ Zero 3 │ │ 4 / 5 │ │ (with NPU) │
│ │ │ │ │ │
│ Basic display │ │ Full desktop │ │ Local AI/LLM │
│ Simple tasks │ │ Most projects │ │ Edge AI │
└───────────────┘ └───────────────┘ └───────────────┘
```
<img src="../assets/chapter-13/budget-decision-tree.svg" alt="Hardware Budget Decision Tree" style="max-width: 100%; height: auto;">
### Recommended Starter Kits
@ -107,65 +90,61 @@ This board has a 6 TOPS NPU for accelerated AI inference!
Perfect first project - monitor and log temperature!
```
□ Orange Pi Zero 3 (1GB) $20
□ 16GB microSD card $5
□ 5V 2A power supply $5
□ DHT22 temperature sensor $6
□ 0.96" OLED display (I2C) $6
□ Jumper wires (female-female) $3
─────────────
Total: $45
```
| Item | Price |
|------|-------|
| Orange Pi Zero 3 (1GB) | $20 |
| 16GB microSD card | $5 |
| 5V 2A power supply | $5 |
| DHT22 temperature sensor | $6 |
| 0.96" OLED display (I2C) | $6 |
| Jumper wires (female-female) | $3 |
| **Total** | **$45** |
### Smart Doorbell ($70)
AI-powered doorbell with notifications!
```
□ Raspberry Pi Zero 2 W $15
□ Pi Camera Module $25
□ Push button $1
□ Piezo buzzer $2
□ LED (with resistor) $1
□ 16GB microSD card $5
□ 5V 2.5A power supply $8
□ Case $5
□ Jumper wires $3
─────────────
Total: $70
```
| Item | Price |
|------|-------|
| Raspberry Pi Zero 2 W | $15 |
| Pi Camera Module | $25 |
| Push button | $1 |
| Piezo buzzer | $2 |
| LED (with resistor) | $1 |
| 16GB microSD card | $5 |
| 5V 2.5A power supply | $8 |
| Case | $5 |
| Jumper wires | $3 |
| **Total** | **$70** |
### Offline AI Assistant ($150)
Run AI completely offline - no internet needed!
```
□ Orange Pi 5 (8GB RAM) $89
□ 128GB NVMe SSD $20
□ 12V 3A power supply $12
□ 7" HDMI touchscreen $45
□ USB microphone $10
□ Case with fan $15
□ Jumper wires $3
─────────────
Total: ~$195
```
| Item | Price |
|------|-------|
| Orange Pi 5 (8GB RAM) | $89 |
| 128GB NVMe SSD | $20 |
| 12V 3A power supply | $12 |
| 7" HDMI touchscreen | $45 |
| USB microphone | $10 |
| Case with fan | $15 |
| Jumper wires | $3 |
| **Total** | **~$195** |
### Voice-Controlled Lights ($55)
Control your lights by talking!
```
□ Raspberry Pi 4 (2GB) $35
□ 4-channel relay module $6
□ USB microphone $8
□ 16GB microSD card $5
□ 5V 3A power supply $8
□ Jumper wires $3
─────────────
Total: ~$65
```
| Item | Price |
|------|-------|
| Raspberry Pi 4 (2GB) | $35 |
| 4-channel relay module | $6 |
| USB microphone | $8 |
| 16GB microSD card | $5 |
| 5V 3A power supply | $8 |
| Jumper wires | $3 |
| **Total** | **~$65** |
## Where to Buy (By Region)

View file

@ -39,22 +39,7 @@
The Orange Pi 5 with RK3588S is ideal for embedded LLM:
```
┌─────────────────────────────────────────────────────────────┐
│ Orange Pi 5 - Best for Offline AI │
├─────────────────────────────────────────────────────────────┤
│ CPU: Rockchip RK3588S (4x A76 + 4x A55) │
│ NPU: 6 TOPS (Neural Processing Unit) │
│ GPU: Mali-G610 MP4 │
│ RAM: 4GB / 8GB / 16GB LPDDR4X │
│ Storage: M.2 NVMe + eMMC + microSD │
│ │
│ LLM Performance: │
│ ├─ TinyLlama 1.1B Q4: ~8-12 tokens/sec │
│ ├─ Phi-2 2.7B Q4: ~4-6 tokens/sec │
│ └─ With NPU (rkllm): ~20-30 tokens/sec │
└─────────────────────────────────────────────────────────────┘
```
<img src="../assets/chapter-13/orange-pi-5-specs.svg" alt="Orange Pi 5 Specifications" style="max-width: 100%; height: auto;">
## Displays
@ -68,13 +53,7 @@ For text-only interfaces:
| HD44780 20x4 | 20 chars × 4 lines | I2C/GPIO | More context |
| LCD2004 | 20 chars × 4 lines | I2C | Industrial |
**Example output on 16x2:**
```
┌────────────────┐
│> How can I help│
< Processing...
└────────────────┘
```
**Example output on 16x2:** Simple text display showing user prompt and bot status.
### OLED Displays
@ -138,17 +117,7 @@ For low-power, readable in sunlight:
### Buttons & GPIO
```
┌─────────────────────────────────────────────┐
│ Simple 4-Button Interface │
├─────────────────────────────────────────────┤
│ │
│ [◄ PREV] [▲ UP] [▼ DOWN] [► SELECT] │
│ │
│ GPIO 17 GPIO 27 GPIO 22 GPIO 23 │
│ │
└─────────────────────────────────────────────┘
```
<img src="../assets/chapter-13/gpio-button-interface.svg" alt="GPIO Button Interface" style="max-width: 100%; height: auto;">
## Enclosures

654
src/13-devices/humanoid.md Normal file
View file

@ -0,0 +1,654 @@
# Humanoid Robotics with General Bots
Build custom humanoid robots powered by General Bots, using computer vision from botmodels, LLM intelligence from botserver, and BASIC keywords for movement control.
## Overview
General Bots transforms humanoid robot kits into intelligent conversational assistants capable of:
- **Natural conversation** via LLM integration
- **Visual recognition** using botmodels CV pipelines
- **Expressive movement** through BASIC keyword commands
- **Autonomous behavior** with scheduled tasks and triggers
<img src="../assets/chapter-13/humanoid-architecture.svg" alt="Humanoid Robot Architecture" style="max-width: 100%; height: auto;">
## Supported Robot Kits
### Consumer Kits
| Kit | DOF | Height | Price | Best For |
|-----|-----|--------|-------|----------|
| **Unitree G1** | 23 | 127cm | $16,000 | Research, commercial |
| **Unitree H1** | 19 | 180cm | $90,000 | Industrial, research |
| **UBTECH Walker S** | 41 | 145cm | Contact | Enterprise service |
| **Fourier GR-1** | 40 | 165cm | $100,000+ | Research |
| **Agility Digit** | 16 | 175cm | Lease | Warehouse, logistics |
### DIY/Maker Kits
| Kit | DOF | Height | Price | Best For |
|-----|-----|--------|-------|----------|
| **Poppy Humanoid** | 25 | 83cm | $8,000 | Education, research |
| **InMoov** | 22+ | 170cm | $1,500+ | Makers, open source |
| **Reachy** | 14 | Torso | $15,000 | HRI research |
| **NimbRo-OP2X** | 20 | 95cm | $30,000 | RoboCup, research |
| **ROBOTIS OP3** | 20 | 51cm | $11,000 | Education, competition |
### Affordable Entry Points
| Kit | DOF | Height | Price | Best For |
|-----|-----|--------|-------|----------|
| **ROBOTIS MINI** | 16 | 27cm | $500 | Learning, hobby |
| **Lynxmotion SES-V2** | 17 | 40cm | $800 | Education |
| **Hiwonder TonyPi** | 16 | 35cm | $400 | Raspberry Pi projects |
| **XGO-Mini** | 12 | 20cm | $350 | Quadruped + arm |
## Architecture
### System Components
| Component | Role | Technology |
|-----------|------|------------|
| **botserver** | Brain - LLM, conversation, decisions | Rust, Port 8088 |
| **botmodels** | Eyes - CV, face recognition, object detection | Python, ONNX |
| **BASIC Runtime** | Motor control - movement sequences | Keywords → servo commands |
| **Hardware Bridge** | Translation layer | GPIO, Serial, CAN bus |
### Communication Flow
| Step | From | To | Protocol |
|------|------|----|----------|
| 1 | Microphone | botserver | Audio stream / STT |
| 2 | botserver | LLM | HTTP API |
| 3 | LLM response | BASIC interpreter | Internal |
| 4 | BASIC keywords | Hardware bridge | Serial/GPIO |
| 5 | Hardware bridge | Servos | PWM/CAN |
| 6 | Camera | botmodels | Video stream |
| 7 | botmodels | botserver | Detection events |
## Hardware Setup
### Computing Options
| Option | Specs | Use Case |
|--------|-------|----------|
| **Jetson Orin Nano** | 8GB, 40 TOPS | Full CV + local LLM |
| **Raspberry Pi 5** | 8GB, no NPU | Cloud LLM, basic CV |
| **Orange Pi 5** | 8GB, 6 TOPS NPU | Local LLM, basic CV |
| **Intel NUC** | 32GB, x86 | Maximum performance |
### Servo Controllers
| Controller | Channels | Interface | Best For |
|------------|----------|-----------|----------|
| **PCA9685** | 16 PWM | I2C | Small robots |
| **Pololu Maestro** | 6-24 | USB/Serial | Hobby servos |
| **Dynamixel U2D2** | 254 | USB/TTL/RS485 | Dynamixel servos |
| **ODrive** | 2 BLDC | USB/CAN | High-torque joints |
### Sensor Integration
| Sensor | Purpose | Interface |
|--------|---------|-----------|
| USB Camera | Vision, face detection | USB/CSI |
| IMU (MPU6050) | Balance, orientation | I2C |
| ToF (VL53L0X) | Distance sensing | I2C |
| Microphone Array | Voice input, direction | USB/I2S |
| Force Sensors | Grip feedback | ADC |
## BASIC Keywords for Robotics
### Servo Control
```basic
' Initialize servo controller
SERVO INIT "PCA9685", address: 0x40
' Move single servo
SERVO MOVE channel, angle, speed
SERVO MOVE 0, 90, 50 ' Channel 0 to 90° at speed 50
' Move multiple servos simultaneously
SERVO SYNC moves
SERVO SYNC [[0, 90], [1, 45], [2, 135]]
' Read servo position
position = SERVO READ channel
```
### Predefined Movements
```basic
' Humanoid poses
ROBOT POSE "stand"
ROBOT POSE "sit"
ROBOT POSE "wave"
ROBOT POSE "bow"
ROBOT POSE "point", direction: "left"
' Walking
ROBOT WALK steps, direction
ROBOT WALK 5, "forward"
ROBOT WALK 3, "backward"
ROBOT TURN degrees
ROBOT TURN 90 ' Turn right 90°
ROBOT TURN -45 ' Turn left 45°
' Arm movements
ROBOT ARM "left", action: "raise"
ROBOT ARM "right", action: "extend"
ROBOT GRIP "left", "open"
ROBOT GRIP "right", "close"
' Head movements
ROBOT HEAD "nod"
ROBOT HEAD "shake"
ROBOT HEAD "look", pan: 30, tilt: -15
```
### Motion Sequences
```basic
' Define custom sequence
SEQUENCE DEFINE "greeting"
ROBOT POSE "stand"
WAIT 500
ROBOT HEAD "look", pan: 0, tilt: 0
ROBOT ARM "right", action: "wave"
WAIT 1000
ROBOT POSE "bow"
WAIT 800
ROBOT POSE "stand"
SEQUENCE END
' Play sequence
SEQUENCE PLAY "greeting"
' Interrupt sequence
SEQUENCE STOP
```
### Balance and Safety
```basic
' Enable balance control
ROBOT BALANCE ON
ROBOT BALANCE OFF
' Set safety limits
ROBOT LIMITS torque: 80, speed: 70
' Emergency stop
ROBOT STOP
' Check stability
stable = ROBOT STABLE
IF NOT stable THEN
ROBOT POSE "stable"
END IF
```
## Computer Vision Integration
### Face Detection and Recognition
```basic
' Start face detection
CV START "face_detection"
' Wait for face
face = CV DETECT "face", timeout: 5000
IF face THEN
' Get face details
name = face.identity
emotion = face.emotion
direction = face.direction
' React to person
ROBOT HEAD "look", pan: direction.x, tilt: direction.y
IF name <> "unknown" THEN
TALK "Hello " + name + "!"
SEQUENCE PLAY "greeting"
ELSE
TALK "Hello! I don't think we've met."
END IF
END IF
```
### Object Detection
```basic
' Detect objects in view
objects = CV DETECT "objects"
FOR EACH obj IN objects
IF obj.class = "cup" THEN
' Point at the cup
ROBOT ARM "right", action: "point"
ROBOT HEAD "look", pan: obj.x, tilt: obj.y
TALK "I see a " + obj.color + " cup"
END IF
NEXT
```
### Gesture Recognition
```basic
' Enable gesture detection
CV START "gesture_detection"
' React to gestures
gesture = CV DETECT "gesture", timeout: 10000
SELECT CASE gesture.type
CASE "wave"
SEQUENCE PLAY "wave_back"
CASE "thumbs_up"
ROBOT POSE "thumbs_up"
TALK "Great!"
CASE "come_here"
ROBOT WALK 3, "forward"
CASE "stop"
ROBOT STOP
END SELECT
```
### Following Behavior
```basic
' Follow a person
CV START "person_tracking"
WHILE TRUE
person = CV TRACK "person"
IF person THEN
' Keep person centered
IF person.x < -20 THEN
ROBOT TURN -10
ELSE IF person.x > 20 THEN
ROBOT TURN 10
END IF
' Maintain distance
IF person.distance < 1.0 THEN
ROBOT WALK 1, "backward"
ELSE IF person.distance > 2.0 THEN
ROBOT WALK 1, "forward"
END IF
ELSE
' Lost tracking, look around
ROBOT HEAD "look", pan: 45, tilt: 0
WAIT 1000
ROBOT HEAD "look", pan: -45, tilt: 0
WAIT 1000
END IF
WAIT 100
WEND
```
## LLM-Driven Behavior
### Conversational Movement
```basic
' System prompt for embodied AI
system_prompt = "You are a helpful humanoid robot assistant. "
system_prompt = system_prompt + "When responding, include movement commands in brackets. "
system_prompt = system_prompt + "Available: [wave], [nod], [shake_head], [bow], [point_left], [point_right], [think]"
SET SYSTEM PROMPT system_prompt
' Process conversation with movements
response = LLM "How can I help you today?"
' Parse and execute movements
movements = EXTRACT_BRACKETS response
FOR EACH move IN movements
SELECT CASE move
CASE "wave"
SEQUENCE PLAY "wave"
CASE "nod"
ROBOT HEAD "nod"
CASE "shake_head"
ROBOT HEAD "shake"
CASE "bow"
ROBOT POSE "bow"
CASE "point_left"
ROBOT ARM "left", action: "point"
CASE "point_right"
ROBOT ARM "right", action: "point"
CASE "think"
ROBOT HEAD "look", pan: 15, tilt: 15
END SELECT
NEXT
' Speak cleaned response
clean_response = REMOVE_BRACKETS response
TALK clean_response
```
### Autonomous Behavior Loop
```basic
' Main behavior loop
WHILE TRUE
' Check for voice input
input = HEAR timeout: 5000
IF input <> "" THEN
' Process with LLM
response = LLM input
ProcessResponse response
ELSE
' Idle behaviors
idle_action = RANDOM 1, 5
SELECT CASE idle_action
CASE 1
' Look around
ROBOT HEAD "look", pan: RANDOM -30, 30, tilt: 0
CASE 2
' Check for faces
face = CV DETECT "face", timeout: 1000
IF face THEN
TALK "Hello there!"
SEQUENCE PLAY "greeting"
END IF
CASE 3
' Slight movement
ROBOT POSE "relax"
CASE 4, 5
' Do nothing
END SELECT
END IF
WAIT 1000
WEND
```
## Configuration
### Robot Definition File
Create `robot.csv` in your `.gbot` folder:
| name | value |
|------|-------|
| robot_type | humanoid |
| controller | PCA9685 |
| controller_address | 0x40 |
| servo_count | 16 |
| balance_enabled | true |
| cv_enabled | true |
| cv_model | yolov8n |
| face_model | arcface |
### Servo Mapping
Create `servos.csv` to map joints to channels:
| joint | channel | min_angle | max_angle | home | inverted |
|-------|---------|-----------|-----------|------|----------|
| head_pan | 0 | -90 | 90 | 0 | false |
| head_tilt | 1 | -45 | 45 | 0 | false |
| left_shoulder | 2 | -90 | 180 | 0 | false |
| left_elbow | 3 | 0 | 135 | 90 | false |
| left_wrist | 4 | -90 | 90 | 0 | false |
| right_shoulder | 5 | -90 | 180 | 0 | true |
| right_elbow | 6 | 0 | 135 | 90 | true |
| right_wrist | 7 | -90 | 90 | 0 | true |
| left_hip | 8 | -45 | 45 | 0 | false |
| left_knee | 9 | 0 | 90 | 0 | false |
| left_ankle | 10 | -30 | 30 | 0 | false |
| right_hip | 11 | -45 | 45 | 0 | true |
| right_knee | 12 | 0 | 90 | 0 | true |
| right_ankle | 13 | -30 | 30 | 0 | true |
| left_grip | 14 | 0 | 90 | 0 | false |
| right_grip | 15 | 0 | 90 | 0 | false |
### Motion Files
Create `motions.csv` for predefined sequences:
| name | frames |
|------|--------|
| wave | [{"t":0,"joints":{"right_shoulder":90}},{"t":500,"joints":{"right_shoulder":120}},{"t":1000,"joints":{"right_shoulder":90}}] |
| bow | [{"t":0,"joints":{"head_tilt":0}},{"t":500,"joints":{"head_tilt":-30}},{"t":1500,"joints":{"head_tilt":0}}] |
| nod | [{"t":0,"joints":{"head_tilt":0}},{"t":200,"joints":{"head_tilt":-15}},{"t":400,"joints":{"head_tilt":10}},{"t":600,"joints":{"head_tilt":0}}] |
## Build Guides
### Beginner: Desktop Companion
**Budget: ~$600**
| Component | Model | Price |
|-----------|-------|-------|
| Robot Kit | ROBOTIS MINI | $500 |
| Compute | Raspberry Pi 5 4GB | $60 |
| Camera | Pi Camera v3 | $25 |
| Microphone | USB mic | $15 |
**Capabilities:**
- Voice conversation via cloud LLM
- Basic face detection
- Gesture responses
- Desktop assistant
### Intermediate: Service Robot
**Budget: ~$10,000**
| Component | Model | Price |
|-----------|-------|-------|
| Robot Kit | Poppy Humanoid | $8,000 |
| Compute | Jetson Orin Nano | $500 |
| Camera | Intel RealSense D435 | $300 |
| Microphone | ReSpeaker Array | $100 |
| Speakers | USB powered | $50 |
**Capabilities:**
- Local LLM (Llama 3.2 3B)
- Full body tracking
- Object manipulation
- Walking and navigation
### Advanced: Research Platform
**Budget: ~$20,000+**
| Component | Model | Price |
|-----------|-------|-------|
| Robot Kit | Unitree G1 EDU | $16,000 |
| Compute | Onboard + Workstation | $3,000 |
| Sensors | LiDAR + Depth cameras | $1,000 |
**Capabilities:**
- Full autonomous navigation
- Complex manipulation
- Multi-modal interaction
- Research-grade motion control
## Safety Considerations
### Physical Safety
- Always have emergency stop accessible
- Set conservative torque limits initially
- Test movements at reduced speed first
- Ensure stable base before walking
- Keep clear area around robot
### Software Safety
```basic
' Always wrap motion code in error handling
ON ERROR GOTO SafetyStop
' Motion code here...
END
SafetyStop:
ROBOT STOP
ROBOT POSE "safe"
LOG ERROR "Emergency stop: " + ERROR MESSAGE
TALK "I need to stop for safety"
```
### Operating Guidelines
| Situation | Action |
|-----------|--------|
| Unknown obstacle detected | Stop, assess, navigate |
| Balance threshold exceeded | Widen stance or sit |
| Person too close | Step back, warn verbally |
| Battery low | Announce, return to charger |
| Communication lost | Safe pose, wait for reconnect |
## Example: Reception Robot
Complete example of a reception desk humanoid:
```basic
' reception-robot.bas
' A humanoid reception assistant
' Initialize systems
SERVO INIT "PCA9685", address: 0x40
CV START "face_detection"
ROBOT POSE "stand"
ROBOT BALANCE ON
' Set personality
SET SYSTEM PROMPT "You are a friendly reception robot at TechCorp. "
SET SYSTEM PROMPT SYSTEM PROMPT + "Greet visitors, answer questions about the company, "
SET SYSTEM PROMPT SYSTEM PROMPT + "and direct them to the right department. "
SET SYSTEM PROMPT SYSTEM PROMPT + "Be warm and professional. Use [wave], [nod], [point_left], [point_right] for gestures."
' Load company knowledge
USE KB "company-info"
' Main loop
WHILE TRUE
' Watch for approaching people
face = CV DETECT "face", timeout: 2000
IF face THEN
' Turn to face person
ROBOT HEAD "look", pan: face.direction.x, tilt: face.direction.y
' Check if known
IF face.identity <> "unknown" THEN
TALK "Welcome back, " + face.identity + "!"
SEQUENCE PLAY "wave"
ELSE
TALK "Hello! Welcome to TechCorp. How can I help you today?"
SEQUENCE PLAY "greeting"
END IF
' Enter conversation mode
ConversationLoop
END IF
' Idle animation
IdleBehavior
WAIT 500
WEND
SUB ConversationLoop
silence_count = 0
WHILE silence_count < 3
input = HEAR timeout: 10000
IF input <> "" THEN
silence_count = 0
' Process with LLM
response = LLM input
' Execute gestures
ExecuteGestures response
' Speak response
TALK REMOVE_BRACKETS response
' Check for directions
IF CONTAINS response, "point_left" THEN
TALK "The elevators are to your left"
ELSE IF CONTAINS response, "point_right" THEN
TALK "You'll find that department to your right"
END IF
ELSE
silence_count = silence_count + 1
IF silence_count = 2 THEN
TALK "Is there anything else I can help with?"
ROBOT HEAD "tilt", angle: 10
END IF
END IF
WEND
TALK "Have a great day!"
SEQUENCE PLAY "wave"
END SUB
SUB IdleBehavior
action = RANDOM 1, 10
SELECT CASE action
CASE 1
ROBOT HEAD "look", pan: RANDOM -20, 20, tilt: 0
CASE 2
ROBOT POSE "relax"
CASE 3, 4, 5, 6, 7, 8, 9, 10
' Mostly stay still
END SELECT
END SUB
SUB ExecuteGestures response
IF CONTAINS response, "[wave]" THEN
SEQUENCE PLAY "wave"
END IF
IF CONTAINS response, "[nod]" THEN
ROBOT HEAD "nod"
END IF
IF CONTAINS response, "[point_left]" THEN
ROBOT ARM "left", action: "point"
END IF
IF CONTAINS response, "[point_right]" THEN
ROBOT ARM "right", action: "point"
END IF
END SUB
```
## Resources
### Robot Kits
- [Unitree Robotics](https://www.unitree.com/) - G1, H1 humanoids
- [ROBOTIS](https://www.robotis.com/) - OP3, MINI, Dynamixel
- [Poppy Project](https://www.poppy-project.org/) - Open source humanoid
- [InMoov](https://inmoov.fr/) - 3D printable humanoid
- [Pollen Robotics](https://www.pollen-robotics.com/) - Reachy
### Components
- [Dynamixel](https://www.robotis.us/dynamixel/) - Smart servos
- [ODrive](https://odriverobotics.com/) - BLDC motor control
- [Intel RealSense](https://www.intelrealsense.com/) - Depth cameras
- [NVIDIA Jetson](https://developer.nvidia.com/embedded/jetson-modules) - Edge AI
### Learning
- [ROS 2 Humanoid](https://docs.ros.org/) - Robot Operating System
- [MoveIt](https://moveit.ros.org/) - Motion planning
- [OpenCV](https://opencv.org/) - Computer vision
- [MediaPipe](https://mediapipe.dev/) - Body/hand tracking

View file

@ -4,24 +4,7 @@ Run AI inference completely offline on embedded devices. No internet, no API cos
## Overview
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ Local LLM Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User Input ──▶ botserver ──▶ llama.cpp ──▶ Response │
│ │ │ │
│ │ ┌────┴────┐ │
│ │ │ Model │ │
│ │ │ GGUF │ │
│ │ │ (Q4_K) │ │
│ │ └─────────┘ │
│ │ │
│ SQLite DB │
│ (sessions) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
<img src="../assets/chapter-13/local-llm-architecture.svg" alt="Local LLM Architecture" style="max-width: 100%; height: auto;">
## Recommended Models

View file

@ -6,27 +6,7 @@ Deploy General Bots as the primary interface on Android and HarmonyOS devices, t
BotDevice transforms any Android or HarmonyOS device into a dedicated General Bots system, removing manufacturer bloatware and installing GB as the default launcher.
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ BotDevice Architecture │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ BotDevice App (Tauri) │ │
│ ├──────────────────────────────────────────────────────────────────┤ │
│ │ botui/ui/suite │ Tauri Android │ src/lib.rs (Rust) │ │
│ │ (Web Interface) │ (WebView + NDK) │ (Backend + Hardware) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────┴────────────────────────────┐ │
│ │ Android/HarmonyOS System │ │
│ │ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌─────────┐ │ │
│ │ │ Camera │ │ GPS │ │ WiFi │ │ Storage │ │ │
│ │ └─────────┘ └──────────┘ └────────┘ └─────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
<img src="../assets/chapter-13/botdevice-architecture.svg" alt="BotDevice Architecture" style="max-width: 100%; height: auto;">
## Supported Platforms
@ -226,38 +206,23 @@ adb reboot
## Project Structure
```
botdevice/
├── Cargo.toml # Rust/Tauri dependencies
├── tauri.conf.json # Tauri config → botui/ui/suite
├── build.rs # Build script
├── src/lib.rs # Android entry point
├── icons/
│ ├── gb-bot.svg # Source icon
│ ├── icon.png # Main icon (512x512)
│ └── */ic_launcher.png # Icons by density
├── scripts/
│ ├── generate-icons.sh # Generate PNGs from SVG
│ └── create-bootanimation.sh
├── capabilities/
│ └── default.json # Tauri permissions
├── gen/android/ # Generated Android project
│ └── app/src/main/
│ ├── AndroidManifest.xml
│ └── res/values/themes.xml
└── rom/ # Installation tools
├── install.sh # Interactive installer
├── scripts/
│ ├── debloat.sh # Remove bloatware
│ └── build-magisk-module.sh
└── gsi/
└── README.md # GSI instructions
```
| Path | Description |
|------|-------------|
| `Cargo.toml` | Rust/Tauri dependencies |
| `tauri.conf.json` | Tauri config → botui/ui/suite |
| `build.rs` | Build script |
| `src/lib.rs` | Android entry point |
| `icons/gb-bot.svg` | Source icon |
| `icons/icon.png` | Main icon (512x512) |
| `icons/*/ic_launcher.png` | Icons by density |
| `scripts/generate-icons.sh` | Generate PNGs from SVG |
| `scripts/create-bootanimation.sh` | Boot animation generator |
| `capabilities/default.json` | Tauri permissions |
| `gen/android/` | Generated Android project |
| `rom/install.sh` | Interactive installer |
| `rom/scripts/debloat.sh` | Remove bloatware |
| `rom/scripts/build-magisk-module.sh` | Magisk module builder |
| `rom/gsi/README.md` | GSI instructions |
## Offline Mode

View file

@ -38,6 +38,104 @@ Use standard HTML image syntax with responsive styling:
---
## SVG Theme Transparency Guidelines
All SVGs MUST be theme-agnostic and work with light/dark modes. Follow these requirements:
### Required CSS Media Query
Every SVG must include a `<style>` block with `prefers-color-scheme` support:
```xml
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
```
### Background Transparency
- Use `fill="#FAFBFC"` for light backgrounds (subtle, not pure white)
- Add dot pattern overlay for texture: `fill="url(#dots)"` with low opacity
- Cards use `fill="#FFFFFF"` with border strokes for definition
- **NEVER** use pure black (`#000000`) backgrounds
### Color Palette (Theme-Safe)
| Purpose | Light Mode | Dark Mode Adaptation |
|---------|------------|---------------------|
| Title text | `#1E1B4B` | `#F1F5F9` (via CSS) |
| Main text | `#334155` | `#E2E8F0` (via CSS) |
| Secondary text | `#64748B` | `#94A3B8` (via CSS) |
| Mono/code text | `#475569` | `#CBD5E1` (via CSS) |
| Card backgrounds | `#FFFFFF` | Keep white (contrast) |
| Borders | `#E2E8F0` | Keep (works both) |
### Standard Gradients
Use these gradient IDs consistently across all SVGs:
```xml
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
```
### DO NOT
- ❌ Hardcode text colors without CSS class
- ❌ Use pure black (`#000000`) or pure white (`#FFFFFF`) for text
- ❌ Forget the `@media (prefers-color-scheme: dark)` block
- ❌ Use opaque backgrounds that don't adapt
- ❌ Create new gradient IDs when standard ones exist
### DO
- ✅ Use CSS classes for all text elements
- ✅ Include dark mode media query in every SVG
- ✅ Use standard gradient IDs from the palette
- ✅ Test SVGs in both light and dark browser modes
- ✅ Use subtle shadows with low opacity (`0.15`)
- ✅ Keep white cards for contrast in both modes
---
## Conversation Examples (WhatsApp Style)
All conversation examples throughout the book use the WhatsApp-style HTML format. This provides a familiar, visually consistent representation of bot interactions.

View file

@ -0,0 +1,144 @@
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#10B981"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="550" fill="#FAFBFC"/>
<rect width="900" height="550" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">How Knowledge Base Works</text>
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Semantic search through your documents</text>
<!-- Main Container -->
<rect x="30" y="75" width="840" height="450" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Step 1: User Question -->
<g transform="translate(350, 95)">
<rect x="0" y="0" width="200" height="60" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="25" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="20" width="200" height="5" fill="url(#cyanGrad)"/>
<text x="100" y="17" text-anchor="middle" font-size="10" font-weight="600" class="white-text">User asks:</text>
<text x="100" y="45" text-anchor="middle" font-size="10" class="mono-text">"What is our refund policy?"</text>
</g>
<!-- Arrow down -->
<path d="M450 160 L450 190" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Step 2: Semantic Search -->
<g transform="translate(300, 200)">
<rect x="0" y="0" width="300" height="70" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="300" height="28" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="22" width="300" height="6" fill="url(#primaryGrad)"/>
<text x="150" y="19" text-anchor="middle" font-size="12" font-weight="600" class="white-text">🔍 Semantic Search</text>
<text x="150" y="50" text-anchor="middle" font-size="10" class="secondary-text">Searches through all documents in the knowledge base</text>
</g>
<!-- Arrow down splitting into 3 -->
<path d="M450 275 L450 305" stroke="#8B5CF6" stroke-width="2" fill="none"/>
<path d="M200 305 L700 305" stroke="#8B5CF6" stroke-width="2" fill="none"/>
<path d="M200 305 L200 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<path d="M450 305 L450 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<path d="M700 305 L700 330" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Document Cards -->
<!-- policies.pdf -->
<g transform="translate(120, 340)">
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">policies.pdf</text>
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Refund terms</text>
</g>
<!-- FAQ.docx -->
<g transform="translate(370, 340)">
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">FAQ.docx</text>
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Common questions</text>
</g>
<!-- terms.md -->
<g transform="translate(620, 340)">
<rect x="0" y="0" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<text x="80" y="25" text-anchor="middle" font-size="22">📄</text>
<text x="80" y="48" text-anchor="middle" font-size="11" font-weight="500" class="main-text">terms.md</text>
<text x="80" y="62" text-anchor="middle" font-size="9" class="secondary-text">Legal terms</text>
</g>
<!-- Found match indicator -->
<path d="M200 415 L200 440" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<text x="200" y="432" text-anchor="middle" font-size="9" font-weight="600" style="fill:#10B981;">Found match!</text>
<!-- Result Box -->
<g transform="translate(80, 455)">
<rect x="0" y="0" width="740" height="55" rx="8" fill="#F0FDF4" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<text x="20" y="25" font-size="11" font-weight="600" class="main-text">✅ Match Found:</text>
<text x="20" y="43" font-size="10" class="mono-text">"Refunds are available within 30 days of purchase. Contact support@company.com to initiate..."</text>
</g>
<!-- Legend -->
<g transform="translate(60, 110)">
<rect x="0" y="0" width="160" height="80" rx="6" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<text x="80" y="18" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Process Flow</text>
<circle cx="20" cy="38" r="6" fill="url(#cyanGrad)"/>
<text x="35" y="42" font-size="9" class="secondary-text">User query</text>
<circle cx="20" cy="58" r="6" fill="url(#primaryGrad)"/>
<text x="35" y="62" font-size="9" class="secondary-text">Vector search</text>
<circle cx="100" cy="58" r="6" fill="url(#greenGrad)"/>
<text x="115" y="62" font-size="9" class="secondary-text">Result</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -0,0 +1,110 @@
<svg width="900" height="200" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowGray" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#94A3B8"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="200" fill="#FAFBFC"/>
<rect width="900" height="200" fill="url(#dots)"/>
<!-- Main Container -->
<rect x="20" y="20" width="860" height="160" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Step 1 -->
<g transform="translate(60, 50)">
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#cyanGrad)"/>
<rect x="0" y="26" width="150" height="6" fill="url(#cyanGrad)"/>
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 1</text>
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Prepare</text>
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Gather requirements</text>
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">and resources</text>
</g>
<!-- Arrow 1→2 -->
<path d="M215 100 L260 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
<!-- Step 2 -->
<g transform="translate(270, 50)">
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="26" width="150" height="6" fill="url(#primaryGrad)"/>
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 2</text>
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Configure</text>
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Set up settings</text>
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">and connections</text>
</g>
<!-- Arrow 2→3 -->
<path d="M425 100 L470 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
<!-- Step 3 -->
<g transform="translate(480, 50)">
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#orangeGrad)"/>
<rect x="0" y="26" width="150" height="6" fill="url(#orangeGrad)"/>
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 3</text>
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Deploy</text>
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Execute the</text>
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">implementation</text>
</g>
<!-- Arrow 3→4 -->
<path d="M635 100 L680 100" stroke="#94A3B8" stroke-width="2" fill="none" marker-end="url(#arrowGray)"/>
<!-- Step 4 -->
<g transform="translate(690, 50)">
<rect x="0" y="0" width="150" height="100" rx="10" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="150" height="32" rx="10" fill="url(#greenGrad)"/>
<rect x="0" y="26" width="150" height="6" fill="url(#greenGrad)"/>
<text x="75" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Step 4</text>
<text x="75" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">Test</text>
<text x="75" y="75" text-anchor="middle" font-size="9" class="secondary-text">Verify everything</text>
<text x="75" y="90" text-anchor="middle" font-size="9" class="secondary-text">works correctly</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,152 @@
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="whatsappGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#25D366;stop-opacity:1" />
<stop offset="100%" style="stop-color:#128C7E;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#25D366"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="500" fill="#FAFBFC"/>
<rect width="900" height="500" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">How WhatsApp Integration Works</text>
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Message flow between WhatsApp and General Bots</text>
<!-- Main Container -->
<rect x="30" y="75" width="840" height="400" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Step 1: User sends message -->
<g transform="translate(80, 105)">
<rect x="0" y="0" width="200" height="80" rx="10" fill="#FFFFFF" stroke="#25D366" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="30" rx="10" fill="url(#whatsappGrad)"/>
<rect x="0" y="24" width="200" height="6" fill="url(#whatsappGrad)"/>
<text x="100" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">📱 User</text>
<text x="100" y="52" text-anchor="middle" font-size="10" class="main-text">Sends message on</text>
<text x="100" y="68" text-anchor="middle" font-size="10" font-weight="500" class="main-text">WhatsApp</text>
</g>
<!-- Arrow 1 -->
<path d="M285 145 L340 145" stroke="#25D366" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<text x="312" y="138" text-anchor="middle" font-size="8" class="secondary-text">1</text>
<!-- Step 2: WhatsApp Cloud API receives -->
<g transform="translate(350, 105)">
<rect x="0" y="0" width="200" height="80" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="30" rx="10" fill="url(#cyanGrad)"/>
<rect x="0" y="24" width="200" height="6" fill="url(#cyanGrad)"/>
<text x="100" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WhatsApp Cloud API</text>
<text x="100" y="52" text-anchor="middle" font-size="10" class="main-text">Meta's servers</text>
<text x="100" y="68" text-anchor="middle" font-size="10" class="secondary-text">receive message</text>
</g>
<!-- Arrow 2 (webhook) -->
<path d="M450 190 L450 230" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<text x="470" y="215" font-size="9" class="mono-text">Webhook</text>
<text x="435" y="215" text-anchor="middle" font-size="8" class="secondary-text">2</text>
<!-- Step 3: General Bots Server -->
<g transform="translate(300, 240)">
<rect x="0" y="0" width="300" height="90" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="300" height="32" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="26" width="300" height="6" fill="url(#primaryGrad)"/>
<text x="150" y="22" text-anchor="middle" font-size="12" font-weight="600" class="white-text">General Bots Server</text>
<text x="150" y="55" text-anchor="middle" font-size="10" class="main-text">Your bot processes the message</text>
<text x="150" y="72" text-anchor="middle" font-size="9" class="secondary-text">LLM • Knowledge Base • BASIC Scripts</text>
<text x="150" y="85" text-anchor="middle" font-size="9" class="mono-text">botserver:8080</text>
</g>
<!-- Arrow 3 (API call) -->
<path d="M450 335 L450 375" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<text x="470" y="360" font-size="9" class="mono-text">API Call</text>
<text x="435" y="360" text-anchor="middle" font-size="8" class="secondary-text">3</text>
<!-- Step 4: WhatsApp Cloud API sends reply -->
<g transform="translate(350, 385)">
<rect x="0" y="0" width="200" height="70" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="28" rx="10" fill="url(#cyanGrad)"/>
<rect x="0" y="22" width="200" height="6" fill="url(#cyanGrad)"/>
<text x="100" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WhatsApp Cloud API</text>
<text x="100" y="48" text-anchor="middle" font-size="10" class="main-text">Sends reply to user</text>
</g>
<!-- Arrow 4 (back to user) -->
<path d="M345 420 L285 420 L285 185 L180 185" stroke="#25D366" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<text x="265" y="300" text-anchor="middle" font-size="8" class="secondary-text">4</text>
<!-- Info Box: Required Configuration -->
<g transform="translate(620, 105)">
<rect x="0" y="0" width="220" height="180" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Required Configuration</text>
<text x="15" y="48" font-size="9" font-weight="600" class="secondary-text">Meta Developer Portal:</text>
<text x="15" y="63" font-size="9" class="mono-text">• Phone Number ID</text>
<text x="15" y="78" font-size="9" class="mono-text">• Access Token</text>
<text x="15" y="93" font-size="9" class="mono-text">• Webhook URL</text>
<text x="15" y="118" font-size="9" font-weight="600" class="secondary-text">Bot Settings:</text>
<text x="15" y="133" font-size="9" class="mono-text">• whatsapp_phone_id</text>
<text x="15" y="148" font-size="9" class="mono-text">• whatsapp_token</text>
<text x="15" y="163" font-size="9" class="mono-text">• webhook_verify_token</text>
</g>
<!-- Info Box: Message Types -->
<g transform="translate(620, 300)">
<rect x="0" y="0" width="220" height="145" rx="8" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Supported Messages</text>
<text x="15" y="45" font-size="9" class="secondary-text">✅ Text messages</text>
<text x="15" y="62" font-size="9" class="secondary-text">✅ Images &amp; documents</text>
<text x="15" y="79" font-size="9" class="secondary-text">✅ Location sharing</text>
<text x="15" y="96" font-size="9" class="secondary-text">✅ Interactive buttons</text>
<text x="15" y="113" font-size="9" class="secondary-text">✅ Template messages</text>
<text x="15" y="130" font-size="9" class="secondary-text">✅ Voice messages</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View file

@ -0,0 +1,159 @@
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<linearGradient id="pinkGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#EC4899;stop-opacity:1" />
<stop offset="100%" style="stop-color:#F472B6;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="500" fill="#FAFBFC"/>
<rect width="900" height="500" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">BotDevice Architecture</text>
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Mobile app deployment with Tauri for Android/HarmonyOS</text>
<!-- Main Container -->
<rect x="30" y="75" width="840" height="400" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- BotDevice App Container -->
<g transform="translate(60, 100)">
<rect x="0" y="0" width="780" height="130" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="780" height="35" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="28" width="780" height="7" fill="url(#primaryGrad)"/>
<text x="390" y="24" text-anchor="middle" font-size="13" font-weight="600" class="white-text">BotDevice App (Tauri)</text>
<!-- Three columns inside -->
<!-- botui/ui/suite -->
<g transform="translate(20, 50)">
<rect x="0" y="0" width="230" height="65" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="115" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">botui/ui/suite</text>
<text x="115" y="40" text-anchor="middle" font-size="10" class="secondary-text">(Web Interface)</text>
<text x="115" y="55" text-anchor="middle" font-size="9" class="mono-text">HTML + HTMX + CSS</text>
</g>
<!-- Tauri Android -->
<g transform="translate(270, 50)">
<rect x="0" y="0" width="230" height="65" rx="6" fill="#F0FDF4" stroke="#A7F3D0" stroke-width="1"/>
<text x="115" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Tauri Android</text>
<text x="115" y="40" text-anchor="middle" font-size="10" class="secondary-text">(WebView + NDK)</text>
<text x="115" y="55" text-anchor="middle" font-size="9" class="mono-text">Native bridge</text>
</g>
<!-- src/lib.rs -->
<g transform="translate(520, 50)">
<rect x="0" y="0" width="240" height="65" rx="6" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
<text x="120" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">src/lib.rs (Rust)</text>
<text x="120" y="40" text-anchor="middle" font-size="10" class="secondary-text">(Backend + Hardware)</text>
<text x="120" y="55" text-anchor="middle" font-size="9" class="mono-text">Business logic</text>
</g>
</g>
<!-- Connector line down -->
<path d="M450 235 L450 265" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Android/HarmonyOS System Container -->
<g transform="translate(60, 275)">
<rect x="0" y="0" width="780" height="180" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="780" height="35" rx="10" fill="#64748B"/>
<rect x="0" y="28" width="780" height="7" fill="#64748B"/>
<text x="390" y="24" text-anchor="middle" font-size="13" font-weight="600" class="white-text">Android / HarmonyOS System</text>
<!-- Hardware capabilities row -->
<!-- Camera -->
<g transform="translate(30, 55)">
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="24" width="160" height="6" fill="url(#cyanGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Camera</text>
<text x="80" y="52" text-anchor="middle" font-size="20">📷</text>
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Photo/Video</text>
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">QR Scanning</text>
</g>
<!-- GPS -->
<g transform="translate(210, 55)">
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#34D399" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="24" width="160" height="6" fill="url(#greenGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">GPS</text>
<text x="80" y="52" text-anchor="middle" font-size="20">📍</text>
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Location</text>
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">Geofencing</text>
</g>
<!-- WiFi -->
<g transform="translate(390, 55)">
<rect x="0" y="0" width="160" height="105" rx="8" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#primaryGrad)"/>
<rect x="0" y="24" width="160" height="6" fill="url(#primaryGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">WiFi</text>
<text x="80" y="52" text-anchor="middle" font-size="20">📶</text>
<text x="80" y="75" text-anchor="middle" font-size="10" class="main-text">Connectivity</text>
<text x="80" y="92" text-anchor="middle" font-size="9" class="mono-text">Network API</text>
</g>
<!-- Storage -->
<g transform="translate(570, 55)">
<rect x="0" y="0" width="190" height="105" rx="8" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="190" height="30" rx="8" fill="url(#orangeGrad)"/>
<rect x="0" y="24" width="190" height="6" fill="url(#orangeGrad)"/>
<text x="95" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Storage</text>
<text x="95" y="52" text-anchor="middle" font-size="20">💾</text>
<text x="95" y="75" text-anchor="middle" font-size="10" class="main-text">Local Files</text>
<text x="95" y="92" text-anchor="middle" font-size="9" class="mono-text">SQLite / Assets</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,152 @@
<svg width="800" height="450" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowGray" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#94A3B8"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="800" height="450" fill="#FAFBFC"/>
<rect width="800" height="450" fill="url(#dots)"/>
<!-- Title -->
<text x="400" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Hardware Budget Decision Tree</text>
<text x="400" y="55" text-anchor="middle" font-size="12" class="secondary-text">Choose the right single-board computer for your project</text>
<!-- Decision Node: Budget Question -->
<g transform="translate(300, 80)">
<rect x="0" y="0" width="200" height="50" rx="25" fill="url(#primaryGrad)" filter="url(#cardShadow)"/>
<text x="100" y="30" text-anchor="middle" font-size="13" font-weight="600" class="white-text">What's your budget?</text>
</g>
<!-- Connector Lines from Decision -->
<g stroke="#94A3B8" stroke-width="2" fill="none">
<!-- Left branch -->
<path d="M350 130 L350 160 L150 160 L150 190" marker-end="url(#arrowGray)"/>
<!-- Center branch -->
<path d="M400 130 L400 190" marker-end="url(#arrowGray)"/>
<!-- Right branch -->
<path d="M450 130 L450 160 L650 160 L650 190" marker-end="url(#arrowGray)"/>
</g>
<!-- Budget Labels on Lines -->
<text x="200" y="155" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">Under $30</text>
<text x="400" y="175" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">$30 - $80</text>
<text x="600" y="155" text-anchor="middle" font-size="11" font-weight="600" class="secondary-text">$80 - $150</text>
<!-- Option 1: Orange Pi Zero 3 -->
<g transform="translate(50, 200)">
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#orangeGrad)"/>
<rect x="0" y="28" width="200" height="7" fill="url(#orangeGrad)"/>
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Orange Pi Zero 3</text>
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">~$20</text>
<text x="15" y="85" font-size="10" class="secondary-text">• 1GB RAM</text>
<text x="15" y="100" font-size="10" class="secondary-text">• Basic display</text>
<text x="15" y="115" font-size="10" class="secondary-text">• Simple tasks</text>
<rect x="15" y="122" width="170" height="10" rx="3" fill="#FEF3C7"/>
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Entry Level</text>
</g>
<!-- Option 2: Raspberry Pi 4/5 -->
<g transform="translate(300, 200)">
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#greenGrad)"/>
<rect x="0" y="28" width="200" height="7" fill="url(#greenGrad)"/>
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Raspberry Pi 4 / 5</text>
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">$35 - $80</text>
<text x="15" y="85" font-size="10" class="secondary-text">• 2-8GB RAM</text>
<text x="15" y="100" font-size="10" class="secondary-text">• Full desktop</text>
<text x="15" y="115" font-size="10" class="secondary-text">• Most projects</text>
<rect x="15" y="122" width="170" height="10" rx="3" fill="#ECFDF5"/>
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Recommended</text>
</g>
<!-- Option 3: Orange Pi 5 -->
<g transform="translate(550, 200)">
<rect x="0" y="0" width="200" height="140" rx="10" fill="#FFFFFF" stroke="#C7D2FE" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="35" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="28" width="200" height="7" fill="url(#primaryGrad)"/>
<text x="100" y="24" text-anchor="middle" font-size="12" font-weight="600" class="white-text">Orange Pi 5</text>
<text x="100" y="60" text-anchor="middle" font-size="18" font-weight="700" class="main-text">~$89</text>
<text x="15" y="85" font-size="10" class="secondary-text">• 8-16GB RAM + NPU</text>
<text x="15" y="100" font-size="10" class="secondary-text">• Local AI / LLM</text>
<text x="15" y="115" font-size="10" class="secondary-text">• Edge AI ready</text>
<rect x="15" y="122" width="170" height="10" rx="3" fill="#EEF2FF"/>
<text x="100" y="130" text-anchor="middle" font-size="8" font-weight="500" class="secondary-text">Best for Local AI</text>
</g>
<!-- Use Case Icons/Labels -->
<g transform="translate(50, 365)">
<rect x="0" y="0" width="200" height="60" rx="8" fill="#FFF7ED" stroke="#FDBA74" stroke-width="1"/>
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">Sensors, displays</text>
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Simple automation</text>
</g>
<g transform="translate(300, 365)">
<rect x="0" y="0" width="200" height="60" rx="8" fill="#F0FDF4" stroke="#86EFAC" stroke-width="1"/>
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">General projects</text>
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Home assistant, kiosk</text>
</g>
<g transform="translate(550, 365)">
<rect x="0" y="0" width="200" height="60" rx="8" fill="#EEF2FF" stroke="#A5B4FC" stroke-width="1"/>
<text x="100" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Best For</text>
<text x="100" y="40" text-anchor="middle" font-size="9" class="secondary-text">Offline LLM, AI voice</text>
<text x="100" y="52" text-anchor="middle" font-size="9" class="secondary-text">Edge computing</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -0,0 +1,164 @@
<svg width="900" height="400" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="400" fill="#FAFBFC"/>
<rect width="900" height="400" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Embedded GB Architecture</text>
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Single-board computer deployment with local LLM</text>
<!-- Main Container -->
<rect x="30" y="75" width="840" height="290" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Display Section -->
<g transform="translate(60, 100)">
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#C7D2FE" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="22" width="160" height="8" fill="url(#cyanGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Display</text>
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">LCD / OLED</text>
<text x="80" y="72" text-anchor="middle" font-size="10" class="main-text">TFT / HDMI</text>
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">SPI / I2C</text>
</g>
<!-- BotServer Section -->
<g transform="translate(280, 100)">
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#DDD6FE" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#primaryGrad)"/>
<rect x="0" y="22" width="160" height="8" fill="url(#primaryGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Rust)</text>
<text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 8088</text>
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">ARM64 Binary</text>
</g>
<!-- llama.cpp Section -->
<g transform="translate(500, 100)">
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="22" width="160" height="8" fill="url(#greenGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">llama.cpp</text>
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Local)</text>
<text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 8080</text>
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">GGUF Models</text>
</g>
<!-- Arrows between top row -->
<g stroke="#8B5CF6" stroke-width="2" fill="none">
<path d="M225 155 L275 155" marker-end="url(#arrowPurple)"/>
<path d="M445 155 L495 155" marker-end="url(#arrowPurple)"/>
</g>
<!-- Input Section -->
<g transform="translate(60, 240)">
<rect x="0" y="0" width="160" height="100" rx="8" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#orangeGrad)"/>
<rect x="0" y="22" width="160" height="8" fill="url(#orangeGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Input</text>
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">Keyboard</text>
<text x="80" y="68" text-anchor="middle" font-size="10" class="main-text">Buttons</text>
<text x="80" y="84" text-anchor="middle" font-size="10" class="main-text">Touch</text>
</g>
<!-- SQLite Section -->
<g transform="translate(280, 240)">
<rect x="0" y="0" width="160" height="100" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="#64748B"/>
<rect x="0" y="22" width="160" height="8" fill="#64748B"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">SQLite</text>
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">(Data)</text>
<text x="80" y="72" text-anchor="middle" font-size="9" class="mono-text">Sessions</text>
<text x="80" y="88" text-anchor="middle" font-size="9" class="mono-text">Conversations</text>
</g>
<!-- Model Section -->
<g transform="translate(500, 240)">
<rect x="0" y="0" width="160" height="100" rx="8" fill="#FFFFFF" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="160" height="30" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="22" width="160" height="8" fill="url(#greenGrad)"/>
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">TinyLlama</text>
<text x="80" y="52" text-anchor="middle" font-size="10" class="main-text">GGUF Model</text>
<text x="80" y="72" text-anchor="middle" font-size="9" class="mono-text">Q4_K_M</text>
<text x="80" y="88" text-anchor="middle" font-size="9" class="mono-text">~700MB</text>
</g>
<!-- Vertical Arrows -->
<g stroke="#8B5CF6" stroke-width="2" fill="none">
<path d="M140 215 L140 235" marker-end="url(#arrowPurple)"/>
<path d="M360 215 L360 235" marker-end="url(#arrowPurple)"/>
<path d="M580 215 L580 235" marker-end="url(#arrowPurple)"/>
</g>
<!-- Hardware specs badge -->
<g transform="translate(700, 120)">
<rect x="0" y="0" width="150" height="200" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="75" y="25" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Requirements</text>
<text x="15" y="55" font-size="9" class="secondary-text">CPU</text>
<text x="15" y="70" font-size="10" class="mono-text">ARM64 / x86</text>
<text x="15" y="95" font-size="9" class="secondary-text">RAM</text>
<text x="15" y="110" font-size="10" class="mono-text">2GB+ min</text>
<text x="15" y="135" font-size="9" class="secondary-text">Storage</text>
<text x="15" y="150" font-size="10" class="mono-text">16GB+ SD</text>
<text x="15" y="175" font-size="9" class="secondary-text">Power</text>
<text x="15" y="190" font-size="10" class="mono-text">5V 2-3A</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,89 @@
<svg width="600" height="300" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="600" height="300" fill="#FAFBFC"/>
<rect width="600" height="300" fill="url(#dots)"/>
<!-- Main Card -->
<rect x="30" y="20" width="540" height="260" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Header -->
<rect x="30" y="20" width="540" height="45" rx="12" fill="url(#primaryGrad)"/>
<rect x="30" y="52" width="540" height="13" fill="url(#primaryGrad)"/>
<text x="300" y="50" text-anchor="middle" font-size="15" font-weight="600" class="white-text">Simple 4-Button Interface</text>
<!-- Buttons Container -->
<rect x="60" y="90" width="480" height="120" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<!-- Button 1: PREV -->
<g transform="translate(85, 110)">
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">◄ PREV</text>
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 17</text>
</g>
<!-- Button 2: UP -->
<g transform="translate(195, 110)">
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">▲ UP</text>
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 27</text>
</g>
<!-- Button 3: DOWN -->
<g transform="translate(305, 110)">
<rect x="0" y="0" width="90" height="55" rx="8" fill="url(#orangeGrad)" filter="url(#cardShadow)"/>
<text x="45" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">▼ DOWN</text>
<text x="45" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 22</text>
</g>
<!-- Button 4: SELECT -->
<g transform="translate(415, 110)">
<rect x="0" y="0" width="100" height="55" rx="8" fill="url(#primaryGrad)" filter="url(#cardShadow)"/>
<text x="50" y="28" text-anchor="middle" font-size="16" font-weight="700" class="white-text">► SELECT</text>
<text x="50" y="45" text-anchor="middle" font-size="9" class="white-text">GPIO 23</text>
</g>
<!-- Legend -->
<g transform="translate(60, 225)">
<rect x="0" y="0" width="480" height="40" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="20" y="25" font-size="10" class="secondary-text">Pull-up resistors enabled</text>
<text x="170" y="25" font-size="10" class="secondary-text"></text>
<text x="190" y="25" font-size="10" class="secondary-text">Active LOW (pressed = 0)</text>
<text x="350" y="25" font-size="10" class="secondary-text"></text>
<text x="370" y="25" font-size="10" class="secondary-text">Debounce: 50ms</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,234 @@
<svg width="1000" height="600" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<linearGradient id="pinkGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#EC4899;stop-opacity:1" />
<stop offset="100%" style="stop-color:#F472B6;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<marker id="arrowCyan" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#06B6D4"/>
</marker>
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#10B981"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="1000" height="600" fill="#FAFBFC"/>
<rect width="1000" height="600" fill="url(#dots)"/>
<!-- Title -->
<text x="500" y="35" text-anchor="middle" font-size="22" font-weight="600" class="title-text">Humanoid Robot Architecture</text>
<text x="500" y="57" text-anchor="middle" font-size="12" class="secondary-text">General Bots + Computer Vision + LLM + Movement Control</text>
<!-- Main Container -->
<rect x="25" y="75" width="950" height="500" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Left Column: Sensors -->
<g transform="translate(50, 100)">
<text x="80" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">SENSORS</text>
<!-- Camera -->
<rect x="0" y="15" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="15" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="35" width="160" height="5" fill="url(#cyanGrad)"/>
<text x="80" y="33" text-anchor="middle" font-size="10" font-weight="600" class="white-text">📷 Camera</text>
<text x="80" y="58" text-anchor="middle" font-size="9" class="main-text">RGB + Depth</text>
<text x="80" y="73" text-anchor="middle" font-size="8" class="mono-text">USB / CSI</text>
<!-- Microphone -->
<rect x="0" y="100" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="100" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="120" width="160" height="5" fill="url(#cyanGrad)"/>
<text x="80" y="118" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🎤 Microphone</text>
<text x="80" y="143" text-anchor="middle" font-size="9" class="main-text">Voice Input</text>
<text x="80" y="158" text-anchor="middle" font-size="8" class="mono-text">USB / I2S</text>
<!-- IMU -->
<rect x="0" y="185" width="160" height="70" rx="8" fill="#FFFFFF" stroke="#06B6D4" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="185" width="160" height="25" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="205" width="160" height="5" fill="url(#cyanGrad)"/>
<text x="80" y="203" text-anchor="middle" font-size="10" font-weight="600" class="white-text">⚖️ IMU</text>
<text x="80" y="228" text-anchor="middle" font-size="9" class="main-text">Balance + Orientation</text>
<text x="80" y="243" text-anchor="middle" font-size="8" class="mono-text">I2C</text>
</g>
<!-- Center Column: Processing -->
<g transform="translate(280, 100)">
<text x="200" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">PROCESSING</text>
<!-- botmodels (CV) -->
<rect x="0" y="15" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#EC4899" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="15" width="180" height="30" rx="10" fill="url(#pinkGrad)"/>
<rect x="0" y="40" width="180" height="5" fill="url(#pinkGrad)"/>
<text x="90" y="36" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botmodels</text>
<text x="90" y="65" text-anchor="middle" font-size="10" class="main-text">Computer Vision</text>
<text x="90" y="82" text-anchor="middle" font-size="9" class="secondary-text">Face • Object • Gesture</text>
<text x="90" y="97" text-anchor="middle" font-size="8" class="mono-text">ONNX / Python</text>
<!-- botserver (Brain) -->
<rect x="0" y="120" width="180" height="110" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="120" width="180" height="30" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="145" width="180" height="5" fill="url(#primaryGrad)"/>
<text x="90" y="141" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
<text x="90" y="168" text-anchor="middle" font-size="10" class="main-text">Brain / LLM</text>
<text x="90" y="185" text-anchor="middle" font-size="9" class="secondary-text">Conversation • Decisions</text>
<text x="90" y="202" text-anchor="middle" font-size="9" class="secondary-text">Knowledge • Memory</text>
<text x="90" y="220" text-anchor="middle" font-size="8" class="mono-text">Rust / Port 8088</text>
<!-- BASIC Runtime -->
<rect x="0" y="245" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="245" width="180" height="30" rx="10" fill="url(#orangeGrad)"/>
<rect x="0" y="270" width="180" height="5" fill="url(#orangeGrad)"/>
<text x="90" y="266" text-anchor="middle" font-size="11" font-weight="600" class="white-text">BASIC Runtime</text>
<text x="90" y="295" text-anchor="middle" font-size="10" class="main-text">Movement Control</text>
<text x="90" y="312" text-anchor="middle" font-size="9" class="secondary-text">SERVO • ROBOT • SEQUENCE</text>
<text x="90" y="327" text-anchor="middle" font-size="8" class="mono-text">Keywords → Commands</text>
<!-- Hardware Bridge -->
<rect x="200" y="120" width="180" height="90" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="200" y="120" width="180" height="30" rx="10" fill="#64748B"/>
<rect x="200" y="145" width="180" height="5" fill="#64748B"/>
<text x="290" y="141" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Hardware Bridge</text>
<text x="290" y="168" text-anchor="middle" font-size="10" class="main-text">Protocol Translation</text>
<text x="290" y="185" text-anchor="middle" font-size="9" class="secondary-text">GPIO • Serial • CAN</text>
<text x="290" y="200" text-anchor="middle" font-size="8" class="mono-text">PCA9685 / Dynamixel</text>
</g>
<!-- Right Column: Actuators -->
<g transform="translate(720, 100)">
<text x="110" y="0" text-anchor="middle" font-size="12" font-weight="600" class="secondary-text">ACTUATORS</text>
<!-- Servos -->
<rect x="0" y="15" width="220" height="90" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="15" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="35" width="220" height="5" fill="url(#greenGrad)"/>
<text x="110" y="33" text-anchor="middle" font-size="10" font-weight="600" class="white-text">⚙️ Servos / Motors</text>
<text x="110" y="58" text-anchor="middle" font-size="9" class="main-text">Joint Movement</text>
<text x="110" y="73" text-anchor="middle" font-size="8" class="secondary-text">Head • Arms • Legs • Grippers</text>
<text x="110" y="90" text-anchor="middle" font-size="8" class="mono-text">PWM / TTL / RS485</text>
<!-- Speaker -->
<rect x="0" y="120" width="220" height="70" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="120" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="140" width="220" height="5" fill="url(#greenGrad)"/>
<text x="110" y="138" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🔊 Speaker</text>
<text x="110" y="163" text-anchor="middle" font-size="9" class="main-text">Voice Output (TTS)</text>
<text x="110" y="178" text-anchor="middle" font-size="8" class="mono-text">USB / Audio Jack</text>
<!-- Display -->
<rect x="0" y="205" width="220" height="70" rx="8" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="205" width="220" height="25" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="225" width="220" height="5" fill="url(#greenGrad)"/>
<text x="110" y="223" text-anchor="middle" font-size="10" font-weight="600" class="white-text">🖥️ Display</text>
<text x="110" y="248" text-anchor="middle" font-size="9" class="main-text">Face / Status / Emotions</text>
<text x="110" y="263" text-anchor="middle" font-size="8" class="mono-text">HDMI / LCD / OLED</text>
</g>
<!-- Arrows: Sensors → Processing -->
<path d="M215 145 L275 85" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
<path d="M215 230 L275 175" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
<path d="M215 315 L275 290" stroke="#06B6D4" stroke-width="2" fill="none" marker-end="url(#arrowCyan)"/>
<!-- Arrows: Between Processing -->
<path d="M370 200 L370 235" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<path d="M465 265 L475 215" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Arrows: Processing → Actuators -->
<path d="M660 210 L715 145" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<path d="M660 210 L715 220" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<path d="M660 210 L715 290" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowGreen)"/>
<!-- Bottom: Flow Summary -->
<g transform="translate(50, 460)">
<rect x="0" y="0" width="900" height="95" rx="10" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<text x="450" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Data Flow</text>
<!-- Flow steps -->
<g transform="translate(30, 40)">
<rect x="0" y="0" width="140" height="40" rx="6" fill="#EFF6FF" stroke="#BFDBFE" stroke-width="1"/>
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">1. Voice / Vision</text>
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Input sensors</text>
</g>
<text x="185" y="60" font-size="16" class="secondary-text"></text>
<g transform="translate(200, 40)">
<rect x="0" y="0" width="140" height="40" rx="6" fill="#F5F3FF" stroke="#DDD6FE" stroke-width="1"/>
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">2. CV Analysis</text>
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">botmodels</text>
</g>
<text x="355" y="60" font-size="16" class="secondary-text"></text>
<g transform="translate(370, 40)">
<rect x="0" y="0" width="140" height="40" rx="6" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">3. LLM Decision</text>
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">botserver</text>
</g>
<text x="525" y="60" font-size="16" class="secondary-text"></text>
<g transform="translate(540, 40)">
<rect x="0" y="0" width="140" height="40" rx="6" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">4. BASIC Script</text>
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Movement cmds</text>
</g>
<text x="695" y="60" font-size="16" class="secondary-text"></text>
<g transform="translate(710, 40)">
<rect x="0" y="0" width="140" height="40" rx="6" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
<text x="70" y="17" text-anchor="middle" font-size="9" font-weight="500" class="main-text">5. Physical Action</text>
<text x="70" y="32" text-anchor="middle" font-size="8" class="mono-text">Servos + Voice</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,182 @@
<svg width="900" height="400" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<linearGradient id="flowGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:0.3" />
<stop offset="50%" style="stop-color:#8B5CF6;stop-opacity:0.6" />
<stop offset="100%" style="stop-color:#A855F7;stop-opacity:0.3" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="400" fill="#FAFBFC"/>
<rect width="900" height="400" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="35" text-anchor="middle" font-size="20" font-weight="600" class="title-text">Local LLM Architecture</text>
<text x="450" y="55" text-anchor="middle" font-size="12" class="secondary-text">Offline inference with llama.cpp on embedded devices</text>
<!-- Main Container -->
<rect x="30" y="75" width="840" height="295" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Main Flow: User Input → botserver → llama.cpp → Response -->
<!-- User Input -->
<g transform="translate(60, 105)">
<rect x="0" y="0" width="130" height="80" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="130" height="28" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="22" width="130" height="6" fill="url(#cyanGrad)"/>
<text x="65" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">User Input</text>
<text x="65" y="52" text-anchor="middle" font-size="10" class="main-text">Text / Voice</text>
<text x="65" y="68" text-anchor="middle" font-size="9" class="mono-text">HTTP / WebSocket</text>
</g>
<!-- Arrow 1 -->
<path d="M195 145 L240 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
<!-- BotServer -->
<g transform="translate(250, 95)">
<rect x="0" y="0" width="170" height="100" rx="8" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#primaryGrad)"/>
<rect x="0" y="22" width="170" height="6" fill="url(#primaryGrad)"/>
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Rust Runtime</text>
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Session Manager</text>
<text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 8088</text>
</g>
<!-- Arrow 2 -->
<path d="M425 145 L475 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
<!-- llama.cpp -->
<g transform="translate(485, 95)">
<rect x="0" y="0" width="170" height="100" rx="8" fill="#FFFFFF" stroke="#34D399" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="22" width="170" height="6" fill="url(#greenGrad)"/>
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">llama.cpp</text>
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Local Inference</text>
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">OpenAI API compat</text>
<text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 8080</text>
</g>
<!-- Arrow 3 -->
<path d="M660 145 L710 145" stroke="url(#flowGrad)" stroke-width="3" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Response -->
<g transform="translate(720, 105)">
<rect x="0" y="0" width="130" height="80" rx="8" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="130" height="28" rx="8" fill="url(#cyanGrad)"/>
<rect x="0" y="22" width="130" height="6" fill="url(#cyanGrad)"/>
<text x="65" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Response</text>
<text x="65" y="52" text-anchor="middle" font-size="10" class="main-text">Generated Text</text>
<text x="65" y="68" text-anchor="middle" font-size="9" class="mono-text">Streaming</text>
</g>
<!-- Lower components: SQLite and Model -->
<!-- Vertical connector from botserver -->
<path d="M335 200 L335 240" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- SQLite DB -->
<g transform="translate(250, 250)">
<rect x="0" y="0" width="170" height="90" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="170" height="28" rx="8" fill="#64748B"/>
<rect x="0" y="22" width="170" height="6" fill="#64748B"/>
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">SQLite DB</text>
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">(sessions)</text>
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Conversations</text>
<text x="85" y="82" text-anchor="middle" font-size="9" class="mono-text">Context history</text>
</g>
<!-- Vertical connector from llama.cpp -->
<path d="M570 200 L570 240" stroke="#10B981" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Model GGUF -->
<g transform="translate(485, 250)">
<rect x="0" y="0" width="170" height="90" rx="8" fill="#F0FDF4" stroke="#A7F3D0" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="170" height="28" rx="8" fill="url(#greenGrad)"/>
<rect x="0" y="22" width="170" height="6" fill="url(#greenGrad)"/>
<text x="85" y="19" text-anchor="middle" font-size="11" font-weight="600" class="white-text">Model (GGUF)</text>
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Quantized Model</text>
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Q4_K_M format</text>
<text x="85" y="82" text-anchor="middle" font-size="9" class="mono-text">700MB - 4GB</text>
</g>
<!-- Info box -->
<g transform="translate(60, 230)">
<rect x="0" y="0" width="160" height="110" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="80" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Offline Benefits</text>
<text x="15" y="45" font-size="9" class="secondary-text">✓ No internet needed</text>
<text x="15" y="62" font-size="9" class="secondary-text">✓ Data stays local</text>
<text x="15" y="79" font-size="9" class="secondary-text">✓ Zero API costs</text>
<text x="15" y="96" font-size="9" class="secondary-text">✓ ~2-30 tok/sec</text>
</g>
<!-- Model recommendations -->
<g transform="translate(690, 230)">
<rect x="0" y="0" width="160" height="110" rx="8" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
<text x="80" y="22" text-anchor="middle" font-size="10" font-weight="600" class="main-text">Recommended</text>
<text x="15" y="45" font-size="9" class="secondary-text">2GB RAM:</text>
<text x="75" y="45" font-size="9" class="mono-text">TinyLlama</text>
<text x="15" y="62" font-size="9" class="secondary-text">4GB RAM:</text>
<text x="75" y="62" font-size="9" class="mono-text">Phi-2</text>
<text x="15" y="79" font-size="9" class="secondary-text">8GB RAM:</text>
<text x="75" y="79" font-size="9" class="mono-text">Llama-3-8B</text>
<text x="15" y="96" font-size="9" class="secondary-text">16GB RAM:</text>
<text x="75" y="96" font-size="9" class="mono-text">Mistral-7B</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -0,0 +1,128 @@
<svg width="700" height="380" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="700" height="380" fill="#FAFBFC"/>
<rect width="700" height="380" fill="url(#dots)"/>
<!-- Main Card -->
<rect x="30" y="20" width="640" height="340" rx="12" fill="#FFFFFF" stroke="#FCD34D" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Header -->
<rect x="30" y="20" width="640" height="50" rx="12" fill="url(#orangeGrad)"/>
<rect x="30" y="55" width="640" height="15" fill="url(#orangeGrad)"/>
<text x="350" y="52" text-anchor="middle" font-size="18" font-weight="600" class="white-text">Orange Pi 5 — Best for Offline AI</text>
<!-- Badge -->
<rect x="550" y="30" width="100" height="25" rx="12" fill="#FFFFFF" opacity="0.9"/>
<text x="600" y="48" text-anchor="middle" font-size="11" font-weight="600" style="fill:#F59E0B;">NPU Ready</text>
<!-- Specs Grid -->
<!-- Row 1 -->
<g transform="translate(50, 90)">
<rect x="0" y="0" width="180" height="65" rx="8" fill="#FFF7ED" stroke="#FDBA74" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">CPU</text>
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">Rockchip RK3588S</text>
<text x="15" y="55" font-size="10" class="mono-text">4x A76 + 4x A55</text>
</g>
<g transform="translate(250, 90)">
<rect x="0" y="0" width="180" height="65" rx="8" fill="#EEF2FF" stroke="#C7D2FE" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">NPU</text>
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">6 TOPS</text>
<text x="15" y="55" font-size="10" class="mono-text">Neural Processing Unit</text>
</g>
<g transform="translate(450, 90)">
<rect x="0" y="0" width="200" height="65" rx="8" fill="#F0FDF4" stroke="#86EFAC" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">GPU</text>
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">Mali-G610 MP4</text>
<text x="15" y="55" font-size="10" class="mono-text">OpenGL ES 3.2</text>
</g>
<!-- Row 2 -->
<g transform="translate(50, 170)">
<rect x="0" y="0" width="180" height="65" rx="8" fill="#F5F3FF" stroke="#DDD6FE" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">RAM</text>
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">4GB / 8GB / 16GB</text>
<text x="15" y="55" font-size="10" class="mono-text">LPDDR4X</text>
</g>
<g transform="translate(250, 170)">
<rect x="0" y="0" width="180" height="65" rx="8" fill="#ECFDF5" stroke="#A7F3D0" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">Storage</text>
<text x="15" y="40" font-size="12" font-weight="500" class="main-text">M.2 NVMe + eMMC</text>
<text x="15" y="55" font-size="10" class="mono-text">+ microSD slot</text>
</g>
<g transform="translate(450, 170)">
<rect x="0" y="0" width="200" height="65" rx="8" fill="#FEF3C7" stroke="#FCD34D" stroke-width="1"/>
<text x="15" y="22" font-size="10" font-weight="600" class="secondary-text">Price</text>
<text x="15" y="40" font-size="16" font-weight="700" class="main-text">~$89</text>
<text x="15" y="55" font-size="10" class="mono-text">8GB model</text>
</g>
<!-- LLM Performance Section -->
<rect x="50" y="250" width="600" height="95" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<text x="70" y="275" font-size="12" font-weight="600" class="main-text">LLM Performance</text>
<!-- Performance Bars -->
<g transform="translate(70, 290)">
<text x="0" y="12" font-size="10" class="secondary-text">TinyLlama 1.1B Q4</text>
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
<rect x="140" y="2" width="160" height="14" rx="4" fill="url(#greenGrad)"/>
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~8-12 tok/s</text>
</g>
<g transform="translate(70, 312)">
<text x="0" y="12" font-size="10" class="secondary-text">Phi-2 2.7B Q4</text>
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
<rect x="140" y="2" width="80" height="14" rx="4" fill="url(#orangeGrad)"/>
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~4-6 tok/s</text>
</g>
<g transform="translate(70, 334)">
<text x="0" y="12" font-size="10" class="secondary-text">With NPU (rkllm)</text>
<rect x="140" y="2" width="200" height="14" rx="4" fill="#E2E8F0"/>
<rect x="140" y="2" width="200" height="14" rx="4" fill="url(#primaryGrad)"/>
<text x="350" y="12" font-size="10" font-weight="500" class="mono-text">~20-30 tok/s</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -0,0 +1,162 @@
<svg width="900" height="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366F1;stop-opacity:1" />
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
</linearGradient>
<linearGradient id="cyanGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06B6D4;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0EA5E9;stop-opacity:1" />
</linearGradient>
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10B981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#34D399;stop-opacity:1" />
</linearGradient>
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FBBF24;stop-opacity:1" />
</linearGradient>
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-color="#6366F1" flood-opacity="0.15"/>
</filter>
<marker id="arrowPurple" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#8B5CF6"/>
</marker>
<pattern id="dots" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="1" fill="#6366F1" opacity="0.08"/>
</pattern>
</defs>
<style>
.title-text { fill: #1E1B4B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.main-text { fill: #334155; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.secondary-text { fill: #64748B; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.white-text { fill: #FFFFFF; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.mono-text { fill: #475569; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; }
@media (prefers-color-scheme: dark) {
.title-text { fill: #F1F5F9; }
.main-text { fill: #E2E8F0; }
.secondary-text { fill: #94A3B8; }
.mono-text { fill: #CBD5E1; }
}
</style>
<!-- Background -->
<rect width="900" height="500" fill="#FAFBFC"/>
<rect width="900" height="500" fill="url(#dots)"/>
<!-- Title -->
<text x="450" y="40" text-anchor="middle" font-size="22" font-weight="600" class="title-text">General Bots Platform</text>
<text x="450" y="62" text-anchor="middle" font-size="12" class="secondary-text">Enterprise-Grade LLM Orchestrator &amp; AI Automation Platform</text>
<!-- Main Container -->
<rect x="30" y="85" width="840" height="385" rx="12" fill="#FFFFFF" stroke="#E2E8F0" stroke-width="2" filter="url(#cardShadow)"/>
<!-- Top Row: botapp → botui → botserver -->
<!-- botapp -->
<g transform="translate(70, 120)">
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#0EA5E9" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#cyanGrad)"/>
<rect x="0" y="25" width="200" height="7" fill="url(#cyanGrad)"/>
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botapp</text>
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Tauri)</text>
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">Desktop</text>
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">Native file access</text>
</g>
<!-- Arrow botapp → botui -->
<path d="M275 170 L330 170" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- botui -->
<g transform="translate(340, 120)">
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#10B981" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#greenGrad)"/>
<rect x="0" y="25" width="200" height="7" fill="url(#greenGrad)"/>
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botui</text>
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Pure Web)</text>
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">HTMX/HTML</text>
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">Suite &amp; Minimal UI</text>
</g>
<!-- Arrow botui → botserver -->
<path d="M545 170 L600 170" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- botserver -->
<g transform="translate(610, 120)">
<rect x="0" y="0" width="200" height="100" rx="10" fill="#FFFFFF" stroke="#8B5CF6" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="200" height="32" rx="10" fill="url(#primaryGrad)"/>
<rect x="0" y="25" width="200" height="7" fill="url(#primaryGrad)"/>
<text x="100" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botserver</text>
<text x="100" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(API)</text>
<text x="100" y="72" text-anchor="middle" font-size="10" class="secondary-text">Rust</text>
<text x="100" y="88" text-anchor="middle" font-size="9" class="mono-text">LLM Orchestration</text>
</g>
<!-- Vertical connector from botserver -->
<path d="M710 225 L710 280" stroke="#8B5CF6" stroke-width="2" fill="none"/>
<!-- Horizontal split line -->
<path d="M450 280 L710 280" stroke="#8B5CF6" stroke-width="2" fill="none"/>
<!-- Vertical lines down to bottom components -->
<path d="M450 280 L450 320" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<path d="M710 280 L710 320" stroke="#8B5CF6" stroke-width="2" fill="none" marker-end="url(#arrowPurple)"/>
<!-- Bottom Row: botlib and botbook -->
<!-- botlib -->
<g transform="translate(340, 330)">
<rect x="0" y="0" width="220" height="100" rx="10" fill="#FFFFFF" stroke="#F59E0B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="220" height="32" rx="10" fill="url(#orangeGrad)"/>
<rect x="0" y="25" width="220" height="7" fill="url(#orangeGrad)"/>
<text x="110" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botlib</text>
<text x="110" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Shared)</text>
<text x="110" y="72" text-anchor="middle" font-size="10" class="secondary-text">Rust Library</text>
<text x="110" y="88" text-anchor="middle" font-size="9" class="mono-text">Common types, HTTP client</text>
</g>
<!-- botbook -->
<g transform="translate(600, 330)">
<rect x="0" y="0" width="220" height="100" rx="10" fill="#FFFFFF" stroke="#64748B" stroke-width="2" filter="url(#cardShadow)"/>
<rect x="0" y="0" width="220" height="32" rx="10" fill="#64748B"/>
<rect x="0" y="25" width="220" height="7" fill="#64748B"/>
<text x="110" y="22" text-anchor="middle" font-size="13" font-weight="600" class="white-text">botbook</text>
<text x="110" y="55" text-anchor="middle" font-size="11" font-weight="500" class="main-text">(Docs)</text>
<text x="110" y="72" text-anchor="middle" font-size="10" class="secondary-text">mdBook</text>
<text x="110" y="88" text-anchor="middle" font-size="9" class="mono-text">Documentation</text>
</g>
<!-- Legend on left side -->
<g transform="translate(70, 280)">
<rect x="0" y="0" width="220" height="150" rx="8" fill="#F8FAFC" stroke="#E2E8F0" stroke-width="1"/>
<text x="110" y="22" text-anchor="middle" font-size="11" font-weight="600" class="main-text">Components</text>
<rect x="15" y="38" width="12" height="12" rx="2" fill="url(#cyanGrad)"/>
<text x="35" y="48" font-size="10" class="secondary-text">Desktop wrapper</text>
<rect x="15" y="58" width="12" height="12" rx="2" fill="url(#greenGrad)"/>
<text x="35" y="68" font-size="10" class="secondary-text">Web interface</text>
<rect x="15" y="78" width="12" height="12" rx="2" fill="url(#primaryGrad)"/>
<text x="35" y="88" font-size="10" class="secondary-text">Core API server</text>
<rect x="15" y="98" width="12" height="12" rx="2" fill="url(#orangeGrad)"/>
<text x="35" y="108" font-size="10" class="secondary-text">Shared library</text>
<rect x="15" y="118" width="12" height="12" rx="2" fill="#64748B"/>
<text x="35" y="128" font-size="10" class="secondary-text">Documentation</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB